From 45ecafd7b1e4f6fd0a5f29ccfefcb9250bb91486 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 26 Mar 2025 17:33:10 +0000 Subject: [PATCH] Really bodgy android packing system for `odin build` --- src/build_settings.cpp | 36 +++++++++- src/linker.cpp | 148 ++++++++++++++++++++++++++++++++++++++++- src/main.cpp | 22 ++++++ 3 files changed, 202 insertions(+), 4 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index eff6cef2e..bb7cb7208 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -543,8 +543,9 @@ struct BuildContext { String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL; String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT; - String ANDROID_JAR_SIGNER; + String ODIN_ANDROID_JAR_SIGNER; String android_keystore; + String android_keystore_alias; String android_manifest; }; @@ -1816,6 +1817,32 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB, make_string_c(buf)); bc->ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, str_lit("sysroot/")); + + + bc->ODIN_ANDROID_JAR_SIGNER = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_JAR_SIGNER", permanent_allocator())), NIX_SEPARATOR_STRING); + if (bc->build_mode == BuildMode_Executable) { + if (bc->ODIN_ANDROID_SDK.len == 0) { + gb_printf_err("Error: ODIN_ANDROID_SDK not set, which is required for -build-mode:executable for -subtarget:android"); + gb_exit(1); + } + if (bc->ODIN_ANDROID_JAR_SIGNER.len == 0) { + gb_printf_err("Error: ODIN_ANDROID_JAR_SIGNER not set, which is required for -build-mode:executable for -subtarget:android"); + gb_exit(1); + } + if (bc->android_keystore.len == 0) { + gb_printf_err("Error: -android-keystore: has not been set\n"); + gb_exit(1); + } + if (bc->android_keystore_alias.len == 0) { + gb_printf_err("Error: -android-keystore_alias: has not been set\n"); + gb_exit(1); + } + if (bc->android_manifest.len == 0) { + gb_printf_err("Error: -android-manifest: has not been set\n"); + gb_exit(1); + } + } + } if (!bc->custom_optimization_level) { @@ -1862,16 +1889,18 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta if (subtarget == Subtarget_Android) { switch (build_context.build_mode) { + case BuildMode_Executable: case BuildMode_DynamicLibrary: case BuildMode_Object: case BuildMode_Assembly: case BuildMode_LLVM_IR: break; - case BuildMode_Executable: + default: case BuildMode_StaticLibrary: if ((build_context.command_kind & Command__does_build) != 0) { gb_printf_err("Unsupported -build-mode for -subtarget:android\n"); gb_printf_err("\tCurrently only supporting: \n"); + gb_printf_err("\t\texe\n"); gb_printf_err("\t\tshared\n"); gb_printf_err("\t\tobject\n"); gb_printf_err("\t\tassembly\n"); @@ -2080,7 +2109,8 @@ gb_internal bool init_build_paths(String init_filename) { String const single_file_extension = str_lit(".odin"); if (selected_subtarget == Subtarget_Android) { - output_extension = STR_LIT("bin"); + // NOTE(bill): It's always shared! + output_extension = STR_LIT("so"); } else if (build_context.metrics.os == TargetOs_windows) { output_extension = STR_LIT("exe"); } else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { diff --git a/src/linker.cpp b/src/linker.cpp index 29dc5afc6..7c1b045b6 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -742,7 +742,9 @@ try_cross_linking:; link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' "); link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' "); } - + } else if (is_android) { + // Always shared even in android! + link_settings = gb_string_appendc(link_settings, "-shared "); } if (build_context.build_mode == BuildMode_Executable && build_context.reloc_mode == RelocMode_PIC) { @@ -866,6 +868,150 @@ try_cross_linking:; return result; } } + if (is_android && build_context.build_mode == BuildMode_Executable) { + String android_sdk_build_tools = concatenate3_strings(temporary_allocator(), + build_context.ODIN_ANDROID_SDK, str_lit("build-tools"), NIX_SEPARATOR_STRING); + + Array list = {}; + ReadDirectoryError rd_err = read_directory(android_sdk_build_tools, &list); + defer (array_free(&list)); + + switch (rd_err) { + case ReadDirectory_InvalidPath: + gb_printf_err("Invalid path: %.*s\n", LIT(android_sdk_build_tools)); + return 1; + case ReadDirectory_NotExists: + gb_printf_err("Path does not exist: %.*s\n", LIT(android_sdk_build_tools)); + return 1; + case ReadDirectory_Permission: + gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(android_sdk_build_tools)); + return 1; + case ReadDirectory_NotDir: + gb_printf_err("Expected a directory for a package, got a file: %.*s\n", LIT(android_sdk_build_tools)); + return 1; + case ReadDirectory_Empty: + gb_printf_err("Empty directory: %.*s\n", LIT(android_sdk_build_tools)); + return 1; + case ReadDirectory_Unknown: + gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(android_sdk_build_tools)); + return 1; + } + + auto possible_valid_dirs = array_make(heap_allocator(), 0, list.count); + defer (array_free(&possible_valid_dirs)); + + + for (FileInfo fi : list) if (fi.is_dir) { + bool all_numbers = true; + for (isize i = 0; i < fi.name.len; i++) { + u8 c = fi.name[i]; + if ('0' <= c && c <= '9') { + // true + } else if (i == 0) { + all_numbers = false; + } else if (c == '.') { + break; + } else { + all_numbers = false; + } + } + + if (all_numbers) { + array_add(&possible_valid_dirs, fi); + } + } + + if (possible_valid_dirs.count == 0) { + gb_printf_err("Unable to find any Android SDK/API Level in %.*s\n", LIT(android_sdk_build_tools)); + return 1; + } + + int *dir_numbers = gb_alloc_array(temporary_allocator(), int, possible_valid_dirs.count); + + char buf[1024] = {}; + for_array(i, possible_valid_dirs) { + FileInfo fi = possible_valid_dirs[i]; + isize n = gb_min(gb_size_of(buf)-1, fi.name.len); + memcpy(buf, fi.name.text, n); + buf[n] = 0; + + dir_numbers[i] = atoi(buf); + } + + isize closest_number_idx = -1; + for (isize i = 0; i < possible_valid_dirs.count; i++) { + if (dir_numbers[i] >= ODIN_ANDROID_API_LEVEL) { + if (closest_number_idx < 0) { + closest_number_idx = i; + } else if (dir_numbers[i] < dir_numbers[closest_number_idx]) { + closest_number_idx = i; + } + } + } + + if (closest_number_idx < 0) { + gb_printf_err("Unable to find any Android SDK/API Level in %.*s meeting the minimum API level of %d\n", LIT(android_sdk_build_tools), ODIN_ANDROID_API_LEVEL); + return 1; + } + + String api_number = possible_valid_dirs[closest_number_idx].name; + + android_sdk_build_tools = concatenate_strings(temporary_allocator(), android_sdk_build_tools, api_number); + String android_sdk_platforms = concatenate_strings(temporary_allocator(), + build_context.ODIN_ANDROID_SDK, + make_string_c(gb_bprintf("platforms/android-%d/", dir_numbers[closest_number_idx])) + ); + + + + android_sdk_build_tools = normalize_path(temporary_allocator(), android_sdk_build_tools, NIX_SEPARATOR_STRING); + android_sdk_platforms = normalize_path(temporary_allocator(), android_sdk_platforms, NIX_SEPARATOR_STRING); + + gbString cmd = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(cmd)); + + TIME_SECTION("Android aapt"); + + cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len); + cmd = gb_string_appendc(cmd, "aapt"); + cmd = gb_string_appendc(cmd, " package -f"); + cmd = gb_string_append_fmt(cmd, " -M \"%.*s\"", LIT(build_context.android_manifest)); + cmd = gb_string_append_fmt(cmd, " -I \"%.*sandroid.jar\"", LIT(android_sdk_platforms)); + cmd = gb_string_append_fmt(cmd, " -F \"%.*s.apk-build\"", LIT(output_filename)); + + result = system_exec_command_line_app("android-aapt", cmd); + if (result) { + return result; + } + + TIME_SECTION("Android jarsigner"); + gb_string_clear(cmd); + + cmd = gb_string_append_length(cmd, build_context.ODIN_ANDROID_JAR_SIGNER.text, build_context.ODIN_ANDROID_JAR_SIGNER.len); + cmd = gb_string_append_fmt(cmd, " -storepass android -keystore \"%.*s\" \"%.*s.apk-build\" \"%.*s\"", + LIT(build_context.android_keystore), + LIT(output_filename), + LIT(build_context.android_keystore_alias) + ); + result = system_exec_command_line_app("android-jarsigner", cmd); + if (result) { + return result; + } + + TIME_SECTION("Android zipalign"); + gb_string_clear(cmd); + + cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len); + cmd = gb_string_appendc(cmd, "zipalign"); + cmd = gb_string_appendc(cmd, " -f 4"); + cmd = gb_string_append_fmt(cmd, " \"%.*s.apk-build\" \"%.*s.apk\"", LIT(output_filename), LIT(output_filename)); + + + result = system_exec_command_line_app("android-zipalign", cmd); + if (result) { + return result; + } + } } } diff --git a/src/main.cpp b/src/main.cpp index 3549eb277..22121db73 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -408,6 +408,10 @@ enum BuildFlagKind { BuildFlag_Subsystem, #endif + BuildFlag_AndroidKeystore, + BuildFlag_AndroidKeystoreAlias, + BuildFlag_AndroidManifest, + BuildFlag_COUNT, }; @@ -624,6 +628,10 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build); #endif + add_flag(&build_flags, BuildFlag_AndroidKeystore, str_lit("android-keystore"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_AndroidKeystoreAlias, str_lit("android-keystore-alias"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_AndroidManifest, str_lit("android-manifest"), BuildFlagParam_String, Command__does_build); + GB_ASSERT(args.count >= 3); Array flag_args = array_slice(args, 3, args.count); @@ -1638,6 +1646,20 @@ gb_internal bool parse_build_flags(Array args) { } #endif + case BuildFlag_AndroidKeystore: + GB_ASSERT(value.kind == ExactValue_String); + build_context.android_keystore = value.value_string; + break; + + case BuildFlag_AndroidKeystoreAlias: + GB_ASSERT(value.kind == ExactValue_String); + build_context.android_keystore_alias = value.value_string; + break; + + case BuildFlag_AndroidManifest: + GB_ASSERT(value.kind == ExactValue_String); + build_context.android_manifest = value.value_string; + break; } }