88using CS = Microsoft . CodeAnalysis . CSharp ;
99using VB = Microsoft . CodeAnalysis . VisualBasic ;
1010
11+ #nullable enable
12+
1113namespace Docfx . Dotnet ;
1214
1315internal static class CompilationHelper
@@ -52,43 +54,55 @@ public static bool CheckDiagnostics(this Compilation compilation, bool errorAsWa
5254 return errorCount > 0 ;
5355 }
5456
55- public static Compilation CreateCompilationFromCSharpFiles ( IEnumerable < string > files )
57+ public static Compilation CreateCompilationFromCSharpFiles ( IEnumerable < string > files , IDictionary < string , string > msbuildProperties )
5658 {
59+ var parserOption = GetCSharpParseOptions ( msbuildProperties ) ;
60+ var syntaxTrees = files . Select ( path => CS . CSharpSyntaxTree . ParseText ( File . ReadAllText ( path ) , parserOption , path : path ) ) ;
61+
5762 return CS . CSharpCompilation . Create (
5863 assemblyName : null ,
5964 options : new CS . CSharpCompilationOptions ( OutputKind . DynamicallyLinkedLibrary , xmlReferenceResolver : XmlFileResolver . Default ) ,
60- syntaxTrees : files . Select ( path => CS . SyntaxFactory . ParseSyntaxTree ( File . ReadAllText ( path ) , path : path ) ) ,
65+ syntaxTrees : syntaxTrees ,
6166 references : GetDefaultMetadataReferences ( "C#" ) ) ;
6267 }
6368
64- public static Compilation CreateCompilationFromCSharpCode ( string code , string name = null , params MetadataReference [ ] references )
69+ public static Compilation CreateCompilationFromCSharpCode ( string code , IDictionary < string , string > msbuildProperties , string ? name = null , params MetadataReference [ ] references )
6570 {
71+ var parserOption = GetCSharpParseOptions ( msbuildProperties ) ;
72+ var syntaxTree = CS . CSharpSyntaxTree . ParseText ( code , parserOption ) ;
73+
6674 return CS . CSharpCompilation . Create (
6775 name ,
6876 options : new CS . CSharpCompilationOptions ( OutputKind . DynamicallyLinkedLibrary , xmlReferenceResolver : XmlFileResolver . Default ) ,
69- syntaxTrees : new [ ] { CS . SyntaxFactory . ParseSyntaxTree ( code ) } ,
77+ syntaxTrees : [ syntaxTree ] ,
7078 references : GetDefaultMetadataReferences ( "C#" ) . Concat ( references ) ) ;
7179 }
7280
73- public static Compilation CreateCompilationFromVBFiles ( IEnumerable < string > files )
81+ public static Compilation CreateCompilationFromVBFiles ( IEnumerable < string > files , IDictionary < string , string > msbuildProperties )
7482 {
83+ var parserOption = GetVisualBasicParseOptions ( msbuildProperties ) ;
84+ var syntaxTrees = files . Select ( path => VB . VisualBasicSyntaxTree . ParseText ( File . ReadAllText ( path ) , parserOption , path : path ) ) ;
85+
7586 return VB . VisualBasicCompilation . Create (
7687 assemblyName : null ,
7788 options : new VB . VisualBasicCompilationOptions ( OutputKind . DynamicallyLinkedLibrary , globalImports : GetVBGlobalImports ( ) , xmlReferenceResolver : XmlFileResolver . Default ) ,
78- syntaxTrees : files . Select ( path => VB . SyntaxFactory . ParseSyntaxTree ( File . ReadAllText ( path ) , path : path ) ) ,
89+ syntaxTrees : syntaxTrees ,
7990 references : GetDefaultMetadataReferences ( "VB" ) ) ;
8091 }
8192
82- public static Compilation CreateCompilationFromVBCode ( string code , string name = null , params MetadataReference [ ] references )
93+ public static Compilation CreateCompilationFromVBCode ( string code , IDictionary < string , string > msbuildProperties , string ? name = null , params MetadataReference [ ] references )
8394 {
95+ var parserOption = GetVisualBasicParseOptions ( msbuildProperties ) ;
96+ var syntaxTree = VB . VisualBasicSyntaxTree . ParseText ( code , parserOption ) ;
97+
8498 return VB . VisualBasicCompilation . Create (
8599 name ,
86100 options : new VB . VisualBasicCompilationOptions ( OutputKind . DynamicallyLinkedLibrary , globalImports : GetVBGlobalImports ( ) , xmlReferenceResolver : XmlFileResolver . Default ) ,
87- syntaxTrees : new [ ] { VB . SyntaxFactory . ParseSyntaxTree ( code ) } ,
101+ syntaxTrees : [ syntaxTree ] ,
88102 references : GetDefaultMetadataReferences ( "VB" ) . Concat ( references ) ) ;
89103 }
90104
91- public static ( Compilation , IAssemblySymbol ) CreateCompilationFromAssembly ( string assemblyPath , IEnumerable < string > references = null )
105+ public static ( Compilation , IAssemblySymbol ) CreateCompilationFromAssembly ( string assemblyPath , IEnumerable < string > ? references = null )
92106 {
93107 var metadataReference = CreateMetadataReference ( assemblyPath ) ;
94108 var compilation = CS . CSharpCompilation . Create (
@@ -100,7 +114,7 @@ public static (Compilation, IAssemblySymbol) CreateCompilationFromAssembly(strin
100114 . Select ( CreateMetadataReference )
101115 . Append ( metadataReference ) ) ;
102116
103- var assembly = ( IAssemblySymbol ) compilation . GetAssemblyOrModuleSymbol ( metadataReference ) ;
117+ var assembly = ( IAssemblySymbol ) compilation . GetAssemblyOrModuleSymbol ( metadataReference ) ! ;
104118 return ( compilation , assembly ) ;
105119 }
106120
@@ -124,8 +138,8 @@ private static IEnumerable<MetadataReference> GetDefaultMetadataReferences(strin
124138 {
125139 var dotnetExeDirectory = DotNetCorePathFinder . FindDotNetExeDirectory ( ) ;
126140 var refDirectory = Path . Combine ( dotnetExeDirectory , "packs/Microsoft.NETCore.App.Ref" ) ;
127- var version = new DirectoryInfo ( refDirectory ) . GetDirectories ( ) . Select ( d => d . Name ) . Max ( ) ;
128- var moniker = new DirectoryInfo ( Path . Combine ( refDirectory , version , "ref" ) ) . GetDirectories ( ) . Select ( d => d . Name ) . Max ( ) ;
141+ var version = new DirectoryInfo ( refDirectory ) . GetDirectories ( ) . Select ( d => d . Name ) . Max ( ) ! ;
142+ var moniker = new DirectoryInfo ( Path . Combine ( refDirectory , version , "ref" ) ) . GetDirectories ( ) . Select ( d => d . Name ) . Max ( ) ! ;
129143 var path = Path . Combine ( refDirectory , version , "ref" , moniker ) ;
130144
131145 Logger . LogInfo ( $ "Compiling { language } files using .NET SDK { version } for { moniker } ") ;
@@ -177,4 +191,34 @@ private static MetadataReference CreateMetadataReference(string assemblyPath)
177191 var documentation = XmlDocumentationProvider . CreateFromFile ( Path . ChangeExtension ( assemblyPath , ".xml" ) ) ;
178192 return MetadataReference . CreateFromFile ( assemblyPath , documentation : documentation ) ;
179193 }
194+
195+ private static CS . CSharpParseOptions GetCSharpParseOptions ( IDictionary < string , string > msbuildProperties )
196+ {
197+ var preprocessorSymbols = ( msbuildProperties . TryGetValue ( "DefineConstants" , out var defineConstants ) )
198+ ? defineConstants . Split ( ';' , StringSplitOptions . RemoveEmptyEntries | StringSplitOptions . TrimEntries )
199+ : null ;
200+
201+ return new CS . CSharpParseOptions ( preprocessorSymbols : preprocessorSymbols ) ;
202+ }
203+
204+ private static VB . VisualBasicParseOptions GetVisualBasicParseOptions ( IDictionary < string , string > msbuildProperties )
205+ {
206+ IEnumerable < KeyValuePair < string , object > > ? preprocessorSymbols = null ;
207+ if ( ( msbuildProperties . TryGetValue ( "DefineConstants" , out var defineConstants ) ) )
208+ {
209+ // Visual Basic use symbol/value pairs that are separated by semicolons. And are `key = value` pair syntax:
210+ // https://learn.microsoft.com/en-us/visualstudio/msbuild/vbc-task?view=vs-2022
211+ var items = defineConstants . Split ( ';' ) ;
212+ preprocessorSymbols = items . Select ( x => x . Split ( '=' , 2 , StringSplitOptions . RemoveEmptyEntries | StringSplitOptions . TrimEntries ) )
213+ . Where ( x => x . Length == 2 ) // Silently ignore invalid formatted item.
214+ . Select ( x => new KeyValuePair < string , object > ( x [ 0 ] . Trim ( ) , x [ 1 ] . Trim ( ) ) )
215+ . ToArray ( ) ;
216+ }
217+ else
218+ {
219+ preprocessorSymbols = null ;
220+ }
221+
222+ return new VB . VisualBasicParseOptions ( preprocessorSymbols : preprocessorSymbols ) ;
223+ }
180224}
0 commit comments