using CppSharp.AST; using CppSharp.Generators; using CppSharp.Generators.CLI; using CppSharp.Generators.CSharp; using CppSharp.Passes; using CppSharp.Types; using System; using System.Collections.Generic; using System.IO; namespace CppSharp { public class Driver { public delegate Generator CreateGeneratorDelegate(Driver driver); public static Dictionary Generators; static Driver() { Generators = new Dictionary(); Generators[LanguageGeneratorKind.CSharp] = driver => new CSharpGenerator(driver); Generators[LanguageGeneratorKind.CLI] = driver => new CLIGenerator(driver); } public DriverOptions Options { get; private set; } public IDiagnosticConsumer Diagnostics { get; private set; } public Parser Parser { get; private set; } public TypeMapDatabase TypeDatabase { get; private set; } public PassBuilder TranslationUnitPasses { get; private set; } public PassBuilder GeneratorOutputPasses { get; private set; } public Generator Generator { get; private set; } public Library Library { get; private set; } public Library LibrarySymbols { get; private set; } public Driver(DriverOptions options, IDiagnosticConsumer diagnostics) { Options = options; Diagnostics = diagnostics; Parser = new Parser(Options); Parser.OnHeaderParsed += OnFileParsed; Parser.OnLibraryParsed += OnFileParsed; TypeDatabase = new TypeMapDatabase(); TranslationUnitPasses = new PassBuilder(this); GeneratorOutputPasses = new PassBuilder(this); } static void ValidateOptions(DriverOptions options) { if (!Generators.ContainsKey(options.GeneratorKind)) throw new InvalidOptionException(); if (string.IsNullOrWhiteSpace(options.LibraryName)) throw new InvalidOptionException(); for (var i = 0; i < options.IncludeDirs.Count; i++) options.IncludeDirs[i] = Path.GetFullPath(options.IncludeDirs[i]); for (var i = 0; i < options.LibraryDirs.Count; i++) options.LibraryDirs[i] = Path.GetFullPath(options.LibraryDirs[i]); if (string.IsNullOrWhiteSpace(options.OutputNamespace)) options.OutputNamespace = options.LibraryName; } public void Setup() { ValidateOptions(Options); Generator = Generators[Options.GeneratorKind](this); TypeDatabase.SetupTypeMaps(); } void OnFileParsed(string file, ParserResult result) { switch (result.Kind) { case ParserResultKind.Success: Diagnostics.EmitMessage(DiagnosticId.ParseResult, "Parsed '{0}'", file); break; case ParserResultKind.Error: Diagnostics.EmitError(DiagnosticId.ParseResult, "Error parsing '{0}'", file); break; case ParserResultKind.FileNotFound: Diagnostics.EmitError(DiagnosticId.ParseResult, "File '{0}' was not found", file); break; } foreach (var diag in result.Diagnostics) { if (Options.IgnoreParseWarnings && diag.Level == ParserDiagnosticLevel.Warning) continue; Diagnostics.EmitMessage(DiagnosticId.ParserDiagnostic, "{0}({1},{2}): {3}: {4}", diag.FileName, diag.LineNumber, diag.ColumnNumber, diag.Level.ToString().ToLower(), diag.Message); } } public bool ParseCode() { if (!Parser.ParseHeaders(Options.Headers)) return false; Library = Parser.Library; return true; } public bool ParseLibraries() { if (!Parser.ParseLibraries(Options.Libraries)) return false; LibrarySymbols = Parser.Library; return true; } public void SetupPasses(ILibrary library) { TranslationUnitPasses.AddPass(new CleanUnitPass(Options)); TranslationUnitPasses.AddPass(new SortDeclarationsPass()); TranslationUnitPasses.AddPass(new ResolveIncompleteDeclsPass()); TranslationUnitPasses.AddPass(new CleanInvalidDeclNamesPass()); TranslationUnitPasses.AddPass(new CheckIgnoredDeclsPass()); TranslationUnitPasses.AddPass(new GenerateInlinesCodePass()); library.SetupPasses(this); TranslationUnitPasses.AddPass(new FindSymbolsPass()); TranslationUnitPasses.AddPass(new MoveOperatorToClassPass()); TranslationUnitPasses.AddPass(new CheckOperatorsOverloadsPass()); TranslationUnitPasses.AddPass(new CheckVirtualOverrideReturnCovariance()); TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); Generator.SetupPasses(); TranslationUnitPasses.AddPass(new FieldToPropertyPass()); TranslationUnitPasses.AddPass(new CleanInvalidDeclNamesPass()); TranslationUnitPasses.AddPass(new CheckIgnoredDeclsPass()); TranslationUnitPasses.AddPass(new CheckFlagEnumsPass()); TranslationUnitPasses.AddPass(new CheckDuplicatedNamesPass()); if (Options.GenerateAbstractImpls) TranslationUnitPasses.AddPass(new GenerateAbstractImplementationsPass()); } public void ProcessCode() { TranslationUnitPasses.RunPasses(pass => pass.VisitLibrary(Library)); Generator.Process(); } public List GenerateCode() { return Generator.Generate(); } public void WriteCode(List outputs) { var outputPath = Options.OutputDir; if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath); foreach (var output in outputs) { var fileBase = output.TranslationUnit.FileNameWithoutExtension; if (Options.GenerateName != null) fileBase = Options.GenerateName(output.TranslationUnit); foreach (var template in output.Templates) { var fileName = string.Format("{0}.{1}", fileBase, template.FileExtension); Diagnostics.EmitMessage(DiagnosticId.FileGenerated, "Generated '{0}'", fileName); var filePath = Path.Combine(outputPath, fileName); File.WriteAllText(Path.GetFullPath(filePath), template.Generate()); } } } public void AddTranslationUnitPass(TranslationUnitPass pass) { TranslationUnitPasses.AddPass(pass); } public void AddGeneratorOutputPass(GeneratorOutputPass pass) { GeneratorOutputPasses.AddPass(pass); } } public class DriverOptions { public DriverOptions() { Defines = new List(); IncludeDirs = new List(); SystemIncludeDirs = new List(); Headers = new List(); OutputDir = Directory.GetCurrentDirectory(); var platform = Environment.OSVersion.Platform; var isUnix = platform == PlatformID.Unix || platform == PlatformID.MacOSX; MicrosoftMode = !isUnix; Abi = isUnix ? CppAbi.Itanium : CppAbi.Microsoft; LibraryDirs = new List(); Libraries = new List(); CheckSymbols = true; GeneratorKind = LanguageGeneratorKind.CSharp; GenerateLibraryNamespace = true; GeneratePartialClasses = true; OutputInteropIncludes = true; MaxIndent = 80; CommentPrefix = "///"; } // General options public bool Verbose; public bool Quiet; public bool ShowHelpText; public bool OutputDebug; // Parser options public List Defines; public List IncludeDirs; public List SystemIncludeDirs; public List Headers; public bool NoStandardIncludes; public bool NoBuiltinIncludes; public bool MicrosoftMode; public string TargetTriple; public int ToolsetToUse; public bool IgnoreParseWarnings; public bool IgnoreParseErrors; public CppAbi Abi; public bool IsItaniumAbi { get { return Abi == CppAbi.Itanium; } } public bool IsMicrosoftAbi { get { return Abi == CppAbi.Microsoft; } } // Library options public List LibraryDirs; public List Libraries; public bool CheckSymbols; public string SharedLibraryName; // Generator options public LanguageGeneratorKind GeneratorKind; public string OutputNamespace; public string OutputDir; public string LibraryName; public bool OutputInteropIncludes; public bool GenerateLibraryNamespace; public bool GenerateFunctionTemplates; public bool GeneratePartialClasses; public bool GenerateVirtualTables; public bool GenerateAbstractImpls; public bool GenerateInternalImports; public string IncludePrefix; public bool WriteOnlyWhenChanged; public Func GenerateName; public int MaxIndent; public string CommentPrefix; private string inlinesLibraryName; public string InlinesLibraryName { get { if (string.IsNullOrEmpty(inlinesLibraryName)) { return string.Format("{0}-inlines", OutputNamespace); } return inlinesLibraryName; } set { inlinesLibraryName = value; } } public bool IsCSharpGenerator { get { return GeneratorKind == LanguageGeneratorKind.CSharp; } } public bool IsCLIGenerator { get { return GeneratorKind == LanguageGeneratorKind.CLI; } } public bool Is32Bit { get { return true; } } } public class InvalidOptionException : Exception { } public static class ConsoleDriver { public static void Run(ILibrary library) { var options = new DriverOptions(); var driver = new Driver(options, new TextDiagnosticPrinter()); library.Setup(driver); driver.Setup(); if (!options.Quiet) Console.WriteLine("Parsing libraries..."); if (!driver.ParseLibraries()) return; if (!options.Quiet) Console.WriteLine("Indexing library symbols..."); driver.LibrarySymbols.IndexSymbols(); if (!options.Quiet) Console.WriteLine("Parsing code..."); if (!driver.ParseCode()) return; if (!options.Quiet) Console.WriteLine("Processing code..."); library.Preprocess(driver, driver.Library); driver.SetupPasses(library); driver.ProcessCode(); library.Postprocess(driver.Library); if (!options.Quiet) Console.WriteLine("Generating code..."); var outputs = driver.GenerateCode(); foreach (var output in outputs) { foreach (var pass in driver.GeneratorOutputPasses.Passes) { pass.Driver = driver; pass.VisitGeneratorOutput(output); } } driver.WriteCode(outputs); } } }