Tools and libraries to glue C/C++ APIs to high-level languages
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

481 lines
17 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CppSharp.AST;
using CppSharp.Generators;
using CppSharp.Parser;
using CppSharp.Passes;
using CppSharp.Utils;
using CppSharp.Types;
namespace CppSharp
{
public class Driver : IDisposable
{
public DriverOptions Options { get; }
public ParserOptions ParserOptions { get; set; }
public BindingContext Context { get; private set; }
public Generator Generator { get; private set; }
public bool HasCompilationErrors { get; set; }
public Driver(DriverOptions options)
{
Options = options;
ParserOptions = new ParserOptions();
}
void ValidateOptions()
{
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;
for (int i = 0; i < module.IncludeDirs.Count; i++)
{
var dir = new DirectoryInfo(module.IncludeDirs[i]);
module.IncludeDirs[i] = dir.FullName;
}
}
if (Options.NoGenIncludeDirs != null)
{
foreach (var incDir in Options.NoGenIncludeDirs)
ParserOptions.AddIncludeDirs(incDir);
}
NewLineType newLineType = Options.OutputNewLineType;
if (Options.OutputNewLineType == NewLineType.Auto)
{
var sourceNewLineType = GeneratorHelpers.GetNewLineTypeFromGitConfig(Options.OutputDir);
newLineType = sourceNewLineType ?? NewLineType.Host;
}
TextGenerator.SetLineEndingType(newLineType);
}
public void Setup()
{
ValidateOptions();
ParserOptions.Setup(Platform.Host);
Context = new BindingContext(Options, ParserOptions);
Generator = Options.GeneratorKind.CreateGenerator(Context);
}
public void SetupTypeMaps() =>
Context.TypeMaps = new TypeMapDatabase(Context);
public void SetupDeclMaps() =>
Context.DeclMaps = new DeclMapDatabase(Context);
void OnSourceFileParsed(IEnumerable<string> files, ParserResult result)
{
OnFileParsed(files, result);
}
void OnFileParsed(string file, ParserResult result)
{
OnFileParsed(new[] { file }, result);
}
void OnFileParsed(IEnumerable<string> files, ParserResult result)
{
switch (result.Kind)
{
case ParserResultKind.Success:
if (!Options.Quiet)
Diagnostics.Message("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.Join(",", files));
hasParsingErrors = true;
break;
}
for (uint i = 0; i < result.DiagnosticsCount; ++i)
{
var diag = result.GetDiagnostics(i);
if (diag.Level == ParserDiagnosticLevel.Warning &&
!Options.Verbose)
continue;
if (diag.Level == ParserDiagnosticLevel.Note)
continue;
if (diag.LineNumber == 0 && diag.ColumnNumber == 0)
{
Diagnostics.Message("{0}: {1}: {2}",
diag.FileName, diag.Level.ToString().ToLower(), diag.Message);
}
else
{
Diagnostics.Message("{0}({1},{2}): {3}: {4}",
diag.FileName, diag.LineNumber, diag.ColumnNumber,
diag.Level.ToString().ToLower(), diag.Message);
}
}
}
public bool ParseCode()
{
ClangParser.SourcesParsed += OnSourceFileParsed;
var sourceFiles = Options.Modules.SelectMany(m => m.Headers);
ParserOptions.BuildForSourceFile(Options.Modules);
using (ParserResult result = ClangParser.ParseSourceFiles(
sourceFiles, ParserOptions))
Context.TargetInfo = result.TargetInfo;
Context.ASTContext = ClangParser.ConvertASTContext(ParserOptions.ASTContext);
ClangParser.SourcesParsed -= OnSourceFileParsed;
return !hasParsingErrors;
}
public void SortModulesByDependencies()
{
var sortedModules = Options.Modules.TopologicalSort(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;
});
Options.Modules.Clear();
Options.Modules.AddRange(sortedModules);
}
public bool ParseLibraries()
{
ClangParser.LibraryParsed += OnFileParsed;
foreach (var module in Options.Modules)
{
using var linkerOptions = new LinkerOptions(Context.LinkerOptions);
foreach (var libraryDir in module.LibraryDirs)
linkerOptions.AddLibraryDirs(libraryDir);
foreach (var library in module.Libraries.Where(library =>
Context.Symbols.Libraries.All(l => l.FileName != library)))
{
linkerOptions.AddLibraries(library);
}
using var res = ClangParser.ParseLibrary(linkerOptions);
if (res.Kind != ParserResultKind.Success)
{
continue;
}
for (uint i = 0; i < res.LibrariesCount; i++)
Context.Symbols.Libraries.Add(ClangParser.ConvertLibrary(res.GetLibraries(i)));
}
ClangParser.LibraryParsed -= OnFileParsed;
Context.Symbols.IndexSymbols();
SortModulesByDependencies();
return !hasParsingErrors;
}
public void SetupPasses(ILibrary library)
{
var passes = Context.TranslationUnitPasses;
passes.AddPass(new ResolveIncompleteDeclsPass());
passes.AddPass(new IgnoreSystemDeclarationsPass());
passes.AddPass(new MatchParamNamesWithInstantiatedFromPass());
if (Options.IsCSharpGenerator)
passes.AddPass(new EqualiseAccessOfOverrideAndBasePass());
passes.AddPass(new FlattenAnonymousTypesToFields());
passes.AddPass(new CheckIgnoredDeclsPass());
passes.AddPass(new MarkUsedClassInternalsPass());
if (Options.IsCSharpGenerator)
{
passes.AddPass(new TrimSpecializationsPass());
passes.AddPass(new CheckAmbiguousFunctions());
passes.AddPass(new GenerateSymbolsPass());
passes.AddPass(new CheckIgnoredDeclsPass());
}
if (Options.IsCLIGenerator || Options.IsCSharpGenerator)
{
passes.AddPass(new MoveFunctionToClassPass());
passes.AddPass(new ValidateOperatorsPass());
}
library.SetupPasses(this);
passes.AddPass(new FindSymbolsPass());
passes.AddPass(new CheckMacroPass());
passes.AddPass(new CheckStaticClassPass());
if (Options.IsCLIGenerator || Options.IsCSharpGenerator || Options.IsCppGenerator)
{
passes.AddPass(new CheckAmbiguousFunctions());
}
passes.AddPass(new ConstructorToConversionOperatorPass());
passes.AddPass(new MarshalPrimitivePointersAsRefTypePass());
if (Options.IsCLIGenerator || Options.IsCSharpGenerator)
{
passes.AddPass(new CheckOperatorsOverloadsPass());
}
passes.AddPass(new CheckVirtualOverrideReturnCovariance());
passes.AddPass(new CleanCommentsPass());
Generator.SetupPasses();
passes.AddPass(new CleanInvalidDeclNamesPass());
passes.AddPass(new FastDelegateToDelegatesPass());
if (Options.GeneratorKind != GeneratorKind.Emscripten)
{
passes.AddPass(new FieldToPropertyPass());
}
passes.AddPass(new CheckIgnoredDeclsPass());
passes.AddPass(new CheckEnumsPass());
passes.AddPass(new MakeProtectedNestedTypesPublicPass());
if (Options.IsCSharpGenerator)
{
passes.AddPass(new GenerateAbstractImplementationsPass());
passes.AddPass(new MultipleInheritancePass());
}
if (Options.IsCLIGenerator || Options.IsCSharpGenerator)
{
passes.AddPass(new DelegatesPass());
}
if (Options.GeneratorKind != GeneratorKind.C)
{
passes.AddPass(new GetterSetterToPropertyPass());
}
passes.AddPass(new StripUnusedSystemTypesPass());
if (Options.IsCSharpGenerator)
{
passes.AddPass(new SpecializationMethodsWithDependentPointersPass());
passes.AddPass(new ParamTypeToInterfacePass());
}
passes.AddPass(new CheckDuplicatedNamesPass());
if (Options.IsCLIGenerator || Options.IsCSharpGenerator
|| Options.GeneratorKind.ID is GeneratorKind.Emscripten_ID)
{
passes.RenameDeclsUpperCase(RenameTargets.Any & ~RenameTargets.Parameter);
}
if (Options.IsCLIGenerator || Options.IsCSharpGenerator)
{
passes.AddPass(new CheckKeywordNamesPass());
}
passes.AddPass(new HandleVariableInitializerPass());
passes.AddPass(new MarkEventsWithUniqueIdPass());
}
public void ProcessCode()
{
Context.RunPasses();
Generator.Process();
}
public List<GeneratorOutput> GenerateCode()
{
return Generator.Generate();
}
public void SaveCode(IEnumerable<GeneratorOutput> outputs)
{
var outputPath = Path.GetFullPath(Options.OutputDir);
if (!Directory.Exists(outputPath))
Directory.CreateDirectory(outputPath);
foreach (var output in outputs.Where(o => o.TranslationUnit.IsValid))
{
var fileBase = output.TranslationUnit.FileNameWithoutExtension;
if (Options.UseHeaderDirectories)
{
var dir = Path.Combine(outputPath, output.TranslationUnit.FileRelativeDirectory);
Directory.CreateDirectory(dir);
fileBase = Path.Combine(output.TranslationUnit.FileRelativeDirectory, fileBase);
}
if (Options.GenerateName != null)
fileBase = Options.GenerateName(output.TranslationUnit);
foreach (var template in output.Outputs)
{
var fileRelativePath = $"{fileBase}.{template.FileExtension}";
var file = Path.Combine(outputPath, fileRelativePath);
WriteGeneratedCodeToFile(file, template.Generate());
output.TranslationUnit.Module?.CodeFiles.Add(file);
if (!Options.Quiet)
Diagnostics.Message("Generated '{0}'", fileRelativePath);
}
}
}
private void WriteGeneratedCodeToFile(string file, string generatedCode)
{
var fi = new FileInfo(file);
if (fi.Directory != null && !fi.Directory.Exists)
fi.Directory.Create();
if (!fi.Exists || fi.Length != generatedCode.Length ||
File.ReadAllText(file) != generatedCode)
File.WriteAllText(file, generatedCode);
}
public bool CompileCode(Module module)
{
var msBuildGenerator = new MSBuildGenerator(Context, module, LibraryMappings);
msBuildGenerator.Process();
string csproj = Path.Combine(Options.OutputDir,
$"{module.LibraryName}.{msBuildGenerator.FileExtension}");
File.WriteAllText(csproj, msBuildGenerator.Generate());
string output = ProcessHelper.Run("dotnet", $"build \"{csproj}\"",
out int error, out string errorMessage);
if (error == 0)
{
Diagnostics.Message($@"Compilation succeeded: {LibraryMappings[module] = Path.Combine(
Options.OutputDir, $"{module.LibraryName}.dll")}.");
return true;
}
Diagnostics.Error(output);
Diagnostics.Error(errorMessage);
return false;
}
public void AddTranslationUnitPass(TranslationUnitPass pass)
{
Context.TranslationUnitPasses.AddPass(pass);
}
public void AddGeneratorOutputPass(GeneratorOutputPass pass)
{
Context.GeneratorOutputPasses.AddPass(pass);
}
public void Dispose()
{
Generator?.Dispose();
Context?.TargetInfo?.Dispose();
ParserOptions.Dispose();
Context?.LinkerOptions.Dispose();
}
private bool hasParsingErrors;
private static readonly Dictionary<Module, string> LibraryMappings = new();
}
public static class ConsoleDriver
{
public static bool Run(ILibrary library)
{
var options = new DriverOptions();
using var driver = new Driver(options);
library.Setup(driver);
driver.Setup();
if (driver.Options.Verbose)
Diagnostics.Level = DiagnosticKind.Debug;
if (!options.Quiet)
Diagnostics.Message("Parsing libraries...");
if (!driver.ParseLibraries())
return false;
if (!options.Quiet)
Diagnostics.Message("Parsing code...");
if (!driver.ParseCode())
{
Diagnostics.Error("CppSharp has encountered an error while parsing code.");
return false;
}
new CleanUnitPass { Context = driver.Context }.VisitASTContext(driver.Context.ASTContext);
options.Modules.RemoveAll(m =>
{
bool result = m != options.SystemModule && !m.Units.GetGenerated().Any();
if (result) Diagnostics.Message($"Removing module {m} because no translation units are generated...");
return result;
});
if (!options.Quiet)
Diagnostics.Message("Processing code...");
driver.SetupPasses(library);
driver.SetupTypeMaps();
driver.SetupDeclMaps();
library.Preprocess(driver, driver.Context.ASTContext);
driver.ProcessCode();
library.Postprocess(driver, driver.Context.ASTContext);
if (!options.Quiet)
Diagnostics.Message("Generating code...");
if (options.DryRun)
return true;
var outputs = driver.GenerateCode();
library.GenerateCode(driver, outputs);
foreach (var output in outputs)
{
foreach (var pass in driver.Context.GeneratorOutputPasses.Passes)
pass.VisitGeneratorOutput(output);
}
driver.SaveCode(outputs);
if (driver.Options.IsCSharpGenerator && driver.Options.CompileCode)
return driver.Options.Modules.Any(m => !driver.CompileCode(m));
return true;
}
}
}