Browse Source

Very very rudimentary support for `-target:linux_arm64 -subtarget:android`

gingerBill 5 tháng trước cách đây
mục cha
commit
e6718fcfcc
4 tập tin đã thay đổi với 199 bổ sung13 xóa
  1. 38 1
      src/build_settings.cpp
  2. 1 0
      src/checker.cpp
  3. 157 10
      src/linker.cpp
  4. 3 2
      src/main.cpp

+ 38 - 1
src/build_settings.cpp

@@ -171,6 +171,7 @@ struct TargetMetrics {
 enum Subtarget : u32 {
 	Subtarget_Default,
 	Subtarget_iOS,
+	Subtarget_Android,
 
 	Subtarget_COUNT,
 };
@@ -178,6 +179,7 @@ enum Subtarget : u32 {
 gb_global String subtarget_strings[Subtarget_COUNT] = {
 	str_lit(""),
 	str_lit("ios"),
+	str_lit("android"),
 };
 
 
@@ -946,6 +948,14 @@ gb_internal bool is_arch_x86(void) {
 gb_global String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
 gb_global String const NIX_SEPARATOR_STRING   = {cast(u8 *)"/",  1};
 
+gb_global String const SEPARATOR_STRING =
+#if defined(GB_SYSTEM_WINDOWS)
+	WIN32_SEPARATOR_STRING;
+#else
+	NIX_SEPARATOR_STRING;
+#endif
+
+
 gb_global String const WASM_MODULE_NAME_SEPARATOR = str_lit("..");
 
 gb_internal String internal_odin_root_dir(void);
@@ -1652,6 +1662,15 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
 		default:
 			GB_PANIC("Unknown architecture for darwin");
 		}
+	} else if (metrics->os == TargetOs_linux && subtarget == Subtarget_Android) {
+		switch (metrics->arch) {
+		case TargetArch_arm64:
+			bc->metrics.target_triplet = str_lit("aarch64-none-linux-android");
+			bc->reloc_mode = RelocMode_PIC;
+			break;
+		default:
+			GB_PANIC("Unknown architecture for darwin");
+		}
 	}
 
 	if (bc->metrics.os == TargetOs_windows) {
@@ -1749,6 +1768,22 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
 	if (bc->metrics.os == TargetOs_freestanding) {
 		bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR = !bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR;
 	}
+
+	if (subtarget == Subtarget_Android) {
+		switch (build_context.build_mode) {
+		case BuildMode_DynamicLibrary:
+			break;
+		case BuildMode_Executable:
+		case BuildMode_StaticLibrary:
+		case BuildMode_Object:
+		case BuildMode_Assembly:
+		case BuildMode_LLVM_IR:
+			gb_printf_err("Unsupported -build-mode for -target:android\n");
+			gb_printf_err("\tCurrently only supporting -build-mode:shared\n");
+			gb_exit(1);
+			break;
+		}
+	}
 }
 
 #if defined(GB_SYSTEM_WINDOWS)
@@ -1947,7 +1982,9 @@ gb_internal bool init_build_paths(String init_filename) {
 		output_extension = make_string(nullptr, 0);
 		String const single_file_extension = str_lit(".odin");
 
-		if (build_context.metrics.os == TargetOs_windows) {
+		if (selected_subtarget == Subtarget_Android) {
+			output_extension = STR_LIT("bin");
+		} 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) {
 			// Do nothing: we don't want the .bin extension

+ 1 - 0
src/checker.cpp

@@ -1149,6 +1149,7 @@ gb_internal void init_universal(void) {
 		GlobalEnumValue values[Subtarget_COUNT] = {
 			{"Default", Subtarget_Default},
 			{"iOS",     Subtarget_iOS},
+			{"Android", Subtarget_Android},
 		};
 
 		auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values));

+ 157 - 10
src/linker.cpp

@@ -130,6 +130,9 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 		return result;
 	}
 
+	bool is_cross_linking = false;
+	bool is_android = false;
+
 	if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
 #if defined(GB_SYSTEM_UNIX)
 		result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
@@ -141,12 +144,22 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 		);
 #endif
 	} else if (build_context.cross_compiling && build_context.different_os) {
-		gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
-			LIT(target_os_names[build_context.metrics.os]),
-			LIT(target_arch_names[build_context.metrics.arch])
-		);
-		build_context.keep_object_files = true;
+		switch (selected_subtarget) {
+		case Subtarget_Android:
+			is_cross_linking = true;
+			is_android = true;
+			goto try_cross_linking;
+		default:
+			gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
+				LIT(target_os_names[build_context.metrics.os]),
+				LIT(target_arch_names[build_context.metrics.arch])
+			);
+			build_context.keep_object_files = true;
+			break;
+		}
 	} else {
+try_cross_linking:;
+
 	#if defined(GB_SYSTEM_WINDOWS)
 		bool is_windows = build_context.metrics.os == TargetOs_windows;
 	#else
@@ -155,6 +168,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 
 		bool is_osx = build_context.metrics.os == TargetOs_darwin;
 
+
 		if (is_windows) {
 			String section_name = str_lit("msvc-link");
 			switch (build_context.linker_choice) {
@@ -404,6 +418,44 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 		} else {
 			timings_start_section(timings, str_lit("ld-link"));
 
+			String ODIN_ANDROID_NDK_PATH           = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_NDK_PATH", permanent_allocator())), NIX_SEPARATOR_STRING);
+			String ODIN_ANDROID_NDK_TOOLCHAIN_PATH = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_NDK_TOOLCHAIN_PATH", permanent_allocator())), NIX_SEPARATOR_STRING);
+
+			int ODIN_ANDROID_API_LEVEL = 34;
+			if (char const *found = gb_get_env("ODIN_ANDROID_API_LEVEL", permanent_allocator())) {
+				int new_level = atoi(found);
+				if (new_level >= 34) {
+					ODIN_ANDROID_API_LEVEL = new_level;
+				} else {
+					gb_printf_err("Warning: Invalid ODIN_ANDROID_API_LEVEL '%s', defaulting to %d\n", found, ODIN_ANDROID_API_LEVEL);
+				}
+			}
+
+			String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_PATH = {};
+			String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL_PATH = {};
+			String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT_PATH = {};
+
+			if (is_android) {
+				if (ODIN_ANDROID_NDK_PATH.len == 0)  {
+					gb_printf_err("Error: ODIN_ANDROID_NDK_PATH not set");
+					return 1;
+				}
+
+				if (ODIN_ANDROID_NDK_TOOLCHAIN_PATH.len == 0)  {
+					gb_printf_err("Error: ODIN_ANDROID_NDK_PATH not set");
+					return 1;
+				}
+
+				ODIN_ANDROID_NDK_TOOLCHAIN_LIB_PATH = concatenate_strings(permanent_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN_PATH, str_lit("sysroot/usr/lib/aarch64-linux-android/"));
+
+				char buf[32] = {};
+				gb_snprintf(buf, gb_size_of(buf), "%d/", ODIN_ANDROID_API_LEVEL);
+				ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL_PATH = concatenate_strings(permanent_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN_LIB_PATH, make_string_c(buf));
+
+				ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT_PATH = concatenate_strings(permanent_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN_PATH, str_lit("sysroot/"));
+			}
+
+
 			// Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
 			const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
 			if (clang_path == NULL) {
@@ -412,8 +464,11 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 
 			// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
 			//                files can be passed with -l:
-			gbString lib_str = gb_string_make(heap_allocator(), "-L/");
+			gbString lib_str = gb_string_make(heap_allocator(), "");
 			defer (gb_string_free(lib_str));
+			#if !defined(GB_SYSTEM_WINDOWS)
+				lib_str = gb_string_appendc(lib_str, "-L/ ");
+			#endif
 			
 			StringSet asm_files = {};
 			string_set_init(&asm_files, 64);
@@ -602,6 +657,74 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 
 			gbString object_files = gb_string_make(heap_allocator(), "");
 			defer (gb_string_free(object_files));
+
+
+			if (is_android) { // NOTE(bill): glue code needed for Android
+				String android_glue_object = {};
+				String android_glue_static_lib = {};
+
+				char hash_buf[64] = {};
+				gb_snprintf(hash_buf, gb_size_of(hash_buf), "%p", &hash_buf);
+				String hash = make_string_c(hash_buf);
+
+				String temp_dir = normalize_path(temporary_allocator(), temporary_directory(temporary_allocator()), NIX_SEPARATOR_STRING);
+				android_glue_object = concatenate4_strings(temporary_allocator(), temp_dir, str_lit("android_native_app_glue-"), hash, str_lit(".o"));
+				android_glue_static_lib = concatenate4_strings(permanent_allocator(), temp_dir, str_lit("libandroid_native_app_glue-"), hash, str_lit(".a"));
+
+				gbString glue = gb_string_make(heap_allocator(), clang_path);
+				defer (gb_string_free(glue));
+
+				glue = gb_string_append_fmt(glue, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
+				glue = gb_string_appendc(glue, "-c \"");
+				glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_PATH.text, ODIN_ANDROID_NDK_PATH.len);
+				glue = gb_string_appendc(glue, "sources/android/native_app_glue/android_native_app_glue.c");
+				glue = gb_string_appendc(glue, "\" ");
+				glue = gb_string_appendc(glue, "-o \"");
+				glue = gb_string_append_length(glue, android_glue_object.text, android_glue_object.len);
+				glue = gb_string_appendc(glue, "\" ");
+
+				glue = gb_string_appendc(glue, "\"-I");
+				glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN_PATH.text, ODIN_ANDROID_NDK_TOOLCHAIN_PATH.len);
+				glue = gb_string_appendc(glue, "sysroot/usr/include/");
+				glue = gb_string_appendc(glue, "\" ");
+
+				glue = gb_string_appendc(glue, "\"-I");
+				glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN_PATH.text, ODIN_ANDROID_NDK_TOOLCHAIN_PATH.len);
+				glue = gb_string_appendc(glue, "sysroot/usr/include/aarch64-linux-android/");
+				glue = gb_string_appendc(glue, "\" ");
+
+
+				glue = gb_string_appendc(glue, "-Wno-macro-redefined ");
+
+				result = system_exec_command_line_app("android-native-app-glue-compile", glue);
+				if (result) {
+					return result;
+				}
+
+				gbString ar = gb_string_make_length(heap_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN_PATH.text, ODIN_ANDROID_NDK_TOOLCHAIN_PATH.len);
+				defer (gb_string_free(ar));
+
+				ar = gb_string_appendc(ar, "bin/llvm-ar");
+
+				ar = gb_string_appendc(ar, " rcs ");
+
+				ar = gb_string_appendc(ar, "\"");
+				ar = gb_string_append_length(ar, android_glue_static_lib.text, android_glue_static_lib.len);
+				ar = gb_string_appendc(ar, "\" ");
+
+				ar = gb_string_appendc(ar, "\"");
+				ar = gb_string_append_length(ar, android_glue_object.text, android_glue_object.len);
+				ar = gb_string_appendc(ar, "\" ");
+
+				result = system_exec_command_line_app("android-native-app-glue-ar", ar);
+				if (result) {
+					return result;
+				}
+
+				object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(android_glue_static_lib));
+			}
+
+
 			for (String object_path : gen->output_object_paths) {
 				object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
 			}
@@ -654,6 +777,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 				if (build_context.metrics.os != TargetOs_openbsd
 					&& build_context.metrics.os != TargetOs_haiku
 					&& build_context.metrics.arch != TargetArch_riscv64
+					&& !is_android
 				) {
 					// OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
 					link_settings = gb_string_appendc(link_settings, "-no-pie ");
@@ -687,30 +811,53 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 				}
 			}
 
