diff --git a/src/mono/msbuild/android/build/AndroidApp.targets b/src/mono/msbuild/android/build/AndroidApp.targets index 7fb35a9c4d3bfe..e1a5363d22fbe4 100644 --- a/src/mono/msbuild/android/build/AndroidApp.targets +++ b/src/mono/msbuild/android/build/AndroidApp.targets @@ -85,7 +85,8 @@ - + + diff --git a/src/mono/msbuild/common/LibraryBuilder.targets b/src/mono/msbuild/common/LibraryBuilder.targets index eacc9e2cae7544..2d54b6a07dd161 100644 --- a/src/mono/msbuild/common/LibraryBuilder.targets +++ b/src/mono/msbuild/common/LibraryBuilder.targets @@ -8,6 +8,8 @@ <_IsSharedLibrary>false <_IsSharedLibrary Condition="'$(NativeLib)' == 'shared'">true + <_UsesCustomRuntimeInitCallback>false + <_UsesCustomRuntimeInitCallback Condition="$(CustomRuntimeInitCallback) != ''">true @@ -25,7 +27,8 @@ OutputDirectory="$(BundleDir)" RuntimeIdentifier="$(RuntimeIdentifier)" RuntimeLibraries="@(_RuntimeLibraries)" - TargetOS="$(TargetOS)"> + TargetOS="$(TargetOS)" + UsesCustomRuntimeInitCallback="$(_UsesCustomRuntimeInitCallback)"> diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.cs b/src/tasks/LibraryBuilder/LibraryBuilder.cs index 7b5fed50e618bc..bec8d46b3b1d17 100644 --- a/src/tasks/LibraryBuilder/LibraryBuilder.cs +++ b/src/tasks/LibraryBuilder/LibraryBuilder.cs @@ -66,6 +66,26 @@ public bool IsSharedLibrary } } + /// + /// Determines whether or not the mono runtime auto initialization + /// tremplate, autoinit.c, is used. + /// + public bool UsesCustomRuntimeInitCallback { get; set; } + + public string? AssetsPath { get; set; } + + /// + /// + public ITaskItem[] AppContextKeys { get; set; } = Array.Empty(); + + /// + /// + public ITaskItem[] AppContextValues { get; set; } = Array.Empty(); + + /// + /// + public string? RuntimeConfigBinFile { get; set; } + public bool StripDebugSymbols { get; set; } /// @@ -113,16 +133,23 @@ public override bool Execute() GatherAotSourcesObjects(aotSources, aotObjects, extraSources, linkerArgs); GatherLinkerArgs(linkerArgs); + if (!UsesCustomRuntimeInitCallback) + { + WriteAutoInitializationFromTemplate(); + } + WriteCMakeFileFromTemplate(aotSources.ToString(), aotObjects.ToString(), extraSources.ToString(), linkerArgs.ToString()); OutputPath = BuildLibrary(); return true; } + + private List exportedAssemblies = new List(); + private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aotObjects, StringBuilder extraSources, StringBuilder linkerArgs) { List exportedSymbols = new List(); - List exportedAssemblies = new List(); bool hasExports = false; foreach (CompiledAssembly compiledAssembly in CompiledAssemblies) @@ -226,6 +253,79 @@ private static void WriteLinkerScriptFile(string exportsFile, List expor .Replace("%GLOBAL_SYMBOLS%", globalExports)); } + private void WriteAutoInitializationFromTemplate() + { + string appContextEnvVariables = GenerateAppContextEnvVariables(); + string assembliesLoader = GenerateAssembliesLoader(); + string assetsPath = GenerateAssetsPath(); + string runtimeConfig = GenerateRuntimeConfig(); + File.WriteAllText(Path.Combine(OutputDirectory, "autoinit.c"), + Utils.GetEmbeddedResource("autoinit.c") + .Replace("%APPCTX_ENV_VARIABLES%", appContextEnvVariables) + .Replace("%ASSEMBLIES_LOADER%", assembliesLoader) + .Replace("%ASSETS_PATH%", assetsPath) + .Replace("%RUNTIME_CONFIG%", runtimeConfig)); + } + + private string GenerateAssembliesLoader() + { + var assembliesLoader = new StringBuilder(); + foreach (string exportedAssembly in exportedAssemblies) + { + assembliesLoader.AppendLine($" mono_assembly_open(\"{exportedAssembly}\", NULL);"); + } + return assembliesLoader.ToString(); + } + + private string GenerateAssetsPath() + { + return AssetsPath ?? "DOTNET_ASSETS_PATH"; + } + + private string GenerateRuntimeConfig() + { + if (string.IsNullOrEmpty(RuntimeConfigBinFile)) + return " return;"; + + var runtimeConfig = new StringBuilder(); + runtimeConfig.Append($" char *file_name = {RuntimeConfigBinFile};"); + runtimeConfig.Append(" int str_len = strlen (bundle_path) + strlen (file_name) + 1; // +1 is for the \"/\""); + runtimeConfig.Append(" char *file_path = (char *)malloc (sizeof (char) * (str_len +1)); // +1 is for the terminating null character"); + runtimeConfig.Append(" int num_char = snprintf (file_path, (str_len + 1), \"%s/%s\", bundle_path, file_name);"); + runtimeConfig.Append(" struct stat buffer;\n"); + runtimeConfig.Append(" assert (num_char > 0 && num_char == str_len);\n"); + runtimeConfig.Append(" if (stat (file_path, &buffer) == 0) {"); + runtimeConfig.Append(" MonovmRuntimeConfigArguments *arg = (MonovmRuntimeConfigArguments *)malloc (sizeof (MonovmRuntimeConfigArguments));"); + runtimeConfig.Append(" arg->kind = 0;"); + runtimeConfig.Append(" arg->runtimeconfig.name.path = file_path;"); + runtimeConfig.Append(" monovm_runtimeconfig_initialize (arg, cleanup_runtime_config, file_path);"); + runtimeConfig.Append(" } else {"); + runtimeConfig.Append(" free (file_path);"); + runtimeConfig.Append(" }"); + return runtimeConfig.ToString(); + } + + private string GenerateAppContextEnvVariables() + { + if (AppContextKeys.Length != AppContextValues.Length) + { + throw new LogAsErrorException($"'{nameof(AppContextKeys)}' length does not match '{nameof(AppContextValues)}' length. {AppContextKeys.Length} != {AppContextValues.Length}"); + } + + int numArgs = AppContextKeys.Length; + var appContextEnvVariables = new StringBuilder(); + + appContextEnvVariables.AppendLine($" const char **appctx_keys, **appctx_values = (char**)malloc({numArgs} * sizeof(char*));"); + for (int i = 0; i < numArgs; i++) + { + appContextEnvVariables.AppendLine($" appctx_keys[{i}] = \"{AppContextKeys[i]}\";"); + appContextEnvVariables.AppendLine($" appctx_values[{i}] = \"{AppContextValues[i]}\";"); + } + appContextEnvVariables.AppendLine($" monovm_initialize({numArgs}, appctx_keys, appctx_values);"); + + return appContextEnvVariables.ToString(); + } + private void WriteCMakeFileFromTemplate(string aotSources, string aotObjects, string extraSources, string linkerArgs) { // BundleDir @@ -239,9 +339,6 @@ private void WriteCMakeFileFromTemplate(string aotSources, string aotObjects, st .Replace("%AotObjects%", aotObjects) .Replace("%ExtraSources%", extraSources) .Replace("%LIBRARY_LINKER_ARGS%", linkerArgs)); - - File.WriteAllText(Path.Combine(OutputDirectory, "autoinit.c"), - Utils.GetEmbeddedResource("autoinit.c")); } private void WriteAssembliesToLoadList(List assemblies) diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.csproj b/src/tasks/LibraryBuilder/LibraryBuilder.csproj index c2a8ae77a3c51e..bdd1543fd13a53 100644 --- a/src/tasks/LibraryBuilder/LibraryBuilder.csproj +++ b/src/tasks/LibraryBuilder/LibraryBuilder.csproj @@ -19,6 +19,7 @@ + diff --git a/src/tasks/LibraryBuilder/Templates/autoinit.c b/src/tasks/LibraryBuilder/Templates/autoinit.c index 6746a15a3811da..f18140758cef11 100644 --- a/src/tasks/LibraryBuilder/Templates/autoinit.c +++ b/src/tasks/LibraryBuilder/Templates/autoinit.c @@ -13,10 +13,7 @@ #include #include -// TODO grab the directory where the assemblies are placed on device and remove path hardcode -static char *bundle_path = "/data/user/0/net.dot.Android.Device_Emulator.Aot_Llvm.Test/files"; - -#define RUNTIMECONFIG_BIN_FILE "runtimeconfig.bin" +static char *bundle_path; void register_aot_modules (void); bool monoeg_g_module_address (void *addr, char *file_name, size_t file_name_len, @@ -24,32 +21,25 @@ bool monoeg_g_module_address (void *addr, char *file_name, size_t file_name_len, size_t sym_name_len, void **sym_addr); char *mono_path_resolve_symlinks(const char *path); char *monoeg_g_path_get_dirname (const char *filename); +char *monoeg_g_getenv (const char *variable); -void +static void cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data) { free (args); free (user_data); } -void register_bundled_modules () +static void +initialize_runtimeconfig () { - char *file_name = RUNTIMECONFIG_BIN_FILE; - int str_len = strlen (bundle_path) + strlen (file_name) + 1; // +1 is for the "/" - char *file_path = (char *)malloc (sizeof (char) * (str_len +1)); // +1 is for the terminating null character - int num_char = snprintf (file_path, (str_len + 1), "%s/%s", bundle_path, file_name); - struct stat buffer; - - assert (num_char > 0 && num_char == str_len); +%RUNTIME_CONFIG% +} - if (stat (file_path, &buffer) == 0) { - MonovmRuntimeConfigArguments *arg = (MonovmRuntimeConfigArguments *)malloc (sizeof (MonovmRuntimeConfigArguments)); - arg->kind = 0; - arg->runtimeconfig.name.path = file_path; - monovm_runtimeconfig_initialize (arg, cleanup_runtime_config, file_path); - } else { - free (file_path); - } +static void +initialize_appctx_env_variables () +{ +%APPCTX_ENV_VARIABLES% } static MonoAssembly* @@ -143,14 +133,18 @@ free_aot_data (MonoAssembly *assembly, int size, void *user_data, void *handle) munmap (handle, size); } -void runtime_init_callback () +void +runtime_init_callback () { + initialize_runtimeconfig (); + + initialize_appctx_env_variables (); + register_aot_modules (); - register_bundled_modules (); + // register all bundled modules - char *assemblyPath = assemblies_dir (); - mono_set_assemblies_path ((assemblyPath && assemblyPath[0] != '\0') ? assemblyPath : "./"); + mono_set_assemblies_path ((bundle_path && bundle_path[0] != '\0') ? bundle_path : "./"); mono_jit_set_aot_only (true); @@ -168,15 +162,17 @@ void runtime_init_callback () mono_install_load_aot_data_hook (load_aot_data, free_aot_data, NULL); + mono_set_signal_chaining (true); + mono_jit_init ("dotnet.android"); // Pass in via LibraryBuilder? - // Load assemblies with UnmanagedCallersOnly exported methods - // TODO leverage get_loadable_assemblies and call mono_assembly_open on each - mono_assembly_open("Android.Device_Emulator.Aot_Llvm.Test.dll", NULL); +%ASSEMBLIES_LOADER% } -void init_mono_runtime () +void +init_mono_runtime () { + bundle_path = monoeg_g_getenv("%ASSETS_PATH%"); mono_set_runtime_init_callback (&runtime_init_callback); }