#import "Basic"; #import "Compiler"; #import "File"; #import "File_Utilities"; #import "String"; #import "System"; #import "Process"; OUTPUT_EXECUTABLE_NAME_DESKTOP :: "mexplore"; SOURCE_ENTRY_POINT :: "src/main.jai"; DESKTOP_OS_BUILD_OUTPUT_PATH :: "bin"; #run Build_Project(); Build_Project :: () { // Get start time: // Compile prerequisites: set_build_options_dc(.{do_output=false}); args := get_build_options().compile_time_command_line; // Simplify targeting / command line interface? // [For now] assume x64 for Windows and Linux // Android defaults to ARM64, can be overriden with `x64`. `x64` is ignored on all other OS' target_windows = array_find(args, "windows"); target_linux = array_find(args, "linux"); target_macos = array_find(args, "macos"); target_x64 = array_find(args, "x64"); target_optimize = array_find(args, "release"); // NOTE(Charles): Proably makes sense to default os/cpu to OS/CPU then arguments override that. if target_windows { cpu_target = .X64; os_target = .WINDOWS; assert(os_target == OS); } else if target_linux { cpu_target = .X64; os_target = .LINUX; assert(os_target == OS); } else if target_macos { cpu_target = .ARM64; os_target = .MACOS; assert(os_target == OS); } else { assert(false, "Not a valid build configuration."); } set_working_directory(#filepath); compile_native_code(); // Also copies assets and libraries :ThisAintCompilingNativeCode! print("\n"); } compile_native_code :: () { VERSION :: "0.1"; JAI_VERSION := trim(compiler_get_version_info(null)); current_time := to_calendar(current_time_consensus(), .LOCAL); RELEASE_DATE := calendar_to_string(current_time); result, branch_name := run_command("git", "branch", "--show-current", capture_and_return_output = true); GIT_BRANCH := trim(branch_name); result=, revision := run_command("git", "rev-parse", "HEAD", capture_and_return_output = true); GIT_REVISION := trim(revision); log("Compiling for % %", os_target, cpu_target); start_time := GetUnixTimestamp(); defer log("Native code compilation time: %", GetUnixTimestamp()-start_time); // NOTE(Charles): The compiler already outputs this for us? build_output_directory: string; sdk_lib_path: string; android_main_directory: string; #if OS != .MACOS { target_triple, target_triple_with_sdk := get_android_target_triple(cpu_target); if os_target == .ANDROID { ndk := get_ndk_paths(); sdk_lib_path = tprint("%/usr/lib/%", ndk.sysroot, target_triple); // Create the library directory android_main_directory = tprint("%/app/src/main", ANDROID_PROJECT_PATH); assert(file_exists(android_main_directory)); build_output_directory = tprint("%/jniLibs/%", android_main_directory, get_android_project_jni_architecture_name(cpu_target)); make_directory_if_it_does_not_exist(build_output_directory, recursive = true); } } w := compiler_create_workspace("Target workspace"); options := get_build_options(w); copy_commonly_propagated_fields(get_build_options(), *options); options.cpu_target = cpu_target; options.os_target = os_target; if os_target == .WINDOWS || os_target == .LINUX || OS == .MACOS { if target_optimize { set_optimization(*options, .VERY_OPTIMIZED, preserve_debug_info=false); options.llvm_options.enable_split_modules = false; options.array_bounds_check = .OFF; options.null_pointer_check = .OFF; options.arithmetic_overflow_check = .OFF; options.cast_bounds_check = .OFF; options.output_executable_name = OUTPUT_EXECUTABLE_NAME_DESKTOP; } else { options.output_executable_name = tprint("%-debug", OUTPUT_EXECUTABLE_NAME_DESKTOP); } options.output_path = DESKTOP_OS_BUILD_OUTPUT_PATH; make_directory_if_it_does_not_exist(options.output_path, recursive=true); } import_path: [..] string; array_add(*import_path, "modules"); array_add(*import_path, ..options.import_path); options.import_path = import_path; if cpu_target == .ARM64 { options.backend = .LLVM; // Disable +lse for older ARM64 devices. // options.llvm_options.target_system_features = "+lse"; } set_build_options(options, w); compiler_begin_intercept(w); add_build_file(SOURCE_ENTRY_POINT, w); build_constants := tprint(#string STRING VERSION :: "%"; JAI_VERSION :: "%"; RELEASE_DATE :: "%"; GIT_BRANCH :: "%"; GIT_REVISION :: "%"; DEBUG :: %; STRING, VERSION, JAI_VERSION, RELEASE_DATE, GIT_BRANCH, GIT_REVISION, ifx target_optimize then "false" else "true", ); add_build_string(build_constants, w); while true { message := compiler_wait_for_message(); if message.kind == { case .PHASE; phase_message := cast(*Message_Phase) message; if phase_message.phase == .READY_FOR_CUSTOM_LINK_COMMAND { #if OS != .MACOS { run_android_link_command(phase_message, options, extra_args = .[ tprint("-L%", sdk_lib_path), "-lc++"] // :AndroidStbImageThreadLocals ); } } case .ERROR; print("\n"); // Seems to be some issue with exit() not flushing output, we miss errors! exit(1); case .COMPLETE; break; } } compiler_end_intercept(w); // :ThisAintCompilingNativeCode! ASSETS :: string.[ "extras/images/image_test.jpg", "extras/fonts/JetBrainsMono-Regular.ttf", "extras/fonts/RobotoMono-Regular.ttf", ]; if os_target == .WINDOWS { WINDOWS_LIBS :: string.[]; WINDOWS_LIBS_DEBUG :: string.[]; target_output_directory := tprint("%/bin", #filepath); make_directory_if_it_does_not_exist(options.output_path, recursive=true); for ASSETS { if !copy_file(it, tprint("%/%", target_output_directory, path_filename(it))) { compiler_report(tprint("Failed to copy file % to android project assets", it)); } } target_lib_array := ifx target_optimize then WINDOWS_LIBS else WINDOWS_LIBS_DEBUG; for target_lib_array { if !copy_file(it, tprint("%/%", target_output_directory, path_filename(it))) { compiler_report(tprint("Failed to copy file % to android project assets", it)); } } } if os_target == .LINUX { LINUX_LIBS :: string.[]; target_output_directory := tprint("%/bin", #filepath); make_directory_if_it_does_not_exist(options.output_path, recursive=true); for ASSETS { if !copy_file(it, tprint("%/%", target_output_directory, path_filename(it))) { compiler_report(tprint("Failed to copy file % to android project assets", it)); } } for LINUX_LIBS { if !copy_file(it, tprint("%/%", target_output_directory, path_filename(it))) { compiler_report(tprint("Failed to copy file % to android project assets", it)); } } } if os_target == .ANDROID { resources_output_directory := tprint("%/assets", android_main_directory); make_directory_if_it_does_not_exist(resources_output_directory, recursive = true); for ASSETS { if !copy_file(it, tprint("%/%", resources_output_directory, path_filename(it))) { compiler_report(tprint("Failed to copy file % to android project assets", it)); } } #if OS != .MACOS { // Package libc++ :AndroidStbImageThreadLocals success := copy_android_libcpp(cpu_target, options.output_path); if !success { compiler_report(tprint("Could not copy Android libc++ to %", options.output_path)); } } } } #if OS != .MACOS { generate_android_project :: () { GENERATE_ANDROID_FILES :: false; if file_exists(ANDROID_PROJECT_PATH) && !GENERATE_ANDROID_FILES { log("Project folder at %", ANDROID_PROJECT_PATH); return; } log("Generating new project folder at %", ANDROID_PROJECT_PATH); start_time := GetUnixTimestamp(); config: Android_Project_Config; config.project_path = ANDROID_PROJECT_PATH; config.app_name = ANDROID_APP_NAME; config.app_id = ANDROID_APP_ID; config.android_gradle_plugin_version = "8.2.2"; config.gradle_version = "8.6"; config.lib_name = OUTPUT_EXECUTABLE_NAME_ANDROID; // Hmmm. I wanna do portrait for phones and landscape for tablets, or allow the user to choose via device orientation. // For now just roll with this. config.screen_orientation = .PORTRAIT; // config.screen_orientation = .LANDSCAPE; if !generate_android_project(*config) { compiler_report(tprint("Failed to create android project \"%\"", config.project_path)); return; } log("Succesfully created or updated android project \"%\".", config.project_path); log("Generate Android project time: %", GetUnixTimestamp()-start_time); // NOTE(Charles): Android Icons! // // The apps icon is set with the icon attribute of application tag in manifest. This has to be set to a reference // to a "drawable resource". This most direct way to do this is to put a png in the drawable directory, eg // AndroidProject/app/src/main/res/drawable/my_icon.png. The attribute can then be set to // android:icon="@drawable/my_icon". // // However, on my phone at least, this looks total ass. The image gets massively shrunk down and the rest of the // icon is a white circle. Apparently this is because now android expects you to define "adaptive icons". Instead // you now have to provide both a foreground and background image, and define your "image" in xml. So we now have // two images drawable/icon_foreground.png, drawable/icon_background.png and the magic link file mipmap/icon.xml. // // This now looks ok on my phone, but I fully expect on other devices it might look weird and we will have to // figure some more android configuration junk out. If we manage to figure out enough of the junk then I could // maybe write some helper that generates all the required crap given some simpler inputs, for now it's all manually // defined. // // Adaptive icons docs: https://developer.android.com/develop/ui/views/launch/icon_design_adaptive // "Alternative resources" docs: https://developer.android.com/guide/topics/resources/providing-resources#AlternativeResources } } target_windows : bool; target_linux : bool; target_macos : bool; target_android : bool; target_optimize : bool; target_x64 : bool; cpu_target: CPU_Tag; os_target: Operating_System_Tag; android_make_apk := false; android_install := false; GetUnixTimestamp :: () -> float64 { #if OS == .WINDOWS { #import "Windows"; FILETIME_TO_UNIX :: 116444736000000000; ft: FILETIME; GetSystemTimePreciseAsFileTime(*ft); t: s64 = ((cast(s64)ft.dwHighDateTime) << 32) | (cast(s64)ft.dwLowDateTime); return (cast(float64)(t - FILETIME_TO_UNIX)) / (10.0 * 1000.0 * 1000.0); } #if OS == .LINUX || OS == .MACOS { #import "POSIX"; TIMESPEC_TO_SEC_DIV :: 1000000000.0; at := current_time_consensus(); ts := to_timespec(at); return cast(float64)(ts.tv_sec) + cast(float64)(ts.tv_nsec) / TIMESPEC_TO_SEC_DIV; } }