+			if (is_android) {
+				GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_LIB_PATH.len != 0);
+				GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL_PATH.len != 0);
+				GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT_PATH.len != 0);
+
+				platform_lib_str = gb_string_appendc(platform_lib_str, "\"-L");
+				platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL_PATH.text, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL_PATH.len);
+				platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
+
+				platform_lib_str = gb_string_appendc(platform_lib_str, "\"--sysroot=");
+				platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT_PATH.text, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT_PATH.len);
+				platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
+
+				link_settings = gb_string_appendc(link_settings, "-u ANativeActivity_onCreate ");
+			}
+
 			if (!build_context.no_rpath) {
 				// Set the rpath to the $ORIGIN/@loader_path (the path of the executable),
 				// so that dynamic libraries are looked for at that path.
 				if (build_context.metrics.os == TargetOs_darwin) {
 					link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,@loader_path ");
 				} else {
-					link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
+					if (is_android) {
+						// ignore
+					} else {
+						link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
+					}
 				}
 			}
 
 			if (!build_context.no_crt) {
-				platform_lib_str = gb_string_appendc(platform_lib_str, "-lm ");
+				lib_str = gb_string_appendc(lib_str, "-lm ");
 				if (build_context.metrics.os == TargetOs_darwin) {
 					// NOTE: adding this causes a warning about duplicate libraries, I think it is
 					// automatically assumed/added by clang when you don't do `-nostdlib`.
-					// platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem ");
+					// lib_str = gb_string_appendc(lib_str, "-lSystem ");
 				} else {
-					platform_lib_str = gb_string_appendc(platform_lib_str, "-lc ");
+					lib_str = gb_string_appendc(lib_str, "-lc ");
 				}
 			}
 
 			gbString link_command_line = gb_string_make(heap_allocator(), clang_path);
 			defer (gb_string_free(link_command_line));
 
+			if (is_android) {
+				link_command_line = gb_string_append_fmt(link_command_line, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
+			}
 			link_command_line = gb_string_appendc(link_command_line, " -Wno-unused-command-line-argument ");
 			link_command_line = gb_string_appendc(link_command_line, object_files);
 			link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));

+ 3 - 2
src/main.cpp

@@ -1105,8 +1105,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
 								String str = value.value_string;
 								bool found = false;
 
-								if (selected_target_metrics->metrics->os != TargetOs_darwin) {
-									gb_printf_err("-subtarget can only be used with darwin based targets at the moment\n");
+								if (selected_target_metrics->metrics->os != TargetOs_darwin &&
+								    selected_target_metrics->metrics->os != TargetOs_linux ) {
+									gb_printf_err("-subtarget can only be used with darwin and linux based targets at the moment\n");
 									bad_flags = true;
 									break;
 								}