diff --git a/Xamarin.Android-Tests.sln b/Xamarin.Android-Tests.sln index 308a55fd1eb..40807925892 100644 --- a/Xamarin.Android-Tests.sln +++ b/Xamarin.Android-Tests.sln @@ -76,6 +76,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Performance-Tests", "Perfor EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "timing", "build-tools\timing\timing.csproj", "{37CAA28C-40BE-4253-BA68-CC5D7316A617}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedDSO", "tests\EmbeddedDSOs\EmbeddedDSO\EmbeddedDSO.csproj", "{056ED976-618F-4A3E-910E-AA25230C2296}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedDSO-UnitTests", "tests\EmbeddedDSOs\EmbeddedDSO-UnitTests\EmbeddedDSO-UnitTests.csproj", "{B160F0E7-799A-4EB9-92B8-D71623C7674A}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution tests\Xamarin.Forms-Performance-Integration\Xamarin.Forms.Performance.Integration.projitems*{195be9c2-1f91-40dc-bd6d-de860bf083fb}*SharedItemsImports = 13 @@ -194,6 +198,14 @@ Global {37CAA28C-40BE-4253-BA68-CC5D7316A617}.Debug|Any CPU.Build.0 = Debug|Any CPU {37CAA28C-40BE-4253-BA68-CC5D7316A617}.Release|Any CPU.ActiveCfg = Release|Any CPU {37CAA28C-40BE-4253-BA68-CC5D7316A617}.Release|Any CPU.Build.0 = Release|Any CPU + {056ED976-618F-4A3E-910E-AA25230C2296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {056ED976-618F-4A3E-910E-AA25230C2296}.Debug|Any CPU.Build.0 = Debug|Any CPU + {056ED976-618F-4A3E-910E-AA25230C2296}.Release|Any CPU.ActiveCfg = Release|Any CPU + {056ED976-618F-4A3E-910E-AA25230C2296}.Release|Any CPU.Build.0 = Release|Any CPU + {B160F0E7-799A-4EB9-92B8-D71623C7674A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B160F0E7-799A-4EB9-92B8-D71623C7674A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B160F0E7-799A-4EB9-92B8-D71623C7674A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B160F0E7-799A-4EB9-92B8-D71623C7674A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/build-tools/scripts/RunTests.targets b/build-tools/scripts/RunTests.targets index 2543d7674ff..626245cde70 100644 --- a/build-tools/scripts/RunTests.targets +++ b/build-tools/scripts/RunTests.targets @@ -17,12 +17,14 @@ <_TestAssembly Include="$(_TopDir)\bin\Test$(Configuration)\Xamarin.Android.Build.Tests.dll" /> <_TestAssembly Include="$(_TopDir)\bin\Test$(Configuration)\CodeBehind\CodeBehindUnitTests.dll" /> + <_TestAssembly Include="$(_TopDir)\bin\Test$(Configuration)\EmbeddedDSO\EmbeddedDSOUnitTests.dll" /> <_ApkTestProject Include="$(_TopDir)\src\Mono.Android\Test\Mono.Android-Tests.csproj" /> <_ApkTestProject Include="$(_TopDir)\tests\CodeGen-Binding\Xamarin.Android.JcwGen-Tests\Xamarin.Android.JcwGen-Tests.csproj" /> <_ApkTestProject Include="$(_TopDir)\tests\CodeGen-MkBundle\Xamarin.Android.MakeBundle-Tests\Xamarin.Android.MakeBundle-Tests.csproj" /> <_ApkTestProject Include="$(_TopDir)\tests\locales\Xamarin.Android.Locale-Tests\Xamarin.Android.Locale-Tests.csproj" /> <_ApkTestProject Include="$(_TopDir)\tests\BCL-Tests\Xamarin.Android.Bcl-Tests\Xamarin.Android.Bcl-Tests.csproj" /> <_ApkTestProject Include="$(_TopDir)\tests\Xamarin.Forms-Performance-Integration\Droid\Xamarin.Forms.Performance.Integration.Droid.csproj" /> + <_ApkTestProject Include="$(_TopDir)\tests\EmbeddedDSOs\EmbeddedDSO\EmbeddedDSO.csproj" /> <_ApkTestProjectAot Include="$(_TopDir)\src\Mono.Android\Test\Mono.Android-Tests.csproj" /> <_ApkTestProjectBundle Include="$(_TopDir)\src\Mono.Android\Test\Mono.Android-Tests.csproj" /> diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidZipAlign.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidZipAlign.cs index 57726552c35..54d15e9e7e6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AndroidZipAlign.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AndroidZipAlign.cs @@ -36,7 +36,7 @@ protected override string GenerateCommandLineCommands () string sourceFilename = Path.GetFileNameWithoutExtension (Source.ItemSpec); if (sourceFilename.EndsWith (strSignedUnaligned)) sourceFilename = sourceFilename.Remove (sourceFilename.Length - strSignedUnaligned.Length); - return string.Format ("{0} \"{1}\" \"{2}{3}{4}-Signed.apk\"", + return string.Format ("-p {0} \"{1}\" \"{2}{3}{4}-Signed.apk\"", Alignment, Source.ItemSpec, DestinationDirectory.ItemSpec, Path.DirectorySeparatorChar, sourceFilename); } diff --git a/src/monodroid/jni/dylib-mono.c b/src/monodroid/jni/dylib-mono.c index 67dd46b566d..871e00f6730 100644 --- a/src/monodroid/jni/dylib-mono.c +++ b/src/monodroid/jni/dylib-mono.c @@ -33,25 +33,18 @@ void monodroid_dylib_mono_free (struct DylibMono *mono_imports) free (mono_imports); } -int monodroid_dylib_mono_init (struct DylibMono *mono_imports, const char *libmono_path) +int monodroid_dylib_mono_init (struct DylibMono *mono_imports, void *libmono_handle) { int symbols_missing = FALSE; if (mono_imports == NULL) return FALSE; - memset (mono_imports, 0, sizeof (*mono_imports)); - - /* - * We need to use RTLD_GLOBAL so that libmono-profiler-log.so can resolve - * symbols against the Mono library we're loading. - */ - mono_imports->dl_handle = dlopen (libmono_path, RTLD_LAZY | RTLD_GLOBAL); - - if (!mono_imports->dl_handle) { + if (libmono_handle == NULL) return FALSE; - } + memset (mono_imports, 0, sizeof (*mono_imports)); + mono_imports->dl_handle = libmono_handle; mono_imports->version = sizeof (*mono_imports); log_info (LOG_DEFAULT, "Loading Mono symbols..."); diff --git a/src/monodroid/jni/dylib-mono.h b/src/monodroid/jni/dylib-mono.h index 9d95e4fb7a3..f32d82c6623 100644 --- a/src/monodroid/jni/dylib-mono.h +++ b/src/monodroid/jni/dylib-mono.h @@ -348,6 +348,6 @@ struct DylibMono { MONO_API struct DylibMono* monodroid_dylib_mono_new (const char *libmono_path); MONO_API void monodroid_dylib_mono_free (struct DylibMono *mono_imports); - int monodroid_dylib_mono_init (struct DylibMono *mono_imports, const char *libmono_path); + int monodroid_dylib_mono_init (struct DylibMono *mono_imports, void *libmono_handle); #endif /* INC_MONODROID_DYLIB_MONO_H */ diff --git a/src/monodroid/jni/monodroid-glue.c b/src/monodroid/jni/monodroid-glue.c index 229376cad94..aaefe486e60 100644 --- a/src/monodroid/jni/monodroid-glue.c +++ b/src/monodroid/jni/monodroid-glue.c @@ -72,6 +72,7 @@ #include "ioapi.h" #include "monodroid-glue.h" #include "mkbundle-api.h" +#include "cpu-arch.h" #ifndef WINDOWS #include "xamarin_getifaddrs.h" @@ -100,6 +101,22 @@ static int wait_for_gdb; static volatile int monodroid_gdb_wait = TRUE; static int android_api_level = 0; +// Values correspond to the CPU_KIND_* macros +static const char* android_abi_names[CPU_KIND_X86_64+1] = { + "unknown", + [CPU_KIND_ARM] = "armeabi-v7a", + [CPU_KIND_ARM64] = "arm64-v8a", + [CPU_KIND_MIPS] = "mips", + [CPU_KIND_X86] = "x86", + [CPU_KIND_X86_64] = "x86_64", +}; +#define ANDROID_ABI_NAMES_SIZE (sizeof(android_abi_names) / sizeof (android_abi_names[0])) + +static void* load_dso (const char *path, int dl_flags, mono_bool skip_exists_check); +static void* load_dso_from_app_lib_dirs (const char *name, int dl_flags); +static void* load_dso_from_override_dirs (const char *name, int dl_flags); +static void* load_dso_from_any_directories (const char *name, int dl_flags); + /* Can be called by a native debugger to break the wait on startup */ MONO_API void monodroid_clear_gdb_wait (void) @@ -467,7 +484,9 @@ monodroid_get_dylib (void) return &mono; } -static const char *app_libdir; +static const char **app_lib_directories; +static size_t app_lib_directories_size = 0; +static int embedded_dso_mode = 0; int file_copy(const char *to, const char *from) { @@ -639,6 +658,7 @@ static char* get_libmonosgen_path () { char *libmonoso; + int i; #ifndef RELEASE // Android 5 includes some restrictions on loading dynamic libraries via dlopen() from @@ -647,11 +667,16 @@ get_libmonosgen_path () copy_monosgen_to_internal_location (primary_override_dir, external_override_dir); copy_monosgen_to_internal_location (primary_override_dir, external_legacy_override_dir); - int i; - for (i = 0; i < MAX_OVERRIDES; ++i) - TRY_LIBMONOSGEN (override_dirs [i]) + if (!embedded_dso_mode) { + for (i = 0; i < MAX_OVERRIDES; ++i) + TRY_LIBMONOSGEN (override_dirs [i]); + } #endif - TRY_LIBMONOSGEN (app_libdir) + if (!embedded_dso_mode) { + for (i = 0; i < app_lib_directories_size; i++) { + TRY_LIBMONOSGEN (app_lib_directories [i]); + } + } libmonoso = runtime_libdir ? monodroid_strdup_printf ("%s" MONODROID_PATH_SEPARATOR MONO_SGEN_ARCH_SO, runtime_libdir, sizeof(void*) == 8 ? "64bit" : "32bit") : NULL; if (libmonoso && file_exists (libmonoso)) { @@ -686,13 +711,22 @@ get_libmonosgen_path () TRY_LIBMONOSGEN (get_libmonoandroid_directory_path ()) #endif - TRY_LIBMONOSGEN (SYSTEM_LIB_PATH) - -#ifdef RELEASE - log_fatal (LOG_DEFAULT, "cannot find libmonosgen-2.0.so in app_libdir: %s nor in previously printed locations.", app_libdir); -#else - log_fatal (LOG_DEFAULT, "cannot find libmonosgen-2.0.so in override_dir: %s, app_libdir: %s nor in previously printed locations.", override_dirs[0], app_libdir); + TRY_LIBMONOSGEN (SYSTEM_LIB_PATH); + log_fatal (LOG_DEFAULT, "Cannot find '%s'. Looked in the following locations:", MONO_SGEN_SO); + +#ifndef RELEASE + if (!embedded_dso_mode) { + for (i = 0; i < MAX_OVERRIDES; ++i) { + if (override_dirs [i] == NULL) + continue; + log_fatal (LOG_DEFAULT, " %s", override_dirs [i]); + } + } #endif + for (i = 0; i < app_lib_directories_size; i++) { + log_fatal (LOG_DEFAULT, " %s", app_lib_directories [i]); + } + log_fatal (LOG_DEFAULT, "Do you have a shared runtime build of your app with AndroidManifest.xml android:minSdkVersion < 10 while running on a 64-bit Android 5.0 target? This combination is not supported."); log_fatal (LOG_DEFAULT, "Please either set android:minSdkVersion >= 10 or use a build without the shared runtime (like default Release configuration)."); exit (FATAL_EXIT_CANNOT_FIND_LIBMONOSGEN); @@ -704,53 +738,185 @@ typedef void* (*mono_mkbundle_init_ptr) (void (*)(const MonoBundledAssembly **), mono_mkbundle_init_ptr mono_mkbundle_init; void (*mono_mkbundle_initialize_mono_api) (const BundleMonoAPI *info); -static void -setup_bundled_app (const char *libappso) +static char* +get_full_dso_path (const char *base_dir, const char *dso_path, mono_bool *needs_free) { - void *libapp; + assert (needs_free); + + *needs_free = FALSE; + if (!dso_path) + return NULL; - libapp = dlopen (libappso, RTLD_LAZY); + if (base_dir == NULL || is_path_rooted (dso_path)) + return (char*)dso_path; // Absolute path or no base path, can't do much with it - if (!libapp) { - log_fatal (LOG_BUNDLE, "bundled app initialization error: %s", dlerror ()); - exit (FATAL_EXIT_CANNOT_LOAD_BUNDLE); + char *full_path = path_combine (base_dir, dso_path); + *needs_free = TRUE; + return full_path; +} + +static void* +load_dso (const char *path, int dl_flags, mono_bool skip_exists_check) +{ + if (path == NULL) + return NULL; + + log_info (LOG_ASSEMBLY, "Trying to load shared library '%s'", path); + if (!skip_exists_check && !embedded_dso_mode && !file_exists (path)) { + log_info (LOG_ASSEMBLY, "Shared library '%s' not found", path); + return NULL; } - mono_mkbundle_initialize_mono_api = dlsym (libapp, "initialize_mono_api"); - if (!mono_mkbundle_initialize_mono_api) - log_error (LOG_BUNDLE, "Missing initialize_mono_api in the application"); + void *handle = dlopen (path, dl_flags); + if (handle == NULL) + log_info (LOG_ASSEMBLY, "Failed to load shared library '%s'. %s", path, dlerror ()); + return handle; +} - mono_mkbundle_init = dlsym (libapp, "mono_mkbundle_init"); - if (!mono_mkbundle_init) - log_error (LOG_BUNDLE, "Missing mono_mkbundle_init in the application"); - log_info (LOG_BUNDLE, "Bundled app loaded: %s", libappso); +static void* +load_dso_from_specified_dirs (const char **directories, int num_entries, const char *dso_name, int dl_flags) +{ + assert (directories); + if (dso_name == NULL) + return NULL; + + mono_bool needs_free = FALSE; + char *full_path = NULL; + for (int i = 0; i < num_entries; i++) { + full_path = get_full_dso_path (directories [i], dso_name, &needs_free); + void *handle = load_dso (full_path, dl_flags, FALSE); + if (needs_free) + free (full_path); + if (handle != NULL) + return handle; + } + + return NULL; +} + +static void* +load_dso_from_app_lib_dirs (const char *name, int dl_flags) +{ + return load_dso_from_specified_dirs (app_lib_directories, app_lib_directories_size, name, dl_flags); +} + +static void* +load_dso_from_override_dirs (const char *name, int dl_flags) +{ +#ifdef RELEASE + return NULL; +#else + return load_dso_from_specified_dirs (override_dirs, MAX_OVERRIDES, name, dl_flags); +#endif +} + +static void* load_dso_from_any_directories (const char *name, int dl_flags) +{ + void *handle = load_dso_from_override_dirs (name, dl_flags); + if (handle == NULL) + handle = load_dso_from_app_lib_dirs (name, dl_flags); + return handle; } static char* -get_bundled_app (JNIEnv *env, jstring dir) +get_existing_dso_path_on_disk (const char *base_dir, const char *dso_name, mono_bool *needs_free) { - const char *v; - char *libapp; + assert (needs_free); -#ifndef RELEASE - libapp = path_combine (override_dirs [0], "libmonodroid_bundle_app.so"); + *needs_free = FALSE; + char *dso_path = get_full_dso_path (base_dir, dso_name, needs_free); + if (file_exists (dso_path)) + return dso_path; - if (file_exists (libapp)) - return libapp; + *needs_free = FALSE; + free (dso_path); + return NULL; +} - free (libapp); + +static void +dso_alloc_cleanup (char **dso_path, mono_bool *needs_free) +{ + assert (needs_free); + if (dso_path != NULL) { + if (*needs_free) + free (*dso_path); + *dso_path = NULL; + } + *needs_free = FALSE; +} + +static char* +get_full_dso_path_on_disk (const char *dso_name, mono_bool *needs_free) +{ + assert (needs_free); + + *needs_free = FALSE; + if (embedded_dso_mode) + return NULL; +#ifndef RELEASE + char *dso_path = NULL; + for (int i = 0; i < MAX_OVERRIDES; i++) { + if (override_dirs [i] == NULL) + continue; + dso_path = get_existing_dso_path_on_disk (override_dirs [i], dso_name, needs_free); + if (dso_path != NULL) + return dso_path; + dso_alloc_cleanup (&dso_path, needs_free); + } #endif + for (int i = 0; i < app_lib_directories_size; i++) { + dso_path = get_existing_dso_path_on_disk (app_lib_directories [i], dso_name, needs_free); + if (dso_path != NULL) + return dso_path; + dso_alloc_cleanup (&dso_path, needs_free); + } - if (dir) { - v = (*env)->GetStringUTFChars (env, dir, NULL); - if (v) { - libapp = path_combine (v, "libmonodroid_bundle_app.so"); - (*env)->ReleaseStringUTFChars (env, dir, v); - if (file_exists (libapp)) - return libapp; + return NULL; +} + +// This function could be improved if we somehow marked an apk containing just the bundled app as +// such - perhaps another __XA* environment variable? Would certainly make code faster. +static void +setup_bundled_app (const char *dso_name) +{ + static int dlopen_flags = RTLD_LAZY; + void *libapp = NULL; + + if (embedded_dso_mode) { + log_info (LOG_DEFAULT, "bundle app: embedded DSO mode"); + libapp = load_dso_from_any_directories (dso_name, dlopen_flags); + } else { + mono_bool needs_free = FALSE; + log_info (LOG_DEFAULT, "bundle app: normal mode"); + char *bundle_path = get_full_dso_path_on_disk (dso_name, &needs_free); + log_info (LOG_DEFAULT, "bundle_path == %s", bundle_path ? bundle_path : ""); + if (bundle_path == NULL) + return; + log_info (LOG_BUNDLE, "Attempting to load bundled app from %s", bundle_path); + libapp = load_dso (bundle_path, dlopen_flags, TRUE); + free (bundle_path); + } + + if (libapp == NULL) { + log_info (LOG_DEFAULT, "No libapp!"); + if (!embedded_dso_mode) { + log_fatal (LOG_BUNDLE, "bundled app initialization error"); + exit (FATAL_EXIT_CANNOT_LOAD_BUNDLE); + } else { + log_info (LOG_BUNDLE, "bundled app not found in the APK, ignoring."); + return; } } - return NULL; + + mono_mkbundle_initialize_mono_api = dlsym (libapp, "initialize_mono_api"); + if (!mono_mkbundle_initialize_mono_api) + log_error (LOG_BUNDLE, "Missing initialize_mono_api in the application"); + + mono_mkbundle_init = dlsym (libapp, "mono_mkbundle_init"); + if (!mono_mkbundle_init) + log_error (LOG_BUNDLE, "Missing mono_mkbundle_init in the application"); + log_info (LOG_BUNDLE, "Bundled app loaded: %s", dso_name); } static JavaVM *jvm; @@ -2922,7 +3088,6 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jobject loader) void *args [1]; args [0] = &init; - android_api_level = GetAndroidSdkVersion (env, loader); init.javaVm = jvm; init.env = env; init.logCategories = log_categories; @@ -3098,48 +3263,51 @@ convert_dl_flags (int flags) static void* monodroid_dlopen (const char *name, int flags, char **err, void *user_data) { + int dl_flags = convert_dl_flags (flags); + void *h = NULL; + char *full_name = NULL; + char *basename = NULL; + mono_bool libmonodroid_fallback = FALSE; + /* name is NULL when we're P/Invoking __Internal, so remap to libmonodroid */ - char *full_name = path_combine (app_libdir, name ? name : "libmonodroid.so"); - if (!name && !file_exists (full_name)) { - log_info (LOG_ASSEMBLY, "Trying to load library '%s'", full_name); - free (full_name); - full_name = path_combine (SYSTEM_LIB_PATH, "libmonodroid.so"); + if (name == NULL) { + name = "libmonodroid.so"; + libmonodroid_fallback = TRUE; } - int dl_flags = convert_dl_flags (flags); - void *h = dlopen (full_name, dl_flags); - log_info (LOG_ASSEMBLY, "Trying to load library '%s'", full_name); - if (!h && name && (strstr (name, ".dll.so") || strstr (name, ".exe.so"))) { - char *full_name2; - const char *basename; + h = load_dso_from_any_directories (name, dl_flags); + if (h != NULL) { + goto done_and_out; + } - if (strrchr (name, '/')) - basename = strrchr (name, '/') + 1; - else - basename = name; + if (libmonodroid_fallback) { + full_name = path_combine (SYSTEM_LIB_PATH, "libmonodroid.so"); + h = load_dso (full_name, dl_flags, FALSE); + goto done_and_out; + } - /* Try loading AOT modules from the override dir */ - if (override_dirs [0]) { - full_name2 = monodroid_strdup_printf ("%s" MONODROID_PATH_SEPARATOR "libaot-%s", override_dirs [0], basename); - h = dlopen (full_name2, dl_flags); - free (full_name2); - } + if (!strstr (name, ".dll.so") && !strstr (name, ".exe.so")) { + goto done_and_out; + } - /* Try loading AOT modules from the lib dir */ - if (!h) { - full_name2 = monodroid_strdup_printf ("%s" MONODROID_PATH_SEPARATOR "libaot-%s", app_libdir, basename); - h = dlopen (full_name2, dl_flags); - free (full_name2); - } + basename = strrchr (name, '/'); + if (basename != NULL) + basename++; + else + basename = (char*)name; - if (h) - log_info (LOG_ASSEMBLY, "Loaded AOT image '%s'", full_name2); - } + basename = monodroid_strdup_printf ("libaot-%s", basename); + h = load_dso_from_any_directories (basename, dl_flags); + + if (h != NULL) + log_info (LOG_ASSEMBLY, "Loaded AOT image '%s'", basename); + done_and_out: if (!h && err) { *err = monodroid_strdup_printf ("Could not load library: Library '%s' not found.", full_name); } + free (basename); free (full_name); return h; @@ -3272,57 +3440,18 @@ load_profiler (void *handle, const char *desc, const char *symbol) } static mono_bool -load_embedded_profiler (const char *desc, const char *name) +load_profiler_from_handle (void *dso_handle, const char *desc, const char *name) { - mono_bool result; - - char *full_name = path_combine (app_libdir, "libmonodroid.so"); - void *h = dlopen (full_name, RTLD_LAZY); - const char *e = dlerror (); - - log_warn (LOG_DEFAULT, "looking for embedded profiler within '%s': dlopen=%p error=%s", - full_name, - h, - h != NULL ? "" : e); - - free (full_name); - - if (!h) { - return 0; - } + if (!dso_handle) + return FALSE; char *symbol = monodroid_strdup_printf ("%s_%s", INITIALIZER_NAME, name); - if (!(result = load_profiler (h, desc, symbol))) - dlclose (h); + mono_bool result = load_profiler (dso_handle, desc, symbol); free (symbol); - - return result; -} - -static mono_bool -load_profiler_from_directory (const char *directory, const char *libname, const char *desc, const char *name) -{ - char *full_name = path_combine (directory, libname); - int exists = file_exists (full_name); - void *h = exists ? dlopen (full_name, RTLD_LAZY) : NULL; - const char *e = exists ? dlerror () : "No such file or directory"; - - log_warn (LOG_DEFAULT, "Trying to load profiler: %s: dlopen=%p error=%s", - full_name, - h, - h != NULL ? "" : e); - - free (full_name); - - if (h) { - char *symbol = monodroid_strdup_printf ("%s_%s", INITIALIZER_NAME, name); - mono_bool result = load_profiler (h, desc, symbol); - free (symbol); - if (result) - return 1; - dlclose (h); - } - return 0; + if (result) + return TRUE; + dlclose (dso_handle); + return FALSE; } static void @@ -3330,7 +3459,6 @@ monodroid_profiler_load (const char *libmono_path, const char *desc, const char { const char* col = strchr (desc, ':'); char *mname; - int oi; if (col != NULL) { mname = xmalloc (col - desc + 1); @@ -3340,29 +3468,19 @@ monodroid_profiler_load (const char *libmono_path, const char *desc, const char mname = monodroid_strdup_printf ("%s", desc); } + int dlopen_flags = RTLD_LAZY; char *libname = monodroid_strdup_printf ("libmono-profiler-%s.so", mname); - mono_bool found = 0; - - for (oi = 0; oi < MAX_OVERRIDES; ++oi) { - if (!directory_exists (override_dirs [oi])) - continue; - if ((found = load_profiler_from_directory (override_dirs [oi], libname, desc, mname))) - break; + void *handle = load_dso_from_any_directories (libname, dlopen_flags); + found = load_profiler_from_handle (handle, desc, mname); + + if (!found && libmono_path != NULL) { + char *full_path = path_combine (libmono_path, libname); + handle = load_dso (full_path, dlopen_flags, FALSE); + free (full_path); + found = load_profiler_from_handle (handle, desc, mname); } - do { - if (found) - break; - if ((found = load_profiler_from_directory (app_libdir, libname, desc, mname))) - break; - if ((found = load_embedded_profiler (desc, mname))) - break; - if (libmono_path != NULL && (found = load_profiler_from_directory (libmono_path, libname, desc, mname))) - break; - } while (0); - - if (found && logfile != NULL) set_world_accessable (logfile); @@ -3468,7 +3586,7 @@ setup_environment_from_line (const char *line) } static void -setup_environment_from_file (const char *apk, int index, int apk_count) +setup_environment_from_file (const char *apk, int index, int apk_count, void *user_data) { unzFile file; if ((file = unzOpen (apk)) == NULL) @@ -3510,7 +3628,7 @@ setup_environment_from_file (const char *apk, int index, int apk_count) } static void -for_each_apk (JNIEnv *env, jobjectArray runtimeApks, void (*handler) (const char *apk, int index, int apk_count)) +for_each_apk (JNIEnv *env, jobjectArray runtimeApks, void (*handler) (const char *apk, int index, int apk_count, void *user_data), void *user_data) { int i; jsize apksLength = (*env)->GetArrayLength (env, runtimeApks); @@ -3518,7 +3636,7 @@ for_each_apk (JNIEnv *env, jobjectArray runtimeApks, void (*handler) (const char jstring e = (*env)->GetObjectArrayElement (env, runtimeApks, i); const char *apk = (*env)->GetStringUTFChars (env, e, NULL); - handler (apk, i, apksLength); + handler (apk, i, apksLength, user_data); (*env)->ReleaseStringUTFChars (env, e, apk); } } @@ -3526,11 +3644,11 @@ for_each_apk (JNIEnv *env, jobjectArray runtimeApks, void (*handler) (const char static void setup_environment (JNIEnv *env, jobjectArray runtimeApks) { - for_each_apk (env, runtimeApks, setup_environment_from_file); + for_each_apk (env, runtimeApks, setup_environment_from_file, NULL); } static void -setup_process_args_apk (const char *apk, int index, int apk_count) +setup_process_args_apk (const char *apk, int index, int apk_count, void *user_data) { if (!apk || index != apk_count - 1) return; @@ -3542,7 +3660,7 @@ setup_process_args_apk (const char *apk, int index, int apk_count) static void setup_process_args (JNIEnv *env, jobjectArray runtimeApks) { - for_each_apk (env, runtimeApks, setup_process_args_apk); + for_each_apk (env, runtimeApks, setup_process_args_apk, NULL); } /* @@ -3688,7 +3806,7 @@ This is a hack to set llvm::DisablePrettyStackTrace to true and avoid this sourc static void disable_external_signal_handlers (void) { - void *llvm = dlopen ("libLLVM.so", RTLD_LAZY); + void *llvm = load_dso ("libLLVM.so", RTLD_LAZY, TRUE); if (llvm) { _Bool *disable_signals = dlsym (llvm, "_ZN4llvm23DisablePrettyStackTraceE"); if (disable_signals) { @@ -3742,6 +3860,14 @@ create_and_initialize_domain (JNIEnv* env, jobjectArray runtimeApks, jobjectArra return domain; } +static void +add_apk_libdir (const char *apk, int index, int apk_count, void *user_data) +{ + assert (user_data); + assert (index >= 0 && index < app_lib_directories_size); + app_lib_directories [index] = monodroid_strdup_printf ("%s!/lib/%s", apk, (const char*)user_data); +} + JNIEXPORT void JNICALL Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApks, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, jobjectArray externalStorageDirs, jobjectArray assemblies, jstring packageName) { @@ -3749,13 +3875,13 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject char *connect_args; jstring libdir_s; const char *libdir, *esd; - char *libmonosgen_path; - char *libmonodroid_bundle_app_path; char *counters_path; const char *pkgName; char *aotMode; int i; + android_api_level = GetAndroidSdkVersion (env, loader); + pkgName = (*env)->GetStringUTFChars (env, packageName, NULL); monodroid_store_package_name (pkgName); /* Will make a copy of the string */ (*env)->ReleaseStringUTFChars (env, packageName, pkgName); @@ -3772,6 +3898,26 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject setup_environment (env, runtimeApks); + if (android_api_level < 23 || getenv ("__XA_DSO_IN_APK") == NULL) { + log_info (LOG_DEFAULT, "Setting up for DSO lookup in app data directories"); + libdir_s = (*env)->GetObjectArrayElement (env, appDirs, 2); + libdir = (*env)->GetStringUTFChars (env, libdir_s, NULL); + app_lib_directories_size = 1; + app_lib_directories = (const char**) xcalloc (app_lib_directories_size, sizeof(char*)); + app_lib_directories [0] = monodroid_strdup_printf ("%s", libdir); + (*env)->ReleaseStringUTFChars (env, libdir_s, libdir); + } else { + log_info (LOG_DEFAULT, "Setting up for DSO lookup directly in the APK"); + embedded_dso_mode = 1; + app_lib_directories_size = (*env)->GetArrayLength (env, runtimeApks); + app_lib_directories = (const char**) xcalloc (app_lib_directories_size, sizeof(char*)); + + unsigned short built_for_cpu = 0, running_on_cpu = 0; + unsigned char is64bit = 0; + _monodroid_detect_cpu_and_architecture (&built_for_cpu, &running_on_cpu, &is64bit); + for_each_apk (env, runtimeApks, add_apk_libdir, android_abi_names [running_on_cpu]); + } + primary_override_dir = get_primary_override_dir (env, (*env)->GetObjectArrayElement (env, appDirs, 0)); esd = (*env)->GetStringUTFChars (env, (*env)->GetObjectArrayElement (env, externalStorageDirs, 0), NULL); external_override_dir = monodroid_strdup_printf ("%s", esd); @@ -3799,23 +3945,7 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject log_warn (LOG_DEFAULT, "Using override path: %s", p); } #endif - - jsize appDirsLength = (*env)->GetArrayLength (env, appDirs); - - for (i = 0; i < appDirsLength; ++i) { - jstring appDir = (*env)->GetObjectArrayElement (env, appDirs, i); - libmonodroid_bundle_app_path = get_bundled_app (env, appDir); - if (libmonodroid_bundle_app_path) { - setup_bundled_app (libmonodroid_bundle_app_path); - free (libmonodroid_bundle_app_path); - break; - } - } - - libdir_s = (*env)->GetObjectArrayElement (env, appDirs, 2); - libdir = (*env)->GetStringUTFChars (env, libdir_s, NULL); - app_libdir = monodroid_strdup_printf ("%s", libdir); - (*env)->ReleaseStringUTFChars (env, libdir_s, libdir); + setup_bundled_app ("libmonodroid_bundle_app.so"); if (runtimeNativeLibDir != NULL) { const char *rd; @@ -3824,14 +3954,25 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject (*env)->ReleaseStringUTFChars (env, runtimeNativeLibDir, rd); } - libmonosgen_path = get_libmonosgen_path (); - if (!monodroid_dylib_mono_init (&mono, libmonosgen_path)) { + void *libmonosgen_handle = NULL; + + /* + * We need to use RTLD_GLOBAL so that libmono-profiler-log.so can resolve + * symbols against the Mono library we're loading. + */ + int sgen_dlopen_flags = RTLD_LAZY | RTLD_GLOBAL; + if (embedded_dso_mode) { + libmonosgen_handle = load_dso_from_any_directories (MONO_SGEN_SO, sgen_dlopen_flags); + } + + if (libmonosgen_handle == NULL) + libmonosgen_handle = load_dso (get_libmonosgen_path (), sgen_dlopen_flags, FALSE); + + if (!monodroid_dylib_mono_init (&mono, libmonosgen_handle)) { log_fatal (LOG_DEFAULT, "shared runtime initialization error: %s", dlerror ()); exit (FATAL_EXIT_CANNOT_FIND_MONO); } setup_process_args (env, runtimeApks); - - free (libmonosgen_path); #ifndef WINDOWS _monodroid_getifaddrs_init (); #endif diff --git a/src/monodroid/jni/util.c b/src/monodroid/jni/util.c index 049adcde107..b0a199e2035 100644 --- a/src/monodroid/jni/util.c +++ b/src/monodroid/jni/util.c @@ -14,6 +14,7 @@ #ifdef WINDOWS #include +#include #endif #include "java-interop-util.h" @@ -500,3 +501,18 @@ monodroid_dirent_hasextension (monodroid_dirent_t *e, const char *extension) return result; #endif } + +mono_bool +is_path_rooted (const char *path) +{ + if (path == NULL) + return FALSE; +#ifdef WINDOWS + LPCWSTR wpath = utf8_to_utf16 (path); + BOOL ret = !PathIsRelativeW (wpath); + free (wpath); + return ret; +#else + return path [0] == MONODROID_PATH_SEPARATOR_CHAR; +#endif +} diff --git a/src/monodroid/jni/util.h b/src/monodroid/jni/util.h index aac332abdfa..79b0091d345 100644 --- a/src/monodroid/jni/util.h +++ b/src/monodroid/jni/util.h @@ -10,9 +10,11 @@ #endif #if WINDOWS -#define MONODROID_PATH_SEPARATOR "\\" +#define MONODROID_PATH_SEPARATOR "\\" +#define MONODROID_PATH_SEPARATOR_CHAR '\\' #else -#define MONODROID_PATH_SEPARATOR "/" +#define MONODROID_PATH_SEPARATOR "/" +#define MONODROID_PATH_SEPARATOR_CHAR '/' #endif #if WINDOWS @@ -63,5 +65,6 @@ void *monodroid_runtime_invoke (struct DylibMono *mono, MonoDomain *d MonoClass *monodroid_get_class_from_name (struct DylibMono *mono, MonoDomain *domain, const char* assembly, const char *namespace, const char *type); MonoDomain *monodroid_create_appdomain (struct DylibMono *mono, MonoDomain *parent_domain, const char *friendly_name, int shadow_copy, const char *shadow_directories); MonoClass *monodroid_get_class_from_image (struct DylibMono *mono, MonoDomain *domain, MonoImage* image, const char *namespace, const char *type); +mono_bool is_path_rooted (const char *path); #endif /* __MONODROID_UTIL_H__ */ diff --git a/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/BuildTests.cs b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/BuildTests.cs new file mode 100644 index 00000000000..19050a11295 --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/BuildTests.cs @@ -0,0 +1,299 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Xml; +using System.Xml.XPath; + +using Microsoft.Build.Framework; +using NUnit.Framework; +using Xamarin.ProjectTools; +using Xamarin.Tools.Zip; + +using XABuildPaths = global::Xamarin.Android.Build.Paths; + +namespace EmbeddedDSOUnitTests +{ + sealed class LocalBuilder : Builder + { + public bool Build (string projectOrSolution, string target, string[] parameters = null, Dictionary environmentVariables = null) + { + return BuildInternal (projectOrSolution, target, parameters, environmentVariables); + } + } + + [Parallelizable (ParallelScope.Children)] + public class BuildTests_EmbeddedDSOBuildTests + { + const string ProjectName = "EmbeddedDSO"; + const string ProjectAssemblyName = "Xamarin.Android.EmbeddedDSO_Test"; + + static readonly string TestProjectRootDirectory; + static readonly string TestOutputDir; + + static readonly List produced_binaries = new List { + $"{ProjectAssemblyName}.dll", + $"{ProjectAssemblyName}-Signed.apk", + $"{ProjectAssemblyName}.apk", + }; + + static readonly List log_files = new List { + "process.log", + "msbuild.binlog", + }; + + string testProjectPath; + + static BuildTests_EmbeddedDSOBuildTests () + { + TestProjectRootDirectory = Path.GetFullPath (Path.Combine (XABuildPaths.TopDirectory, "tests", "EmbeddedDSOs", "EmbeddedDSO")); + TestOutputDir = Path.Combine (XABuildPaths.TestOutputDirectory, "EmbeddedDSO"); + + produced_binaries = new List { + $"{ProjectAssemblyName}.dll", + $"{ProjectAssemblyName}-Signed.apk", + $"{ProjectAssemblyName}.apk", + }; + } + + [TestFixtureSetUp] + public void BuildProject () + { + testProjectPath = PrepareProject (ProjectName); + string projectPath = Path.Combine (testProjectPath, $"{ProjectName}.csproj"); + LocalBuilder builder = GetBuilder ("EmbeddedDSO"); + bool success = builder.Build (projectPath, "SignAndroidPackage", new [] { "UnitTestsMode=true" }); + + Assert.That (success, Is.True, "Should have been built"); + } + + [Test] + public void BinariesExist () + { + foreach (string binary in produced_binaries) { + string fp = Path.Combine (testProjectPath, "bin", XABuildPaths.Configuration, binary); + + Assert.That (new FileInfo (fp), Does.Exist, $"File {fp} should exist"); + } + } + + [Test] + public void DSOPageAlignment () + { + Assert.That (new FileInfo (Config.ZipAlignPath), Does.Exist, $"ZipAlign not found at ${Config.ZipAlignPath}"); + + string apk = Path.Combine (testProjectPath, "bin", XABuildPaths.Configuration, $"{ProjectAssemblyName}-Signed.apk"); + Assert.That (new FileInfo (apk), Does.Exist, $"File {apk} should exist"); + Assert.That (RunCommand (Config.ZipAlignPath, $"-c -v -p 4 {apk}"), Is.True, $"{ProjectAssemblyName}-Signed.apk does not contain page-aligned .so files"); + } + + [Test] + public void EnvironmentFileContents () + { + string apk = Path.Combine (testProjectPath, "bin", XABuildPaths.Configuration, $"{ProjectAssemblyName}-Signed.apk"); + Assert.That (new FileInfo (apk), Does.Exist, $"File {apk} should exist"); + + using (ZipArchive zip = ZipArchive.Open (apk, FileMode.Open)) { + Assert.That (zip, Is.Not.Null, $"{apk} couldn't be opened as a zip archive"); + Assert.That (zip.ContainsEntry ("environment"), Is.True, $"`environment` file not found in {apk}"); + + ZipEntry entry = zip.FirstOrDefault (e => String.Compare (e.FullName, "environment", StringComparison.Ordinal) == 0); + Assert.That (entry, Is.Not.Null, $"Unable to open the `environment` entry from {apk}"); + + string environment = null; + using (var ms = new MemoryStream ()) { + entry.Extract (ms); + environment = Encoding.UTF8.GetString (ms.ToArray ()); + } + + Assert.That (String.IsNullOrEmpty (environment), Is.False, $"Environment file from {apk} must not be empty"); + + string[] envLines = environment.Split (new [] { '\n' }, StringSplitOptions.RemoveEmptyEntries); + Assert.That (envLines.Length > 0, Is.True, $"Environment file from {apk} must contain at least one non-empty line"); + + bool found = false; + foreach (string line in envLines) { + string[] ev = line.Split ('='); + if (ev.Length != 2) + continue; + + if (String.Compare ("__XA_DSO_IN_APK", ev [0].Trim (), StringComparison.Ordinal) == 0) { + found = true; + break; + } + } + + Assert.That (found, Is.True, $"The `__XA_DSO_IN_APK` variable wasn't found in the environment file from {apk}"); + } + } + + [Test] + public void DSOCompressionMode () + { + string apk = Path.Combine (testProjectPath, "bin", XABuildPaths.Configuration, $"{ProjectAssemblyName}-Signed.apk"); + Assert.That (new FileInfo (apk), Does.Exist, $"File {apk} should exist"); + + var bad_dsos = new List (); + using (ZipArchive zip = ZipArchive.Open (apk, FileMode.Open)) { + Assert.That (zip, Is.Not.Null, $"{apk} couldn't be opened as a zip archive"); + + foreach (ZipEntry entry in zip) { + if (!entry.FullName.EndsWith (".so", StringComparison.Ordinal)) + continue; + + if (entry.CompressionMethod == CompressionMethod.Store) + continue; + + bad_dsos.Add (entry.FullName); + } + } + + Assert.That (bad_dsos.Count == 0, Is.True, $"Some DSO entries in {apk} are compressed ({BadDsosString ()})"); + + string BadDsosString () + { + return String.Join ("; ", bad_dsos); + } + } + + [Test] + public void AndroidManifestHasFlag () + { + const string AndroidNS = "http://schemas.android.com/apk/res/android"; + + string manifest = Path.Combine (testProjectPath, "obj", XABuildPaths.Configuration, "android", "manifest", "AndroidManifest.xml"); + Assert.That (new FileInfo (manifest), Does.Exist, $"File {manifest} should exist"); + + var doc = new XPathDocument (manifest); + XPathNavigator nav = doc.CreateNavigator (); + + var manager = new XmlNamespaceManager (nav.NameTable); + manager.AddNamespace ("android", AndroidNS); + + XPathNavigator application = nav.SelectSingleNode ("//manifest/application"); + Assert.That (application, Is.Not.Null, $"Manifest {manifest} does not contain the `application` node"); + + string attr = application.GetAttribute ("extractNativeLibs", AndroidNS)?.Trim (); + Assert.That (String.IsNullOrEmpty (attr), Is.False, $"Manifest {manifest} `application` node does not contain the `extractNativeLibs` attribute"); + Assert.That (String.Compare ("false", attr, StringComparison.OrdinalIgnoreCase), Is.EqualTo (0), $"Manifest {manifest} `application` node's `extractNativeLibs` attribute is not set to `false`"); + } + + bool RunCommand (string command, string arguments) + { + var psi = new ProcessStartInfo () { + FileName = command, + Arguments = arguments, + UseShellExecute = false, + RedirectStandardInput = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + }; + + var stderr_completed = new ManualResetEvent (false); + var stdout_completed = new ManualResetEvent (false); + + var p = new Process () { + StartInfo = psi, + }; + + p.ErrorDataReceived += (sender, e) => { + if (e.Data == null) + stderr_completed.Set (); + else + Console.WriteLine (e.Data); + }; + + p.OutputDataReceived += (sender, e) => { + if (e.Data == null) + stdout_completed.Set (); + else + Console.WriteLine (e.Data); + }; + + using (p) { + p.StartInfo = psi; + p.Start (); + p.BeginOutputReadLine (); + p.BeginErrorReadLine (); + + bool success = p.WaitForExit (60000); + + // We need to call the parameter-less WaitForExit only if any of the standard + // streams have been redirected (see + // https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit?view=netframework-4.7.2#System_Diagnostics_Process_WaitForExit) + // + p.WaitForExit (); + stderr_completed.WaitOne (TimeSpan.FromSeconds (60)); + stdout_completed.WaitOne (TimeSpan.FromSeconds (60)); + + if (!success || p.ExitCode != 0) { + Console.Error.WriteLine ($"Process `{command} {arguments}` exited with value {p.ExitCode}."); + return false; + } + + return true; + } + } + + string PrepareProject (string testName) + { + string tempRoot = Path.Combine (TestOutputDir, $"{testName}.build", XABuildPaths.Configuration); + string temporaryProjectPath = Path.Combine (tempRoot, "project"); + + var ignore = new HashSet { + Path.Combine (TestProjectRootDirectory, "bin"), + Path.Combine (TestProjectRootDirectory, "obj"), + }; + + CopyRecursively (TestProjectRootDirectory, temporaryProjectPath, ignore); + return temporaryProjectPath; + } + + void CopyRecursively (string fromDir, string toDir, HashSet ignoreDirs) + { + if (String.IsNullOrEmpty (fromDir)) + throw new ArgumentException ($"{nameof (fromDir)} is must have a non-empty value"); + if (String.IsNullOrEmpty (toDir)) + throw new ArgumentException ($"{nameof (toDir)} is must have a non-empty value"); + + if (ignoreDirs.Contains (fromDir)) + return; + + var fdi = new DirectoryInfo (fromDir); + if (!fdi.Exists) + throw new InvalidOperationException ($"Source directory '{fromDir}' does not exist"); + + if (Directory.Exists (toDir)) + Directory.Delete (toDir, true); + + foreach (FileSystemInfo fsi in fdi.EnumerateFileSystemInfos ("*", SearchOption.TopDirectoryOnly)) { + if (fsi is FileInfo finfo) + CopyFile (fsi.FullName, Path.Combine (toDir, finfo.Name)); + else + CopyRecursively (fsi.FullName, Path.Combine (toDir, fsi.Name), ignoreDirs); + } + } + + void CopyFile (string from, string to) + { + string dir = Path.GetDirectoryName (to); + if (!Directory.Exists (dir)) + Directory.CreateDirectory (dir); + File.Copy (from, to, true); + } + + LocalBuilder GetBuilder (string baseLogFileName) + { + return new LocalBuilder { + Verbosity = LoggerVerbosity.Diagnostic, + BuildLogFile = $"{baseLogFileName}.log" + }; + } + } +} diff --git a/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/Config.cs.in b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/Config.cs.in new file mode 100644 index 00000000000..a2b23e66cd3 --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/Config.cs.in @@ -0,0 +1,10 @@ +using System; +using System.IO; + +namespace EmbeddedDSOUnitTests +{ + public static class Config + { + public static readonly string ZipAlignPath = Path.Combine ("@ANDROID_SDK_DIRECTORY@", "build-tools", "@BUILD_TOOLS_FOLDER@", "zipalign@EXECUTABLE_EXTENSION@"); + } +} diff --git a/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/EmbeddedDSO-UnitTests.csproj b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/EmbeddedDSO-UnitTests.csproj new file mode 100644 index 00000000000..fef6dacb7f1 --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/EmbeddedDSO-UnitTests.csproj @@ -0,0 +1,73 @@ + + + + Debug + AnyCPU + {8B5E63B7-8C18-4BA7-BAAB-A1955B257F5E} + Library + EmbeddedDSOUnitTests + EmbeddedDSOUnitTests + v4.7 + + + true + full + false + ..\..\..\bin\TestDebug\EmbeddedDSO + DEBUG; + prompt + 4 + + + true + ..\..\..\bin\TestRelease\EmbeddedDSO + prompt + 4 + + + + + + + + + + + ..\..\..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll + + + + + + + XABuildPaths.cs + + + + + + + + + {2DD1EE75-6D8D-4653-A800-0A24367F7F38} + Xamarin.ProjectTools + + + {E248B2CA-303B-4645-ADDC-9D4459D550FD} + libZipSharp + + + + + + .exe + + + + + diff --git a/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/Properties/AssemblyInfo.cs b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..f95d796870d --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("EmbeddedDSO-UnitTests")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("Marek Habersack")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/packages.config b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/packages.config new file mode 100644 index 00000000000..d9c5868ddbf --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/run.sh b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/run.sh new file mode 100755 index 00000000000..7f928c5f9f8 --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO-UnitTests/run.sh @@ -0,0 +1,7 @@ +#!/bin/bash -e +export MONO_ENV_OPTIONS="--debug" +export USE_MSBUILD=1 +export MSBUILD=msbuild +msbuild EmbeddedDSO-UnitTests.csproj +cd ../../../ +exec mono --debug packages/NUnit.ConsoleRunner.3.7.0/tools/nunit3-console.exe bin/TestDebug/EmbeddedDSO/EmbeddedDSOUnitTests.dll diff --git a/tests/EmbeddedDSOs/EmbeddedDSO/Assets/AboutAssets.txt b/tests/EmbeddedDSOs/EmbeddedDSO/Assets/AboutAssets.txt new file mode 100644 index 00000000000..a9b0638eb1b --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO/Assets/AboutAssets.txt @@ -0,0 +1,19 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories) and given a Build Action of "AndroidAsset". + +These files will be deployed with your package and will be accessible using Android's +AssetManager, like this: + +public class ReadAsset : Activity +{ + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + InputStream input = Assets.Open ("my_asset.txt"); + } +} + +Additionally, some Android functions will automatically load asset files: + +Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); diff --git a/tests/EmbeddedDSOs/EmbeddedDSO/EmbeddedDSO.csproj b/tests/EmbeddedDSOs/EmbeddedDSO/EmbeddedDSO.csproj new file mode 100644 index 00000000000..dbcadd5f71e --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO/EmbeddedDSO.csproj @@ -0,0 +1,118 @@ + + + + Debug + AnyCPU + {056ED976-618F-4A3E-910E-AA25230C2296} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + EmbeddedDSO + Xamarin.Android.EmbeddedDSO_Test + v8.1 + True + Resources\Resource.designer.cs + Resource + Properties\AndroidManifest.xml + Resources + Assets + true + false + armeabi-v7a;x86 + + + .so + + + + ..\..\..\..\..\.. + + + + ..\..\.. + + + + + true + full + false + $(RelativeRootPath)\bin\TestDebug + bin\Debug + DEBUG; + prompt + 4 + None + + + + true + pdbonly + true + $(RelativeRootPath)\bin\TestRelease + bin\Release + prompt + 4 + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3CC4E384-4985-4D93-A34C-73F69A379FA7} + TestRunner.Core + + + {CB2335CB-0050-4020-8A05-E9614EDAA05E} + TestRunner.NUnit + + + + + + + + + + + + + + + + + $(InstallDependsOnTargets); + _GrantPermissions + + + diff --git a/tests/EmbeddedDSOs/EmbeddedDSO/EmbeddedDSO.projitems b/tests/EmbeddedDSOs/EmbeddedDSO/EmbeddedDSO.projitems new file mode 100644 index 00000000000..f8c3205813f --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO/EmbeddedDSO.projitems @@ -0,0 +1,34 @@ + + + + <_PackageName>Xamarin.Android.EmbeddedDSO_Test + + + + + Xamarin.Android.EmbeddedDSO_Test + xamarin.android.embeddeddso_test.NUnitInstrumentation + $(MSBuildThisFileDirectory)..\..\..\build-tools\scripts\TimingDefinitions.txt + $(MSBuildThisFileDirectory)..\..\..\TestResult-Xamarin.Android.EmbeddedDSO_Test-times.csv + + + + + + Xamarin.Android.EmbeddedDSO_Test + $(OutputPath)TestResult-Xamarin.Android.EmbeddedDSO_Test.nunit.xml + + + + Xamarin.Android.EmbeddedDSO_Test + + + + Xamarin.Android.EmbeddedDSO_Test + + + + Xamarin.Android.EmbeddedDSO_Test + + + diff --git a/tests/EmbeddedDSOs/EmbeddedDSO/Environment.txt b/tests/EmbeddedDSOs/EmbeddedDSO/Environment.txt new file mode 100644 index 00000000000..966b2ab27fb --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO/Environment.txt @@ -0,0 +1,5 @@ +debug.mono.debug=1 +MONO_LOG_LEVEL=debug +MONO_LOG_MASK=asm +MONO_XDEBUG=1 +__XA_DSO_IN_APK=1 diff --git a/tests/EmbeddedDSOs/EmbeddedDSO/MainActivity.cs b/tests/EmbeddedDSOs/EmbeddedDSO/MainActivity.cs new file mode 100644 index 00000000000..74538f5fa87 --- /dev/null +++ b/tests/EmbeddedDSOs/EmbeddedDSO/MainActivity.cs @@ -0,0 +1,25 @@ +using Android.App; +using Android.Widget; +using Android.OS; + +namespace EmbeddedDSO { + [Activity (Label = "EmbeddedDSO", MainLauncher = true, Icon = "@mipmap/icon")] + public class MainActivity : Activity { + int count = 1; + + protected override void OnCreate (Bundle savedInstanceState) + { + base.OnCreate (savedInstanceState); + + // Set our view from the "main" layout resource + SetContentView (Resource.Layout.Main); + + // Get our button from the layout resource, + // and attach an event to it + Button button = FindViewById