From 346a18ee3db517b9ad50adfd35606fec208f8f92 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 13 Feb 2017 22:43:59 +0200 Subject: [PATCH] Added an event to the pass for inlines to help compile them. Signed-off-by: Dimitar Dobrev --- src/Generator/Driver.cs | 2 +- .../Passes/GenerateInlinesCodePass.cs | 87 -------- src/Generator/Passes/GenerateInlinesPass.cs | 187 ++++++++++++++++++ src/Generator/Utils/ProcessHelper.cs | 53 +++++ 4 files changed, 241 insertions(+), 88 deletions(-) delete mode 100644 src/Generator/Passes/GenerateInlinesCodePass.cs create mode 100644 src/Generator/Passes/GenerateInlinesPass.cs create mode 100644 src/Generator/Utils/ProcessHelper.cs diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 5690a1ea..af1e194e 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -288,7 +288,7 @@ namespace CppSharp if (Options.IsCSharpGenerator) { if (!ParserOptions.IsMicrosoftAbi) - TranslationUnitPasses.AddPass(new GenerateInlinesCodePass()); + TranslationUnitPasses.AddPass(new GenerateInlinesPass()); TranslationUnitPasses.AddPass(new TrimSpecializationsPass()); TranslationUnitPasses.AddPass(new GenerateTemplatesCodePass()); } diff --git a/src/Generator/Passes/GenerateInlinesCodePass.cs b/src/Generator/Passes/GenerateInlinesCodePass.cs deleted file mode 100644 index d96cae2f..00000000 --- a/src/Generator/Passes/GenerateInlinesCodePass.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using CppSharp.AST; - -namespace CppSharp.Passes -{ - public class GenerateInlinesCodePass : TranslationUnitPass - { - public GenerateInlinesCodePass() - { - VisitOptions.VisitClassBases = false; - VisitOptions.VisitClassFields = false; - VisitOptions.VisitEventParameters = false; - VisitOptions.VisitFunctionParameters = false; - VisitOptions.VisitFunctionReturnType = false; - VisitOptions.VisitNamespaceEnums = false; - VisitOptions.VisitNamespaceEvents = false; - VisitOptions.VisitNamespaceTemplates = false; - VisitOptions.VisitNamespaceTypedefs = false; - VisitOptions.VisitNamespaceVariables = false; - VisitOptions.VisitTemplateArguments = false; - } - - public override bool VisitASTContext(ASTContext context) - { - var result = base.VisitASTContext(context); - WriteInlines(); - return result; - } - - private void WriteInlines() - { - foreach (var module in Options.Modules.Where(m => inlinesCodeGenerators.ContainsKey(m))) - { - var inlinesCodeGenerator = inlinesCodeGenerators[module]; - var cpp = $"{module.InlinesLibraryName}.{inlinesCodeGenerator.FileExtension}"; - Directory.CreateDirectory(Options.OutputDir); - var path = Path.Combine(Options.OutputDir, cpp); - File.WriteAllText(path, inlinesCodeGenerator.Generate()); - } - } - - public override bool VisitFunctionDecl(Function function) - { - if (!base.VisitFunctionDecl(function) || !NeedsSymbol(function)) - return false; - - var module = function.TranslationUnit.Module; - var inlinesCodeGenerator = GetInlinesCodeGenerator(module); - - if (module == Options.SystemModule) - return false; - - return function.Visit(inlinesCodeGenerator); - } - - private bool NeedsSymbol(Function function) - { - var mangled = function.Mangled; - var method = function as Method; - return function.IsGenerated && !function.IsDeleted && !function.IsDependent && - !function.IsPure && (!string.IsNullOrEmpty(function.Body) || function.IsImplicit) && - // we don't need symbols for virtual functions anyway - (method == null || (!method.IsVirtual && !method.IsSynthetized && - (!method.IsConstructor || !((Class) method.Namespace).IsAbstract))) && - // we cannot handle nested anonymous types - (!(function.Namespace is Class) || !string.IsNullOrEmpty(function.Namespace.OriginalName)) && - !Context.Symbols.FindSymbol(ref mangled); - } - - InlinesCodeGenerator GetInlinesCodeGenerator(Module module) - { - if (inlinesCodeGenerators.ContainsKey(module)) - return inlinesCodeGenerators[module]; - - var inlinesCodeGenerator = new InlinesCodeGenerator(Context, module.Units); - inlinesCodeGenerators[module] = inlinesCodeGenerator; - inlinesCodeGenerator.Process(); - - return inlinesCodeGenerator; - } - - private Dictionary inlinesCodeGenerators = - new Dictionary(); - } -} diff --git a/src/Generator/Passes/GenerateInlinesPass.cs b/src/Generator/Passes/GenerateInlinesPass.cs new file mode 100644 index 00000000..1c7a1f39 --- /dev/null +++ b/src/Generator/Passes/GenerateInlinesPass.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using CppSharp.AST; +using CppSharp.Parser; +using CppSharp.Utils; + +namespace CppSharp.Passes +{ + public class GenerateInlinesPass : TranslationUnitPass + { + public GenerateInlinesPass() + { + VisitOptions.VisitClassBases = false; + VisitOptions.VisitClassFields = false; + VisitOptions.VisitEventParameters = false; + VisitOptions.VisitFunctionParameters = false; + VisitOptions.VisitFunctionReturnType = false; + VisitOptions.VisitNamespaceEnums = false; + VisitOptions.VisitNamespaceEvents = false; + VisitOptions.VisitNamespaceTemplates = false; + VisitOptions.VisitNamespaceTypedefs = false; + VisitOptions.VisitNamespaceVariables = false; + VisitOptions.VisitTemplateArguments = false; + } + + public override bool VisitASTContext(ASTContext context) + { + var result = base.VisitASTContext(context); + var findSymbolsPass = Context.TranslationUnitPasses.FindPass(); + findSymbolsPass.Wait = true; + GenerateInlines(); + return result; + } + + public event EventHandler InlinesCodeGenerated; + + private void GenerateInlines() + { + var modules = (from module in Options.Modules + where inlinesCodeGenerators.ContainsKey(module) + select module).ToList(); + remainingCompilationTasks = modules.Count; + foreach (var module in modules) + { + var inlinesCodeGenerator = inlinesCodeGenerators[module]; + var cpp = $"{module.InlinesLibraryName}.{inlinesCodeGenerator.FileExtension}"; + Directory.CreateDirectory(Options.OutputDir); + var path = Path.Combine(Options.OutputDir, cpp); + File.WriteAllText(path, inlinesCodeGenerator.Generate()); + + var e = new InlinesCodeEventArgs(module); + InlinesCodeGenerated?.Invoke(this, e); + if (string.IsNullOrEmpty(e.CustomCompiler)) + RemainingCompilationTasks--; + else + InvokeCompiler(e.CustomCompiler, e.CompilerArguments, + e.OutputDir, module.InlinesLibraryName); + } + } + + public override bool VisitFunctionDecl(Function function) + { + if (!base.VisitFunctionDecl(function)) + return false; + + var module = function.TranslationUnit.Module; + if (module == Options.SystemModule) + { + GetInlinesCodeGenerator(module); + return false; + } + + if (!NeedsSymbol(function)) + return false; + + var inlinesCodeGenerator = GetInlinesCodeGenerator(module); + return function.Visit(inlinesCodeGenerator); + } + + public class InlinesCodeEventArgs : EventArgs + { + public InlinesCodeEventArgs(Module module) + { + this.Module = module; + } + + public Module Module { get; set; } + public string CustomCompiler { get; set; } + public string CompilerArguments { get; set; } + public string OutputDir { get; set; } + } + + private bool NeedsSymbol(Function function) + { + var mangled = function.Mangled; + var method = function as Method; + return function.IsGenerated && !function.IsDeleted && !function.IsDependent && + !function.IsPure && (!string.IsNullOrEmpty(function.Body) || function.IsImplicit) && + // we don't need symbols for virtual functions anyway + (method == null || (!method.IsVirtual && !method.IsSynthetized && + (!method.IsConstructor || !((Class) method.Namespace).IsAbstract))) && + // we cannot handle nested anonymous types + (!(function.Namespace is Class) || !string.IsNullOrEmpty(function.Namespace.OriginalName)) && + !Context.Symbols.FindSymbol(ref mangled); + } + + private InlinesCodeGenerator GetInlinesCodeGenerator(Module module) + { + if (inlinesCodeGenerators.ContainsKey(module)) + return inlinesCodeGenerators[module]; + + var inlinesCodeGenerator = new InlinesCodeGenerator(Context, module.Units); + inlinesCodeGenerators[module] = inlinesCodeGenerator; + inlinesCodeGenerator.Process(); + + return inlinesCodeGenerator; + } + + private void InvokeCompiler(string compiler, string arguments, string outputDir, string inlines) + { + new Thread(() => + { + int error; + string errorMessage; + ProcessHelper.Run(compiler, arguments, out error, out errorMessage); + if (string.IsNullOrEmpty(errorMessage)) + CollectInlinedSymbols(outputDir, inlines); + else + Diagnostics.Error(errorMessage); + RemainingCompilationTasks--; + }).Start(); + } + + private void CollectInlinedSymbols(string outputDir, string inlines) + { + using (var parserOptions = new ParserOptions()) + { + parserOptions.AddLibraryDirs(outputDir); + var output = Path.GetFileName($@"{(Platform.IsWindows ? + string.Empty : "lib")}{inlines}.{ + (Platform.IsMacOS ? "dylib" : Platform.IsWindows ? "dll" : "so")}"); + parserOptions.LibraryFile = output; + using (var parserResult = Parser.ClangParser.ParseLibrary(parserOptions)) + { + if (parserResult.Kind == ParserResultKind.Success) + { + var nativeLibrary = ClangParser.ConvertLibrary(parserResult.Library); + lock (@lock) + { + Context.Symbols.Libraries.Add(nativeLibrary); + Context.Symbols.IndexSymbols(); + } + parserResult.Library.Dispose(); + } + else + Diagnostics.Error($"Parsing of {Path.Combine(outputDir, output)} failed."); + } + } + } + + private int RemainingCompilationTasks + { + get { return remainingCompilationTasks; } + set + { + if (remainingCompilationTasks != value) + { + remainingCompilationTasks = value; + if (remainingCompilationTasks == 0) + { + var findSymbolsPass = Context.TranslationUnitPasses.FindPass(); + findSymbolsPass.Wait = false; + } + } + } + } + + private int remainingCompilationTasks; + private static readonly object @lock = new object(); + + private Dictionary inlinesCodeGenerators = + new Dictionary(); + } +} diff --git a/src/Generator/Utils/ProcessHelper.cs b/src/Generator/Utils/ProcessHelper.cs new file mode 100644 index 00000000..9b359c6e --- /dev/null +++ b/src/Generator/Utils/ProcessHelper.cs @@ -0,0 +1,53 @@ +using System; +using System.Diagnostics; +using System.Text; + +namespace CppSharp.Utils +{ + public class ProcessHelper + { + public static string Run(string path, string args, out int error, out string errorMessage) + { + using (Process process = new Process()) + { + process.StartInfo.FileName = path; + process.StartInfo.Arguments = args; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + + var reterror = new StringBuilder(); + var retout = new StringBuilder(); + process.OutputDataReceived += (sender, outargs) => + { + if (!string.IsNullOrEmpty(outargs.Data)) + { + if (retout.Length > 0) + retout.AppendLine(); + retout.Append(outargs.Data); + } + }; + process.ErrorDataReceived += (sender, errargs) => + { + if (!string.IsNullOrEmpty(errargs.Data)) + { + if (reterror.Length > 0) + reterror.AppendLine(); + reterror.Append(errargs.Data); + } + }; + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + process.CancelOutputRead(); + process.CancelErrorRead(); + + error = process.ExitCode; + errorMessage = reterror.ToString(); + return retout.ToString(); + } + } + } +}