diff options
author | Alexander Köplinger <alex.koeplinger@outlook.com> | 2021-07-15 23:40:54 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-15 23:40:54 +0300 |
commit | bd416d7bca63c0411547750a050aa57a902ba2ff (patch) | |
tree | 3a76f977834cfe59e0ff352c4a70ffd9e0b15552 /src/tasks | |
parent | 19b1b486258d5f3ff498919670b8e68f700b2819 (diff) |
Add option to MonoAOTCompiler msbuild task to generate .so files (#55753)
Diffstat (limited to 'src/tasks')
-rw-r--r-- | src/tasks/AndroidAppBuilder/ApkBuilder.cs | 36 | ||||
-rw-r--r-- | src/tasks/AndroidAppBuilder/Templates/monodroid.c | 5 | ||||
-rw-r--r-- | src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 184 | ||||
-rw-r--r-- | src/tasks/AotCompilerTask/MonoAOTCompiler.props | 2 |
4 files changed, 195 insertions, 32 deletions
diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index b979ba316b3..23475c9e1e6 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -125,11 +125,13 @@ public class ApkBuilder var assemblerFiles = new StringBuilder(); var assemblerFilesToLink = new StringBuilder(); + var aotLibraryFiles = new List<string>(); foreach (ITaskItem file in Assemblies) { // use AOT files if available var obj = file.GetMetadata("AssemblerFile"); var llvmObj = file.GetMetadata("LlvmObjectFile"); + var lib = file.GetMetadata("LibraryFile"); if (!string.IsNullOrEmpty(obj)) { @@ -143,9 +145,14 @@ public class ApkBuilder var name = Path.GetFileNameWithoutExtension(llvmObj); assemblerFilesToLink.AppendLine($" {llvmObj}"); } + + if (!string.IsNullOrEmpty(lib)) + { + aotLibraryFiles.Add(lib); + } } - if (ForceAOT && assemblerFiles.Length == 0) + if (ForceAOT && assemblerFiles.Length == 0 && aotLibraryFiles.Count == 0) { throw new InvalidOperationException("Need list of AOT files."); } @@ -165,7 +172,8 @@ public class ApkBuilder // Copy sourceDir to OutputDir/assets-tozip (ignore native files) // these files then will be zipped and copied to apk/assets/assets.zip - Utils.DirectoryCopy(AppDir, Path.Combine(OutputDir, "assets-tozip"), file => + var assetsToZipDirectory = Path.Combine(OutputDir, "assets-tozip"); + Utils.DirectoryCopy(AppDir, assetsToZipDirectory, file => { string fileName = Path.GetFileName(file); string extension = Path.GetExtension(file); @@ -184,6 +192,12 @@ public class ApkBuilder return true; }); + // add AOT .so libraries + foreach (var aotlib in aotLibraryFiles) + { + File.Copy(aotlib, Path.Combine(assetsToZipDirectory, Path.GetFileName(aotlib))); + } + // tools: string dx = Path.Combine(buildToolsFolder, "dx"); string aapt = Path.Combine(buildToolsFolder, "aapt"); @@ -195,8 +209,8 @@ public class ApkBuilder string cmake = "cmake"; string zip = "zip"; - Utils.RunProcess(zip, workingDir: Path.Combine(OutputDir, "assets-tozip"), args: "-q -r ../assets/assets.zip ."); - Directory.Delete(Path.Combine(OutputDir, "assets-tozip"), true); + Utils.RunProcess(zip, workingDir: assetsToZipDirectory, args: "-q -r ../assets/assets.zip ."); + Directory.Delete(assetsToZipDirectory, true); if (!File.Exists(androidJar)) throw new ArgumentException($"API level={BuildApiLevel} is not downloaded in Android SDK"); @@ -283,22 +297,26 @@ public class ApkBuilder .Replace("%AotSources%", aotSources) .Replace("%AotModulesSource%", string.IsNullOrEmpty(aotSources) ? "" : "modules.c"); - string defines = ""; + var defines = new StringBuilder(); if (ForceInterpreter) { - defines = "add_definitions(-DFORCE_INTERPRETER=1)"; + defines.AppendLine("add_definitions(-DFORCE_INTERPRETER=1)"); } else if (ForceAOT) { - defines = "add_definitions(-DFORCE_AOT=1)"; + defines.AppendLine("add_definitions(-DFORCE_AOT=1)"); + if (aotLibraryFiles.Count == 0) + { + defines.AppendLine("add_definitions(-DSTATIC_AOT=1)"); + } } if (!string.IsNullOrEmpty(DiagnosticPorts)) { - defines += "\nadd_definitions(-DDIAGNOSTIC_PORTS=\"" + DiagnosticPorts + "\")"; + defines.AppendLine("add_definitions(-DDIAGNOSTIC_PORTS=\"" + DiagnosticPorts + "\")"); } - cmakeLists = cmakeLists.Replace("%Defines%", defines); + cmakeLists = cmakeLists.Replace("%Defines%", defines.ToString()); File.WriteAllText(Path.Combine(OutputDir, "CMakeLists.txt"), cmakeLists); diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid.c b/src/tasks/AndroidAppBuilder/Templates/monodroid.c index 7bd0464075e..4ed02eb2166 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid.c @@ -191,7 +191,7 @@ log_callback (const char *log_domain, const char *log_level, const char *message } } -#if FORCE_AOT +#if defined(FORCE_AOT) && defined(STATIC_AOT) void register_aot_modules (void); #endif @@ -270,7 +270,10 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed LOG_INFO("Interp Enabled"); mono_jit_set_aot_mode(MONO_AOT_MODE_INTERP_ONLY); #elif FORCE_AOT + LOG_INFO("AOT Enabled"); +#if STATIC_AOT register_aot_modules(); +#endif mono_jit_set_aot_mode(MONO_AOT_MODE_FULL); #endif diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index cb3876cfc9c..259622da707 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -49,12 +49,18 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public string? OutputDir { get; set; } /// <summary> + /// Target triple passed to the AOT compiler. + /// </summary> + public string? Triple { get; set; } + + /// <summary> /// Assemblies which were AOT compiled. /// /// Successful AOT compilation will set the following metadata on the items: /// - AssemblerFile (when using OutputType=AsmOnly) /// - ObjectFile (when using OutputType=Normal) - /// - AotDataFile + /// - LibraryFile (when using OutputType=Library) + /// - AotDataFile (when using UseAotDataFile=true) /// - LlvmObjectFile (if using LLVM) /// - LlvmBitcodeFile (if using LLVM-only) /// </summary> @@ -73,13 +79,37 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public bool UseLLVM { get; set; } /// <summary> - /// Use a separate .aotdata file for the AOT data. + /// This instructs the AOT code generator to output certain data constructs into a separate file. This can reduce the executable images some five to twenty percent. + /// Developers need to then ship the resulting aotdata as a resource and register a hook to load the data on demand by using the mono_install_load_aot_data_hook() method. /// Defaults to true. /// </summary> public bool UseAotDataFile { get; set; } = true; /// <summary> - /// File to use for profile-guided optimization. + /// Create an ELF object file (.o) or .s file which can be statically linked into an executable when embedding the mono runtime. + /// Only valid if OutputType is ObjectFile or AsmOnly. + /// </summary> + public bool UseStaticLinking { get; set; } + + /// <summary> + /// When this option is specified, icalls (internal calls made from the standard library into the mono runtime code) are invoked directly instead of going through the operating system symbol lookup operation. + /// This requires UseStaticLinking=true. + /// </summary> + public bool UseDirectIcalls { get; set; } + + /// <summary> + /// When this option is specified, P/Invoke methods are invoked directly instead of going through the operating system symbol lookup operation + /// This requires UseStaticLinking=true. + /// </summary> + public bool UseDirectPInvoke { get; set; } + + /// <summary> + /// Instructs the AOT compiler to emit DWARF debugging information. + /// </summary> + public bool UseDwarfDebug { get; set; } + + /// <summary> + /// File to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled. /// </summary> public string? AotProfilePath { get; set; } @@ -89,8 +119,8 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public string[]? Profilers { get; set; } /// <summary> - /// Generate a file containing mono_aot_register_module() calls for each AOT module - /// Defaults to false. + /// Generate a file containing mono_aot_register_module() calls for each AOT module which can be compiled into the app embedding mono. + /// If set, this implies UseStaticLinking=true. /// </summary> public string? AotModulesTablePath { get; set; } @@ -101,7 +131,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public string? AotModulesTableLanguage { get; set; } = nameof(MonoAotModulesTableLanguage.C); /// <summary> - /// Choose between 'Normal', 'JustInterp', 'Full', 'FullInterp', 'LLVMOnly', 'LLVMOnlyInterp'. + /// Choose between 'Normal', 'JustInterp', 'Full', 'FullInterp', 'Hybrid', 'LLVMOnly', 'LLVMOnlyInterp'. /// LLVMOnly means to use only LLVM for FullAOT, AOT result will be a LLVM Bitcode file (the cross-compiler must be built with LLVM support) /// The "interp" options ('LLVMOnlyInterp' and 'FullInterp') mean generate necessary support to fall back to interpreter if AOT code is not possible for some methods. /// The difference between 'JustInterp' and 'FullInterp' is that 'FullInterp' will AOT all the methods in the given assemblies, while 'JustInterp' will only AOT the wrappers and trampolines necessary for the runtime to execute the managed methods using the interpreter and to interoperate with P/Invokes and unmanaged callbacks. @@ -109,10 +139,21 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public string Mode { get; set; } = nameof(MonoAotMode.Normal); /// <summary> - /// Choose between 'Normal', 'AsmOnly' - /// AsmOnly means the AOT compiler will produce .s assembly code instead of an .o object file. + /// Choose between 'ObjectFile', 'AsmOnly', 'Library' + /// ObjectFile means the AOT compiler will produce an .o object file, AsmOnly will produce .s assembly code and Library will produce a .so/.dylib/.dll shared library. + /// </summary> + public string OutputType { get; set; } = nameof(MonoAotOutputType.ObjectFile); + + /// <summary> + /// Choose between 'Dll', 'Dylib', 'So'. Only valid if OutputType is Library. + /// Dll means the AOT compiler will produce a Windows PE .dll file, Dylib means an Apple Mach-O .dylib and So means a Linux/Android ELF .so file. + /// </summary> + public string? LibraryFormat { get; set; } + + /// <summary> + /// Prefix that will be added to the library file name, e.g. to add 'lib' prefix required by some platforms. Only valid if OutputType is Library. /// </summary> - public string OutputType { get; set; } = nameof(MonoAotOutputType.Normal); + public string LibraryFilePrefix { get; set; } = ""; /// <summary> /// Path to the directory where LLVM binaries (opt and llc) are found. @@ -121,6 +162,11 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task public string? LLVMPath { get; set; } /// <summary> + /// Prepends a prefix to the name of tools ran by the AOT compiler, i.e. 'as'/'ld'. + /// </summary> + public string? ToolPrefix { get; set; } + + /// <summary> /// Path to the directory where msym artifacts are stored. /// </summary> public string? MsymPath { get; set; } @@ -143,6 +189,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task private ConcurrentBag<ITaskItem> compiledAssemblies = new ConcurrentBag<ITaskItem>(); private MonoAotMode parsedAotMode; private MonoAotOutputType parsedOutputType; + private MonoAotLibraryFormat parsedLibraryFormat; private MonoAotModulesTableLanguage parsedAotModulesTableLanguage; public override bool Execute() @@ -200,10 +247,25 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task switch (OutputType) { - case "Normal": parsedOutputType = MonoAotOutputType.Normal; break; + case "ObjectFile": parsedOutputType = MonoAotOutputType.ObjectFile; break; case "AsmOnly": parsedOutputType = MonoAotOutputType.AsmOnly; break; + case "Library": parsedOutputType = MonoAotOutputType.Library; break; + case "Normal": + Log.LogWarning($"'{nameof(OutputType)}=Normal' is deprecated, use 'ObjectFile' instead."); + parsedOutputType = MonoAotOutputType.ObjectFile; break; default: - throw new ArgumentException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.Normal)}', '{nameof(MonoAotOutputType.AsmOnly)}'. Received: '{OutputType}'.", nameof(OutputType)); + throw new ArgumentException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.ObjectFile)}', '{nameof(MonoAotOutputType.AsmOnly)}', '{nameof(MonoAotOutputType.Library)}'. Received: '{OutputType}'.", nameof(OutputType)); + } + + switch (LibraryFormat) + { + case "Dll": parsedLibraryFormat = MonoAotLibraryFormat.Dll; break; + case "Dylib": parsedLibraryFormat = MonoAotLibraryFormat.Dylib; break; + case "So": parsedLibraryFormat = MonoAotLibraryFormat.So; break; + default: + if (parsedOutputType == MonoAotOutputType.Library) + throw new ArgumentException($"'{nameof(LibraryFormat)}' must be one of: '{nameof(MonoAotLibraryFormat.Dll)}', '{nameof(MonoAotLibraryFormat.Dylib)}', '{nameof(MonoAotLibraryFormat.So)}'. Received: '{LibraryFormat}'.", nameof(LibraryFormat)); + break; } if (parsedAotMode == MonoAotMode.LLVMOnly && !UseLLVM) @@ -219,8 +281,29 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task throw new ArgumentException($"'{nameof(AotModulesTableLanguage)}' must be one of: '{nameof(MonoAotModulesTableLanguage.C)}', '{nameof(MonoAotModulesTableLanguage.ObjC)}'. Received: '{AotModulesTableLanguage}'.", nameof(AotModulesTableLanguage)); } - if (!string.IsNullOrEmpty(AotModulesTablePath) && !GenerateAotModulesTable(Assemblies, Profilers)) - return false; + if (!string.IsNullOrEmpty(AotModulesTablePath)) + { + // AOT modules for static linking, needs the aot modules table + UseStaticLinking = true; + + if (!GenerateAotModulesTable(Assemblies, Profilers)) + return false; + } + + if (UseDirectIcalls && !UseStaticLinking) + { + throw new ArgumentException($"'{nameof(UseDirectIcalls)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectIcalls)); + } + + if (UseDirectPInvoke && !UseStaticLinking) + { + throw new ArgumentException($"'{nameof(UseDirectPInvoke)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectPInvoke)); + } + + if (UseStaticLinking && (parsedOutputType == MonoAotOutputType.Library)) + { + throw new ArgumentException($"'{nameof(OutputType)}=Library' can not be used with '{nameof(UseStaticLinking)}=true'.", nameof(OutputType)); + } string? monoPaths = null; if (AdditionalAssemblySearchPaths != null) @@ -287,6 +370,26 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task processArgs.Add("--nollvm"); } + if (UseStaticLinking) + { + aotArgs.Add($"static"); + } + + if (UseDwarfDebug) + { + aotArgs.Add($"dwarfdebug"); + } + + if (!string.IsNullOrEmpty(Triple)) + { + aotArgs.Add($"mtriple={Triple}"); + } + + if (!string.IsNullOrEmpty(ToolPrefix)) + { + aotArgs.Add($"tool-prefix={ToolPrefix}"); + } + string assemblyFilename = Path.GetFileName(assembly); if (isDedup) @@ -328,12 +431,23 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task aotArgs.Add("full"); } + if (parsedAotMode == MonoAotMode.Hybrid) + { + aotArgs.Add("hybrid"); + } + if (parsedAotMode == MonoAotMode.FullInterp || parsedAotMode == MonoAotMode.JustInterp) { aotArgs.Add("interp"); } - if (parsedOutputType == MonoAotOutputType.AsmOnly) + if (parsedOutputType == MonoAotOutputType.ObjectFile) + { + string objectFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".dll.o")); + aotArgs.Add($"outfile={objectFile}"); + aotAssembly.SetMetadata("ObjectFile", objectFile); + } + else if (parsedOutputType == MonoAotOutputType.AsmOnly) { aotArgs.Add("asmonly"); @@ -341,11 +455,19 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task aotArgs.Add($"outfile={assemblerFile}"); aotAssembly.SetMetadata("AssemblerFile", assemblerFile); } - else + else if (parsedOutputType == MonoAotOutputType.Library) { - string objectFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".dll.o")); - aotArgs.Add($"outfile={objectFile}"); - aotAssembly.SetMetadata("ObjectFile", objectFile); + string extension = parsedLibraryFormat switch { + MonoAotLibraryFormat.Dll => ".dll", + MonoAotLibraryFormat.Dylib => ".dylib", + MonoAotLibraryFormat.So => ".so", + _ => throw new ArgumentOutOfRangeException() + }; + string libraryFileName = $"{LibraryFilePrefix}{assemblyFilename}{extension}"; + string libraryFilePath = Path.Combine(OutputDir, libraryFileName); + + aotArgs.Add($"outfile={libraryFilePath}"); + aotAssembly.SetMetadata("LibraryFile", libraryFilePath); } if (UseLLVM) @@ -405,7 +527,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task else { paths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}"; - processArgs.Add(assemblyFilename); + processArgs.Add('"' + assemblyFilename + '"'); } var envVariables = new Dictionary<string, string> @@ -414,11 +536,20 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task {"MONO_ENV_OPTIONS", string.Empty} // we do not want options to be provided out of band to the cross compilers }; + var responseFileContent = string.Join(" ", processArgs); + var responseFilePath = Path.GetTempFileName(); + using (var sw = new StreamWriter(responseFilePath, append: false, encoding: new UTF8Encoding(false))) + { + sw.WriteLine(responseFileContent); + } + + Log.LogMessage(MessageImportance.Low, $"AOT compiler arguments: {responseFileContent}"); + try { // run the AOT compiler (int exitCode, string output) = Utils.TryRunProcess(CompilerBinaryPath, - string.Join(" ", processArgs), + $"--response=\"{responseFilePath}\"", envVariables, assemblyDir, silent: false, @@ -436,6 +567,8 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task return false; } + File.Delete(responseFilePath); + compiledAssemblies.Add(aotAssembly); return true; } @@ -560,14 +693,23 @@ public enum MonoAotMode JustInterp, Full, FullInterp, + Hybrid, LLVMOnly, LLVMOnlyInterp } public enum MonoAotOutputType { - Normal, + ObjectFile, AsmOnly, + Library, +} + +public enum MonoAotLibraryFormat +{ + Dll, + Dylib, + So, } public enum MonoAotModulesTableLanguage diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props index c6b2f2ad28e..4f2721d7483 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props @@ -20,7 +20,7 @@ <MonoAOTCompilerDefaultAotArguments Condition="'$(TargetArchitecture)' == 'arm64'" Include="mtriple=aarch64-linux-android" /> <MonoAOTCompilerDefaultAotArguments Condition="'$(TargetArchitecture)' == 'x86'" Include="mtriple=i686-linux-android" /> <MonoAOTCompilerDefaultAotArguments Condition="'$(TargetArchitecture)' == 'x64'" Include="mtriple=x86_64-linux-android" /> - <MonoAOTCompilerDefaultAotArguments Include="static" /> + <!-- Default trampolines run out for libraries tests --> <MonoAOTCompilerDefaultAotArguments Include="nimt-trampolines=2000" /> <MonoAOTCompilerDefaultAotArguments Include="ntrampolines=10000" /> |