diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index b4e7fb22dfa962..d89c3e3703eeb7 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -25,6 +25,13 @@ true + + + + + <_MobileIntermediateOutputPath>$(IntermediateOutputPath)mobile + + AndroidTestRunner.dll + + + @(MonoAOTCompilerDefaultAotArguments, ';') + @(MonoAOTCompilerDefaultProcessArguments, ';') + + - + + + + + + + + AppDir="$(PublishDir)"> @@ -80,10 +110,6 @@ - - - - diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index d096cac023ebac..0853fd97915bb4 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -8,6 +8,7 @@ true Link $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)\runtimes\android-$(TargetArchitecture)\ + false @@ -23,6 +24,10 @@ + + + <_MobileIntermediateOutputPath>$(IntermediateOutputPath)mobile + @@ -39,6 +44,8 @@ + @@ -59,12 +66,13 @@ - @@ -72,13 +80,15 @@ + OutputDir="$(ApkDir)" + AppDir="$(PublishDir)"> diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs index f9c88cb5a1d957..af036e41e27375 100644 --- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs +++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs @@ -10,22 +10,36 @@ public class AndroidAppBuilderTask : Task { [Required] - public string SourceDir { get; set; } = ""!; + public string MonoRuntimeHeaders { get; set; } = ""!; + /// + /// Target directory with *dll and other content to be AOT'd and/or bundled + /// [Required] - public string MonoRuntimeHeaders { get; set; } = ""!; + public string AppDir { get; set; } = ""!; /// /// This library will be used as an entry-point (e.g. TestRunner.dll) /// public string MainLibraryFileName { get; set; } = ""!; + /// + /// List of paths to assemblies to be included in the app. For AOT builds the 'ObjectFile' metadata key needs to point to the object file. + /// + public ITaskItem[] Assemblies { get; set; } = Array.Empty(); + + /// + /// Prefer FullAOT mode for Emulator over JIT + /// + public bool ForceAOT { get; set; } + [Required] public string RuntimeIdentifier { get; set; } = ""!; [Required] public string OutputDir { get; set; } = ""!; + [Required] public string? ProjectName { get; set; } public string? AndroidSdk { get; set; } @@ -64,6 +78,7 @@ public override bool Execute() var apkBuilder = new ApkBuilder(); apkBuilder.ProjectName = ProjectName; + apkBuilder.AppDir = AppDir; apkBuilder.OutputDir = OutputDir; apkBuilder.AndroidSdk = AndroidSdk; apkBuilder.AndroidNdk = AndroidNdk; @@ -74,7 +89,9 @@ public override bool Execute() apkBuilder.NativeMainSource = NativeMainSource; apkBuilder.KeyStorePath = KeyStorePath; apkBuilder.ForceInterpreter = ForceInterpreter; - (ApkBundlePath, ApkPackageId) = apkBuilder.BuildApk(SourceDir, abi, MainLibraryFileName, MonoRuntimeHeaders); + apkBuilder.ForceAOT = ForceAOT; + apkBuilder.Assemblies = Assemblies; + (ApkBundlePath, ApkPackageId) = apkBuilder.BuildApk(abi, MainLibraryFileName, MonoRuntimeHeaders); return true; } diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 71a83cf2a65b59..0ce54fd15c1d6d 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -5,12 +5,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Build.Framework; public class ApkBuilder { private const string DefaultMinApiLevel = "21"; public string? ProjectName { get; set; } + public string? AppDir { get; set; } public string? AndroidNdk { get; set; } public string? AndroidSdk { get; set; } public string? MinApiLevel { get; set; } @@ -21,49 +23,57 @@ public class ApkBuilder public string? NativeMainSource { get; set; } public string? KeyStorePath { get; set; } public bool ForceInterpreter { get; set; } + public bool ForceAOT { get; set; } + public ITaskItem[] Assemblies { get; set; } = Array.Empty(); public (string apk, string packageId) BuildApk( - string sourceDir, string abi, - string entryPointLib, + string mainLibraryFileName, string monoRuntimeHeaders) { - if (!Directory.Exists(sourceDir)) - throw new ArgumentException($"sourceDir='{sourceDir}' is empty or doesn't exist"); - - if (string.IsNullOrEmpty(abi)) - throw new ArgumentException("abi shoudln't be empty (e.g. x86, x86_64, armeabi-v7a or arm64-v8a"); + if (string.IsNullOrEmpty(AppDir) || !Directory.Exists(AppDir)) + { + throw new ArgumentException($"AppDir='{AppDir}' is empty or doesn't exist"); + } - string entryPointLibPath = ""; - if (!string.IsNullOrEmpty(entryPointLib)) + if (!string.IsNullOrEmpty(mainLibraryFileName) && !File.Exists(Path.Combine(AppDir, mainLibraryFileName))) { - entryPointLibPath = Path.Combine(sourceDir, entryPointLib); - if (!File.Exists(entryPointLibPath)) - throw new ArgumentException($"{entryPointLib} was not found in sourceDir='{sourceDir}'"); + throw new ArgumentException($"MainLibraryFileName='{mainLibraryFileName}' was not found in AppDir='{AppDir}'"); } - if (string.IsNullOrEmpty(ProjectName)) + if (string.IsNullOrEmpty(abi)) { - if (string.IsNullOrEmpty(entryPointLib)) - throw new ArgumentException("ProjectName needs to be set if entryPointLib is empty."); - else - ProjectName = Path.GetFileNameWithoutExtension(entryPointLib); + throw new ArgumentException("abi should not be empty (e.g. x86, x86_64, armeabi-v7a or arm64-v8a"); } - if (ProjectName.Contains(' ')) - throw new ArgumentException($"ProjectName='{ProjectName}' shouldn't not contain spaces."); + if (!string.IsNullOrEmpty(ProjectName) && ProjectName.Contains(' ')) + { + throw new ArgumentException($"ProjectName='{ProjectName}' should not not contain spaces."); + } - if (string.IsNullOrEmpty(AndroidSdk)) + if (string.IsNullOrEmpty(AndroidSdk)){ AndroidSdk = Environment.GetEnvironmentVariable("ANDROID_SDK_ROOT"); + } if (string.IsNullOrEmpty(AndroidNdk)) + { AndroidNdk = Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT"); + } if (string.IsNullOrEmpty(AndroidSdk) || !Directory.Exists(AndroidSdk)) + { throw new ArgumentException($"Android SDK='{AndroidSdk}' was not found or empty (can be set via ANDROID_SDK_ROOT envvar)."); + } if (string.IsNullOrEmpty(AndroidNdk) || !Directory.Exists(AndroidNdk)) + { throw new ArgumentException($"Android NDK='{AndroidNdk}' was not found or empty (can be set via ANDROID_NDK_ROOT envvar)."); + } + + if (ForceInterpreter && ForceAOT) + { + throw new InvalidOperationException("Interpreter and AOT cannot be enabled at the same time"); + } // Try to get the latest build-tools version if not specified if (string.IsNullOrEmpty(BuildToolsVersion)) @@ -88,7 +98,25 @@ public class ApkBuilder string buildToolsFolder = Path.Combine(AndroidSdk, "build-tools", BuildToolsVersion); if (!Directory.Exists(buildToolsFolder)) + { throw new ArgumentException($"{buildToolsFolder} was not found."); + } + + var assemblerFiles = new List(); + foreach (ITaskItem file in Assemblies) + { + // use AOT files if available + var obj = file.GetMetadata("AssemblerFile"); + if (!string.IsNullOrEmpty(obj)) + { + assemblerFiles.Add(obj); + } + } + + if (ForceAOT && !assemblerFiles.Any()) + { + throw new InvalidOperationException("Need list of AOT files."); + } Directory.CreateDirectory(OutputDir); Directory.CreateDirectory(Path.Combine(OutputDir, "bin")); @@ -105,7 +133,7 @@ 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(sourceDir, Path.Combine(OutputDir, "assets-tozip"), file => + Utils.DirectoryCopy(AppDir, Path.Combine(OutputDir, "assets-tozip"), file => { string fileName = Path.GetFileName(file); string extension = Path.GetExtension(file); @@ -143,13 +171,41 @@ public class ApkBuilder // 1. Build libmonodroid.so` via cmake - string monoRuntimeLib = Path.Combine(sourceDir, "libmonosgen-2.0.a"); + string monoRuntimeLib = Path.Combine(AppDir, "libmonosgen-2.0.a"); if (!File.Exists(monoRuntimeLib)) - throw new ArgumentException($"libmonosgen-2.0.a was not found in {sourceDir}"); + { + throw new ArgumentException($"libmonosgen-2.0.a was not found in {AppDir}"); + } + else + { + monoRuntimeLib = $" {monoRuntimeLib}{Environment.NewLine}"; + } + + string aotSources = ""; + foreach (string asm in assemblerFiles) + { + // these libraries are linked via modules.c + aotSources += $" {asm}{Environment.NewLine}"; + } string cmakeLists = Utils.GetEmbeddedResource("CMakeLists-android.txt") .Replace("%MonoInclude%", monoRuntimeHeaders) - .Replace("%NativeLibrariesToLink%", monoRuntimeLib); + .Replace("%NativeLibrariesToLink%", monoRuntimeLib) + .Replace("%AotSources%", aotSources) + .Replace("%AotModulesSource%", string.IsNullOrEmpty(aotSources) ? "" : "modules.c"); + + string defines = ""; + if (ForceInterpreter) + { + defines = "add_definitions(-DFORCE_INTERPRETER=1)"; + } + else if (ForceAOT) + { + defines = "add_definitions(-DFORCE_AOT=1)"; + } + + cmakeLists = cmakeLists.Replace("%Defines%", defines); + File.WriteAllText(Path.Combine(OutputDir, "CMakeLists.txt"), cmakeLists); File.WriteAllText(Path.Combine(OutputDir, "monodroid.c"), Utils.GetEmbeddedResource("monodroid.c")); @@ -186,13 +242,13 @@ public class ApkBuilder File.WriteAllText(javaActivityPath, Utils.GetEmbeddedResource("MainActivity.java") - .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib))); + .Replace("%EntryPointLibName%", Path.GetFileName(mainLibraryFileName))); if (!string.IsNullOrEmpty(NativeMainSource)) File.Copy(NativeMainSource, javaActivityPath, true); string monoRunner = Utils.GetEmbeddedResource("MonoRunner.java") - .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib)) - .Replace("%ForceInterpreter%", ForceInterpreter.ToString().ToLower()); + .Replace("%EntryPointLibName%", Path.GetFileName(mainLibraryFileName)); + File.WriteAllText(monoRunnerPath, monoRunner); File.WriteAllText(Path.Combine(OutputDir, "AndroidManifest.xml"), @@ -213,7 +269,7 @@ public class ApkBuilder var dynamicLibs = new List(); dynamicLibs.Add(Path.Combine(OutputDir, "monodroid", "libmonodroid.so")); - dynamicLibs.AddRange(Directory.GetFiles(sourceDir, "*.so").Where(file => Path.GetFileName(file) != "libmonodroid.so")); + dynamicLibs.AddRange(Directory.GetFiles(AppDir, "*.so").Where(file => Path.GetFileName(file) != "libmonodroid.so")); // add all *.so files to lib/%abi%/ Directory.CreateDirectory(Path.Combine(OutputDir, "lib", abi)); diff --git a/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt b/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt index 48e1d080fcd5d3..c74d4625af0fa0 100644 --- a/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt +++ b/src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt @@ -2,10 +2,21 @@ cmake_minimum_required(VERSION 3.10) project(monodroid) +enable_language(C ASM) + +if(NOT USE_LLVM) + # the assembler code we generate is GNU which isn't understood by llvm + add_compile_options(-no-integrated-as) +endif() + add_library( monodroid SHARED - monodroid.c) + monodroid.c + %AotModulesSource% + %AotSources%) + +%Defines% include_directories("%MonoInclude%") diff --git a/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java b/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java index 5618fdd61504c9..ad5fa5348a4144 100644 --- a/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java +++ b/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java @@ -43,7 +43,6 @@ public class MonoRunner extends Instrumentation static String entryPointLibName = "%EntryPointLibName%"; static Bundle result = new Bundle(); - static boolean forceInterpreter = %ForceInterpreter%; @Override public void onCreate(Bundle arguments) { @@ -84,7 +83,7 @@ public static int initialize(String entryPointLibName, Context context) { unzipAssets(context, filesDir, "assets.zip"); Log.i("DOTNET", "MonoRunner initialize,, entryPointLibName=" + entryPointLibName); - return initRuntime(filesDir, cacheDir, docsDir, entryPointLibName, forceInterpreter); + return initRuntime(filesDir, cacheDir, docsDir, entryPointLibName); } @Override @@ -145,7 +144,7 @@ static void unzipAssets(Context context, String toPath, String zipName) { } } - static native int initRuntime(String libsDir, String cacheDir, String docsDir, String entryPointLibName, boolean forceInterpreter); + static native int initRuntime(String libsDir, String cacheDir, String docsDir, String entryPointLibName); static native int setEnv(String key, String value); } diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid.c b/src/tasks/AndroidAppBuilder/Templates/monodroid.c index 8d4ae5a5ac8d8b..56cd77a65e3947 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid.c @@ -13,16 +13,18 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include static char *bundle_path; static char *executable; -static bool force_interpreter; #define LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "DOTNET", fmt, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "DOTNET", fmt, ##__VA_ARGS__) @@ -80,6 +82,46 @@ mono_droid_assembly_preload_hook (MonoAssemblyName *aname, char **assemblies_pat return mono_droid_load_assembly (name, culture); } +static unsigned char * +load_aot_data (MonoAssembly *assembly, int size, void *user_data, void **out_handle) +{ + *out_handle = NULL; + + char path [1024]; + int res; + + MonoAssemblyName *assembly_name = mono_assembly_get_name (assembly); + const char *aname = mono_assembly_name_get_name (assembly_name); + + LOG_INFO ("Looking for aot data for assembly '%s'.", aname); + res = snprintf (path, sizeof (path) - 1, "%s/%s.aotdata", bundle_path, aname); + assert (res > 0); + + int fd = open (path, O_RDONLY); + if (fd < 0) { + LOG_INFO ("Could not load the aot data for %s from %s: %s\n", aname, path, strerror (errno)); + return NULL; + } + + void *ptr = mmap (NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if (ptr == MAP_FAILED) { + LOG_INFO ("Could not map the aot file for %s: %s\n", aname, strerror (errno)); + close (fd); + return NULL; + } + + close (fd); + LOG_INFO ("Loaded aot data for %s.\n", aname); + *out_handle = ptr; + return (unsigned char *) ptr; +} + +static void +free_aot_data (MonoAssembly *assembly, int size, void *user_data, void *handle) +{ + munmap (handle, size); +} + char * strdup_printf (const char *msg, ...) { @@ -147,6 +189,10 @@ log_callback (const char *log_domain, const char *log_level, const char *message } } +#if FORCE_AOT +void register_aot_modules (void); +#endif + int mono_droid_runtime_init (void) { @@ -174,6 +220,7 @@ mono_droid_runtime_init (void) mono_debug_init (MONO_DEBUG_FORMAT_MONO); mono_install_assembly_preload_hook (mono_droid_assembly_preload_hook, NULL); + mono_install_load_aot_data_hook (load_aot_data, free_aot_data, NULL); mono_install_unhandled_exception_hook (unhandled_exception_handler, NULL); mono_trace_set_log_handler (log_callback, NULL); mono_set_signal_chaining (true); @@ -184,10 +231,13 @@ mono_droid_runtime_init (void) mono_jit_parse_options (1, options); } - if (force_interpreter) { - LOG_INFO("Interp Enabled"); - mono_jit_set_aot_mode(MONO_AOT_MODE_INTERP_ONLY); - } +#if FORCE_INTERPRETER + LOG_INFO("Interp Enabled"); + mono_jit_set_aot_mode(MONO_AOT_MODE_INTERP_ONLY); +#elif FORCE_AOT + register_aot_modules(); + mono_jit_set_aot_mode(MONO_AOT_MODE_FULL); +#endif mono_jit_init_version ("dotnet.android", "mobile"); @@ -224,7 +274,7 @@ Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstrin } int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_docs_dir, jstring j_entryPointLibName, jboolean j_forceInterpreter) +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_docs_dir, jstring j_entryPointLibName) { char file_dir[2048]; char cache_dir[2048]; @@ -237,7 +287,6 @@ Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_ bundle_path = file_dir; executable = entryPointLibName; - force_interpreter = (bool)j_forceInterpreter; setenv ("HOME", bundle_path, true); setenv ("TMPDIR", cache_dir, true); @@ -245,3 +294,11 @@ Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_ return mono_droid_runtime_init (); } + +// called from C# +void +invoke_external_native_api (void (*callback)(void)) +{ + if (callback) + callback(); +} diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 7e5baee6f81dd7..d4cd019dce52e9 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -390,11 +390,13 @@ private void GenerateAotModulesTable(ITaskItem[] assemblies, string[]? profilers _fileWrites.Add(AotModulesTablePath!); if (parsedAotModulesTableLanguage == MonoAotModulesTableLanguage.C) { + writer.WriteLine("#include "); + foreach (var symbol in symbols) { writer.WriteLine($"extern void *{symbol};"); } - writer.WriteLine("static void register_aot_modules ()"); + writer.WriteLine("void register_aot_modules ()"); writer.WriteLine("{"); foreach (var symbol in symbols) { diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/tasks/AotCompilerTask/MonoAOTCompiler.props index db518b2e8455da..852a5330a0e733 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.props @@ -14,6 +14,9 @@ + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT/Android.Device_Emulator.Aot.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/AOT/Android.Device_Emulator.Aot.Test.csproj new file mode 100644 index 00000000000000..49fd6653dde851 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT/Android.Device_Emulator.Aot.Test.csproj @@ -0,0 +1,16 @@ + + + Exe + false + true + true + $(NetCoreAppCurrent) + Android.Device_Emulator.Aot.Test.dll + 42 + true + + + + + + diff --git a/src/tests/FunctionalTests/Android/Emulator/Interpreter/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/AOT/Program.cs similarity index 100% rename from src/tests/FunctionalTests/Android/Emulator/Interpreter/Program.cs rename to src/tests/FunctionalTests/Android/Device_Emulator/AOT/Program.cs diff --git a/src/tests/FunctionalTests/Android/Emulator/Interpreter/Android.Emulator.Interpreter.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/Interpreter/Android.Device_Emulator.Interpreter.Test.csproj similarity index 82% rename from src/tests/FunctionalTests/Android/Emulator/Interpreter/Android.Emulator.Interpreter.Test.csproj rename to src/tests/FunctionalTests/Android/Device_Emulator/Interpreter/Android.Device_Emulator.Interpreter.Test.csproj index f20f4348a2637f..cd209e1ddd3a53 100644 --- a/src/tests/FunctionalTests/Android/Emulator/Interpreter/Android.Emulator.Interpreter.Test.csproj +++ b/src/tests/FunctionalTests/Android/Device_Emulator/Interpreter/Android.Device_Emulator.Interpreter.Test.csproj @@ -5,7 +5,7 @@ false true $(NetCoreAppCurrent) - Android.Emulator.Interpreter.Test.dll + Android.Device_Emulator.Interpreter.Test.dll 42 diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/Interpreter/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/Interpreter/Program.cs new file mode 100644 index 00000000000000..7dcc0f375db878 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/Interpreter/Program.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +public static class Program +{ + public static int Main(string[] args) + { + Console.WriteLine("Hello, Android!"); // logcat + return 42; + } +} diff --git a/src/tests/FunctionalTests/Android/Emulator/JIT/Android.Emulator.JIT.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/JIT/Android.Device_Emulator.JIT.Test.csproj similarity index 81% rename from src/tests/FunctionalTests/Android/Emulator/JIT/Android.Emulator.JIT.Test.csproj rename to src/tests/FunctionalTests/Android/Device_Emulator/JIT/Android.Device_Emulator.JIT.Test.csproj index 109a6cc549ca7b..3503f290ce73ea 100644 --- a/src/tests/FunctionalTests/Android/Emulator/JIT/Android.Emulator.JIT.Test.csproj +++ b/src/tests/FunctionalTests/Android/Device_Emulator/JIT/Android.Device_Emulator.JIT.Test.csproj @@ -4,7 +4,7 @@ false true $(NetCoreAppCurrent) - Android.Emulator.JIT.Test.dll + Android.Device_Emulator.JIT.Test.dll 42 diff --git a/src/tests/FunctionalTests/Android/Emulator/JIT/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/JIT/Program.cs similarity index 100% rename from src/tests/FunctionalTests/Android/Emulator/JIT/Program.cs rename to src/tests/FunctionalTests/Android/Device_Emulator/JIT/Program.cs diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Android.Device_Emulator.PInvoke.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Android.Device_Emulator.PInvoke.Test.csproj new file mode 100644 index 00000000000000..3e8c96b95a705c --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Android.Device_Emulator.PInvoke.Test.csproj @@ -0,0 +1,16 @@ + + + Exe + true + false + true + $(NetCoreAppCurrent) + Android.Device_Emulator.PInvoke.Test.dll + false + 42 + + + + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Program.cs new file mode 100644 index 00000000000000..750e83dc4785ad --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Program.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +public static class Program +{ + [DllImport("__Internal")] + unsafe private static extern void invoke_external_native_api(delegate* unmanaged callback); + + private static int counter = 1; + + [UnmanagedCallersOnly] + private static void Callback() + { + counter = 42; + } + + public static int Main(string[] args) + { + unsafe { + delegate* unmanaged unmanagedPtr = &Callback; + invoke_external_native_api(unmanagedPtr); + } + + return counter; + } +} diff --git a/src/tests/FunctionalTests/Android/Emulator/AOT/README.md b/src/tests/FunctionalTests/Android/Emulator/AOT/README.md deleted file mode 100644 index 55f50108e401f3..00000000000000 --- a/src/tests/FunctionalTests/Android/Emulator/AOT/README.md +++ /dev/null @@ -1 +0,0 @@ -TO-DO: add the test case for AOT mode when https://github.com/dotnet/runtime/pull/43535 has been completed. \ No newline at end of file diff --git a/src/tests/run.proj b/src/tests/run.proj index 73450c6344bbe8..83d512e561e769 100644 --- a/src/tests/run.proj +++ b/src/tests/run.proj @@ -473,7 +473,7 @@ namespace $([System.String]::Copy($(Category)).Replace(".","_").Replace("\",""). ProjectName="$(Category)" MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)/native/include/mono-2.0" StripDebugSymbols="$(StripDebugSymbols)" - SourceDir="$(BuildDir)" + AppDir="$(BuildDir)" OutputDir="$(AppDir)">