11using System ;
2- using System . Reflection ;
3- using System . CodeDom . Compiler ;
2+ using System . Collections . Generic ;
43using System . IO ;
54using System . Linq ;
6- using System . Collections . Generic ;
5+ using System . Reflection ;
6+ using System . Text ;
7+ using Microsoft . CodeAnalysis ;
8+ using Microsoft . CodeAnalysis . CSharp ;
79using NUnit . Framework ;
810
911namespace generatortests
1012{
11- public static class Compiler
13+ class Compiler
1214 {
13- const string RoslynEnvironmentVariable = "ROSLYN_COMPILER_LOCATION" ;
14- private static string unitTestFrameworkAssemblyPath = typeof ( Assert ) . Assembly . Location ;
15- private static string supportFilePath = typeof ( Compiler ) . Assembly . Location ;
16-
17- static CodeDomProvider GetCodeDomProvider ( )
18- {
19- if ( Environment . OSVersion . Platform == PlatformID . Win32NT ) {
20- //NOTE: there is an issue where Roslyn's csc.exe isn't copied to output for non-ASP.NET projects
21- // Comments on this here: https://stackoverflow.com/a/40311406/132442
22- // They added an environment variable as a workaround: https://github.com/aspnet/RoslynCodeDomProvider/pull/12
23- if ( string . IsNullOrEmpty ( Environment . GetEnvironmentVariable ( RoslynEnvironmentVariable , EnvironmentVariableTarget . Process ) ) ) {
24- string roslynPath = Path . GetFullPath ( Path . Combine ( unitTestFrameworkAssemblyPath , ".." , ".." , ".." , "packages" , "microsoft.codedom.providers.dotnetcompilerplatform" , "2.0.1" , "tools" , "RoslynLatest" ) ) ;
25- Environment . SetEnvironmentVariable ( RoslynEnvironmentVariable , roslynPath , EnvironmentVariableTarget . Process ) ;
26- }
27-
28- return new Microsoft . CodeDom . Providers . DotNetCompilerPlatform . CSharpCodeProvider ( ) ;
29- } else {
30- return new Microsoft . CSharp . CSharpCodeProvider ( ) ;
31- }
32- }
15+ private static string supportFilePath = typeof ( Compiler ) . Assembly . Location ;
16+ private static string unitTestFrameworkAssemblyPath = typeof ( Assert ) . Assembly . Location ;
3317
3418 public static Assembly Compile ( Xamarin . Android . Binder . CodeGeneratorOptions options ,
3519 string assemblyFileName , IEnumerable < string > AdditionalSourceDirectories ,
3620 out bool hasErrors , out string output , bool allowWarnings )
3721 {
22+ // Gather all the files we need to compile
3823 var generatedCodePath = options . ManagedCallableWrapperSourceOutputDirectory ;
3924 var sourceFiles = Directory . EnumerateFiles ( generatedCodePath , "*.cs" ,
4025 SearchOption . AllDirectories ) . ToList ( ) ;
41- sourceFiles = sourceFiles . Select ( x => Path . GetFullPath ( x ) ) . ToList ( ) ;
26+ sourceFiles = sourceFiles . Select ( x => Path . GetFullPath ( x ) ) . ToList ( ) ;
4227
4328 var supportFiles = Directory . EnumerateFiles ( Path . Combine ( Path . GetDirectoryName ( supportFilePath ) , "SupportFiles" ) ,
4429 "*.cs" , SearchOption . AllDirectories ) ;
@@ -49,36 +34,51 @@ public static Assembly Compile (Xamarin.Android.Binder.CodeGeneratorOptions opti
4934 sourceFiles . AddRange ( additonal ) ;
5035 }
5136
52- CompilerParameters parameters = new CompilerParameters ( ) ;
53- parameters . GenerateExecutable = false ;
54- parameters . GenerateInMemory = true ;
55- parameters . CompilerOptions = "/unsafe" ;
56- parameters . OutputAssembly = assemblyFileName ;
57- parameters . ReferencedAssemblies . Add ( unitTestFrameworkAssemblyPath ) ;
58- parameters . ReferencedAssemblies . Add ( typeof ( Enumerable ) . Assembly . Location ) ;
59-
60- var binDir = Path . GetDirectoryName ( typeof ( BaseGeneratorTest ) . Assembly . Location ) ;
61- var facDir = GetFacadesPath ( ) ;
62- parameters . ReferencedAssemblies . Add ( Path . Combine ( binDir , "Java.Interop.dll" ) ) ;
63- parameters . ReferencedAssemblies . Add ( Path . Combine ( facDir , "netstandard.dll" ) ) ;
64- #if DEBUG
65- parameters . IncludeDebugInformation = true ;
66- #else
67- parameters . IncludeDebugInformation = false ;
68- #endif
69-
70- using ( var codeProvider = GetCodeDomProvider ( ) ) {
71- CompilerResults results = codeProvider . CompileAssemblyFromFile ( parameters , sourceFiles . ToArray ( ) ) ;
72-
73- hasErrors = false ;
74-
75- foreach ( CompilerError message in results . Errors ) {
76- hasErrors |= ! message . IsWarning || ! allowWarnings ;
37+ // Parse the source files
38+ var syntax_trees = sourceFiles . Distinct ( ) . Select ( s => CSharpSyntaxTree . ParseText ( File . ReadAllText ( s ) ) ) . ToArray ( ) ;
39+
40+ // Set up the assemblies we need to reference
41+ var binDir = Path . GetDirectoryName ( typeof ( BaseGeneratorTest ) . Assembly . Location ) ;
42+ var facDir = GetFacadesPath ( ) ;
43+
44+ var references = new [ ] {
45+ MetadataReference . CreateFromFile ( unitTestFrameworkAssemblyPath ) ,
46+ MetadataReference . CreateFromFile ( typeof ( object ) . Assembly . Location ) ,
47+ MetadataReference . CreateFromFile ( typeof ( Enumerable ) . Assembly . Location ) ,
48+ MetadataReference . CreateFromFile ( Path . Combine ( binDir , "Java.Interop.dll" ) ) ,
49+ MetadataReference . CreateFromFile ( Path . Combine ( facDir , "netstandard.dll" ) )
50+ } ;
51+
52+ // Compile!
53+ var compilation = CSharpCompilation . Create (
54+ Path . GetFileName ( assemblyFileName ) ,
55+ syntaxTrees : syntax_trees ,
56+ references : references ,
57+ options : new CSharpCompilationOptions ( OutputKind . DynamicallyLinkedLibrary , allowUnsafe : true ) ) ;
58+
59+ // Save assembly to a memory stream and load it with reflection
60+ using ( var ms = new MemoryStream ( ) ) {
61+ var result = compilation . Emit ( ms ) ;
62+ var success = result . Success && ( allowWarnings || ! result . Diagnostics . Any ( d => d . Severity == DiagnosticSeverity . Warning ) ) ;
63+
64+ if ( ! success ) {
65+ var failures = result . Diagnostics . Where ( diagnostic =>
66+ diagnostic . Severity == DiagnosticSeverity . Warning ||
67+ diagnostic . Severity == DiagnosticSeverity . Error ) ;
68+
69+ hasErrors = true ;
70+ output = OutputDiagnostics ( failures ) ;
71+ } else {
72+ ms . Seek ( 0 , SeekOrigin . Begin ) ;
73+
74+ hasErrors = false ;
75+ output = null ;
76+
77+ return Assembly . Load ( ms . ToArray ( ) ) ;
7778 }
78- output = string . Join ( Environment . NewLine , results . Output . Cast < string > ( ) ) ;
79-
80- return results . CompiledAssembly ;
8179 }
80+
81+ return null ;
8282 }
8383
8484 static string GetFacadesPath ( )
@@ -94,6 +94,15 @@ static string GetFacadesPath ()
9494
9595 return dir ;
9696 }
97+
98+ static string OutputDiagnostics ( IEnumerable < Diagnostic > diagnostics )
99+ {
100+ var sb = new StringBuilder ( ) ;
101+
102+ foreach ( var d in diagnostics )
103+ sb . AppendLine ( $ "{ d . Id } : { d . GetMessage ( ) } ({ d . Location } )") ;
104+
105+ return sb . ToString ( ) ;
106+ }
97107 }
98108}
99-
0 commit comments