forked from UbiquityDotNET/Llvm.NET
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDriver.cs
More file actions
333 lines (277 loc) · 12 KB
/
Copy pathDriver.cs
File metadata and controls
333 lines (277 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
using ClangParser = CppSharp.ClangParser;
using Module = CppSharp.AST.Module;
namespace LlvmBindingsGenerator
{
/// <summary>Provides a more flexible implementation of the general "driver" concept in CppSharp</summary>
/// <remarks>
/// Notable differences:
/// 1. The driver does not setup ANY passes, it is entirely up to the library to do that
/// 2. There is no intermediate "Generator" needed
/// 3. Deals in interfaces rather than concrete types so it is more extensible
/// 4. The code generation types are interfaces with names that more accurately reflect what they do
/// </remarks>
internal sealed class Driver
: IDriver
, IDisposable
{
public Driver()
: this( new DriverOptions() )
{
}
public Driver( DriverOptions options )
{
Options = options;
ParserOptions = new CppSharp.Parser.ParserOptions();
Context = new BindingContext( Options, ParserOptions );
}
public DriverOptions Options { get; }
public CppSharp.Parser.ParserOptions ParserOptions { get; }
public BindingContext Context { get; private set; }
public void SetupTypeMaps() =>
Context.TypeMaps = new TypeMapDatabase( Context );
public void Setup()
{
ValidateOptions();
ParserOptions.Setup(TargetPlatform.Windows);
}
public bool ParseCode()
{
ClangParser.SourcesParsed += OnSourceFileParsed;
var files = Options.Modules.SelectMany( m => m.Headers );
ParserOptions.BuildForSourceFile( Options.Modules );
using( CppSharp.Parser.ParserResult result = ClangParser.ParseSourceFiles( files, ParserOptions ) )
{
Context.TargetInfo = result.TargetInfo;
}
Context.ASTContext = ClangParser.ConvertASTContext( ParserOptions.ASTContext );
ClangParser.SourcesParsed -= OnSourceFileParsed;
return !HasParsingErrors;
}
public void SortModulesByDependencies()
{
var sortedModules = Options.Modules.TopologicalSort( GetAndAddDependencies );
Options.Modules.Clear();
Options.Modules.AddRange( sortedModules );
}
public bool ParseLibraries()
{
ClangParser.LibraryParsed += OnFileParsed;
foreach( Module module in Options.Modules )
{
using var linkerOptions = new LinkerOptions(Context.LinkerOptions);
foreach( string libraryDir in module.LibraryDirs )
{
linkerOptions.AddLibraryDirs( libraryDir );
}
foreach( string library in module.Libraries )
{
if( !Context.Symbols.Libraries.Any( l => l.FileName == library ) )
{
linkerOptions.AddLibraries( library );
}
}
using ParserResult parserResult = ClangParser.ParseLibrary(linkerOptions);
if( parserResult.Kind == ParserResultKind.Success )
{
for( uint num = 0u; num < parserResult.LibrariesCount; num++ )
{
#pragma warning disable IDISP004 // Don't ignore created IDisposable
// This is "cloned" from CppSharp code and it is assumed to transfer ownership
// documentation is sadly lacking on most of this library
Context.Symbols.Libraries.Add( ClangParser.ConvertLibrary( parserResult.GetLibraries( num ) ) );
#pragma warning restore IDISP004 // Don't ignore created IDisposable
}
}
}
ClangParser.LibraryParsed -= OnFileParsed;
Context.Symbols.IndexSymbols();
SortModulesByDependencies();
return true;
}
public void ProcessCode()
{
Context.RunPasses();
}
public void GenerateCode( IEnumerable<ICodeGenerator> generators )
{
string defaultOutputPath = Path.GetFullPath( Options.OutputDir );
if( !Directory.Exists( defaultOutputPath ) )
{
Directory.CreateDirectory( defaultOutputPath );
}
foreach( ICodeGenerator generator in generators.Where( o => o.IsValid ) )
{
string generatorOutputPath = generator.FileAbsolutePath ?? defaultOutputPath;
if( Options.UseHeaderDirectories && generator.FileRelativeDirectory is not null)
{
generatorOutputPath = Path.Combine( generatorOutputPath, generator.FileRelativeDirectory );
Directory.CreateDirectory( generatorOutputPath );
}
try
{
string templateOutputPath = generatorOutputPath;
if( !string.IsNullOrWhiteSpace( generator.Template.SubFolder ) )
{
templateOutputPath = Path.Combine( templateOutputPath, generator.Template.SubFolder );
Directory.CreateDirectory( templateOutputPath );
}
if( generator.FileAbsolutePath is null)
{
string fileName = $"{generator.FileNameWithoutExtension}.{generator.Template.FileExtension}";
string fullFilePath = Path.Combine( templateOutputPath, fileName );
File.WriteAllText( fullFilePath, generator.Template.Generate() );
Diagnostics.Message( "Generated '{0}'", fullFilePath );
}
else
{
File.WriteAllText( generator.FileAbsolutePath, generator.Template.Generate() );
Diagnostics.Message( "Generated '{0}'", generator.FileAbsolutePath );
}
}
catch( System.IO.IOException ex )
{
Diagnostics.Error( ex.Message );
}
}
}
public void Dispose()
{
Context?.TargetInfo?.Dispose();
ParserOptions.Dispose();
CppSharp.AST.Type.TypePrinterDelegate = null;
}
public static void Run( ILibrary library )
{
using var driver = new Driver();
var options = driver.Options;
library.Setup( driver );
driver.Setup();
Diagnostics.Message( "Parsing libraries..." );
if( !driver.ParseLibraries() )
{
return;
}
Diagnostics.Message( "Parsing Source..." );
if( !driver.ParseCode() )
{
Diagnostics.Error( "Encountered one or more errors while parsing source code - no code generation performed." );
return;
}
new CleanUnitPass { Context = driver.Context }.VisitASTContext( driver.Context.ASTContext );
options.Modules.RemoveAll( m => m != options.SystemModule && !m.Units.GetGenerated().Any() );
Diagnostics.Message( "Processing code..." );
library.SetupPasses();
driver.SetupTypeMaps();
library.Preprocess( driver.Context.ASTContext );
if( CppSharp.AST.Type.TypePrinterDelegate is null)
{
throw new InvalidOperationException("Type.TypePrinterDelegate is null; Library must configure it to a non-null value to prevent null reference crashes");
}
driver.ProcessCode();
library.Postprocess( driver.Context.ASTContext );
if( !options.DryRun )
{
bool hasErrors = Diagnostics.Implementation is ErrorTrackingDiagnostics etd && etd.ErrorCount > 0;
if( hasErrors )
{
Diagnostics.Error( "Errors in previous stages, skipping code generation" );
}
else
{
Diagnostics.Message( "Generating code..." );
var generators = library.CreateGenerators( );
driver.GenerateCode( generators );
}
}
}
private IEnumerable<Module> GetAndAddDependencies( Module m )
{
var dependencies = ( from library in Context.Symbols.Libraries
where m.Libraries.Contains( library.FileName )
from module in Options.Modules
where library.Dependencies.Intersect( module.Libraries ).Any( )
select module ).ToList( );
if( m != Options.SystemModule )
{
m.Dependencies.Add( Options.SystemModule );
}
m.Dependencies.AddRange( dependencies );
return m.Dependencies;
}
private void ValidateOptions()
{
if( !Options.Compilation.Platform.HasValue )
{
Options.Compilation.Platform = Platform.Host;
}
foreach( var module in Options.Modules )
{
if( string.IsNullOrWhiteSpace( module.LibraryName ) )
{
throw new InvalidOptionException( "One of your modules has no library name." );
}
module.OutputNamespace ??= module.LibraryName;
}
if( Options.NoGenIncludeDirs != null )
{
foreach( string incDir in Options.NoGenIncludeDirs )
{
ParserOptions.AddIncludeDirs( incDir );
}
}
}
private void OnSourceFileParsed( IEnumerable<string> files, CppSharp.Parser.ParserResult result )
{
OnFileParsed( files, result );
}
private void OnFileParsed( string file, CppSharp.Parser.ParserResult result )
{
OnFileParsed( [file], result );
}
private void OnFileParsed( IEnumerable<string> files, CppSharp.Parser.ParserResult result )
{
switch( result.Kind )
{
case ParserResultKind.Success:
if( !Options.Quiet )
{
Diagnostics.Debug( "Parsed '{0}'", string.Join( ", ", files ) );
}
break;
case ParserResultKind.Error:
Diagnostics.Error( "Error parsing '{0}'", string.Join( ", ", files ) );
HasParsingErrors = true;
break;
case ParserResultKind.FileNotFound:
Diagnostics.Error( "File{0} not found: '{1}'"
, ( files.Count() > 1 ) ? "s" : string.Empty
, string.Join( ",", files )
);
HasParsingErrors = true;
break;
}
for( uint i = 0; i < result.DiagnosticsCount; ++i )
{
using ParserDiagnostic diag = result.GetDiagnostics( i );
if( diag.Level == ParserDiagnosticLevel.Warning && !Options.Verbose )
{
continue;
}
if( diag.Level == ParserDiagnosticLevel.Note )
{
continue;
}
Diagnostics.Message( "{0}({1},{2}): {3}: {4}"
, diag.FileName
, diag.LineNumber
, diag.ColumnNumber
, diag.Level.ToString().ToUpperInvariant()
, diag.Message
);
}
}
private bool HasParsingErrors;
}
}