[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/CKAndroidProject/ALLVM/main/build.cpp [Back]  [Original]

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static std::string g_script_dir;
static std::string g_build_dir;
static std::string g_ndk_dir;
static std::string g_ndk_bin;
static std::string g_acode_dir;
static std::string g_apk_output_dir;
static std::string g_zstd_dir;
static std::string g_zlib_dir;

struct TestRunOptions {
    bool custom_test = false;
    std::string project_dir;
    std::string app_build_script = "jni/Android.mk";
    std::string app_application_mk = "jni/Application.mk";
    std::string binary_name;
    std::string local_binary_path;
    std::string device_path;
    std::string run_command;
    std::string serial;
    std::string abi;
    int timeout_sec = 20;
    bool skip_ndk_build = false;
    bool clean_test = false;
};

static bool file_exists(const std::string& path) {
    return GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES;
}

static bool get_file_time(const std::string& path, FILETIME& time) {
    WIN32_FILE_ATTRIBUTE_DATA data;
    if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &data)) return false;
    time = data.ftLastWriteTime;
    return true;
}

static bool is_file_newer_or_equal(const std::string& output, const std::string& input) {
    FILETIME output_time, input_time;
    if (!get_file_time(output, output_time)) return false;
    if (!get_file_time(input, input_time)) return true;
    return CompareFileTime(&output_time, &input_time) >= 0;
}

static bool dir_exists(const std::string& path) {
    DWORD attr = GetFileAttributesA(path.c_str());
    return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
}

static std::string get_full_path_copy(const std::string& path) {
    char buf[MAX_PATH];
    DWORD len = GetFullPathNameA(path.c_str(), MAX_PATH, buf, NULL);
    if (len == 0 || len >= MAX_PATH) return path;
    return std::string(buf, len);
}

static std::string path_basename(const std::string& path) {
    size_t pos = path.find_last_of("\\/");
    return (pos == std::string::npos) ? path : path.substr(pos + 1);
}

static void init_paths() {
    char buf[MAX_PATH];
    GetModuleFileNameA(NULL, buf, MAX_PATH);
    std::string exe_path(buf);
    size_t pos = exe_path.find_last_of("\\/");
    g_script_dir = exe_path.substr(0, pos);
    g_build_dir = g_script_dir + "\\build-windows";
    g_acode_dir = g_script_dir + "\\apkUI";
    g_apk_output_dir = g_build_dir + "\\bin";
    g_zstd_dir = g_script_dir + "\\zstd";
    g_zlib_dir = g_script_dir + "\\zlib";

    // Search NDK in current directory first
    std::string local_ndk = g_script_dir + "\\android-ndk-r30-beta1-windows";
    if (dir_exists(local_ndk)) {
        g_ndk_dir = local_ndk;
    } else {
        // Then search in parent directory
        std::string p_ndk = g_script_dir + "\\..\\android-ndk-r30-beta1-windows";
        char abs_path[MAX_PATH];
        if (GetFullPathNameA(p_ndk.c_str(), MAX_PATH, abs_path, NULL)) {
            if (dir_exists(abs_path)) {
                g_ndk_dir = abs_path;
            }
        }
    }

    if (!g_ndk_dir.empty()) {
        g_ndk_bin = g_ndk_dir + "\\toolchains\\llvm\\prebuilt\\windows-x86_64\\bin";
    }
}

// ---- debug instrumentation (aarch64-ndk-crash)
static void dbg_log(const std::string& event, const std::string& detail) {
    static const std::string session = "aarch64-ndk-crash";
    std::string log_path = g_script_dir + "\\trae-debug-log-" + session + ".ndjson";
    SYSTEMTIME st;
    GetSystemTime(&st);
    char ts[64];
    sprintf(ts, "%04d-%02d-%02dT%02d:%02d:%02dZ",
            (int)st.wYear, (int)st.wMonth, (int)st.wDay,
            (int)st.wHour, (int)st.wMinute, (int)st.wSecond);
    std::ofstream ofs(log_path, std::ios::app);
    if (!ofs.is_open()) return;
    std::string esc;
    esc.reserve(detail.size() + 16);
    for (char c : detail) {
        if (c == '\\') esc += "\\\\";
        else if (c == '\"') esc += "\\\"";
        else if (c == '\n') esc += "\\n";
        else esc += c;
    }
    ofs &1 && rm -f " + probe + " >/dev/null 2>&1\"";
        std::string output;
        if (run_cmd_capture(cmd, output, g_script_dir) == 0) {
            return std::string(dir) + "/" + binary_name;
        }
    }
    return "/data/local/tmp/" + binary_name;
}

static std::string find_named_binary(const std::string& project_dir, const std::string& abi,
                                     const std::string& binary_name_or_path) {
    if (binary_name_or_path.empty()) return "";

    if (file_exists(binary_name_or_path)) {
        return get_full_path_copy(binary_name_or_path);
    }

    const std::string candidates[] = {
        project_dir + "\\libs\\" + abi + "\\" + binary_name_or_path,
        project_dir + "\\obj\\local\\" + abi + "\\" + binary_name_or_path
    };
    for (const auto& candidate : candidates) {
        if (file_exists(candidate)) return get_full_path_copy(candidate);
    }
    return "";
}

static std::string lower_ascii_copy(std::string value) {
    std::transform(value.begin(), value.end(), value.begin(),
                   [](unsigned char c) { return static_cast(std::tolower(c)); });
    return value;
}

static std::string normalize_ascii_whitespace(std::string value) {
    value = lower_ascii_copy(std::move(value));
    std::string out;
    out.reserve(value.size());
    bool last_was_space = false;
    for (unsigned char c : value) {
        if (std::isspace(c)) {
            if (!last_was_space) out.push_back(' ');
            last_was_space = true;
        } else {
            out.push_back(static_cast(c));
            last_was_space = false;
        }
    }
    return out;
}

static bool verify_frame_record_codegen() {
    std::string clangxx = g_build_dir + "\\bin\\clang++.exe";
    if (!file_exists(clangxx)) {
        printf("[Warning] clang++ not found, skipping frame record verification\n");
        return true;
    }

    std::string src = g_script_dir + "\\test\\jni\\frame_record_codegen.cpp";
    std::string asm_path = g_script_dir + "\\test\\frame_record_codegen.s";
    if (!file_exists(src)) {
        printf("[Error] Frame record probe source not found: %s\n", src.c_str());
        return false;
    }

    std::string output;
    std::string cmd = "\"" + clangxx +
                      "\" --target=aarch64-linux-android21 -S -O2 "
                      "-fno-omit-frame-pointer "
                      "-mllvm -aarch64-obfuscate-frame-record "
                      "-mllvm -aarch64-enable-ldst-opt=false "
                      "\"" + src + "\" -o \"" + asm_path + "\"";
    if (run_cmd_capture(cmd, output) != 0) {
        if (!output.empty()) printf("%s", output.c_str());
        printf("[Error] Failed to compile frame record probe\n");
        return false;
    }

    std::ifstream in(asm_path, std::ios::binary);
    if (!in.is_open()) {
        printf("[Error] Failed to read generated asm: %s\n", asm_path.c_str());
        return false;
    }
    std::ostringstream ss;
    ss  0 ? static_cast(opts.timeout_sec) * 1000U : 0U,
                                  &timed_out);
    if (!output.empty()) printf("%s", output.c_str());
    if (timed_out) {
        printf("[Error] Device run timed out after %d seconds\n", opts.timeout_sec);
        std::string base = path_basename(device_path);
        std::string kill_cmd = "\"" + adb_path + "\" -s " + serial +
                               " shell \"pkill -f '" + base + "' >/dev/null 2>&1 || true\"";
        std::string kill_output;
        run_cmd_capture(kill_cmd, kill_output, g_script_dir);
        return false;
    }
    if (ret != 0) {
        printf("[Error] Device run failed (code: %d)\n", ret);
        return false;
    }
    return true;
}

static bool ensure_fla_test_project_exists() {
    std::string jni_dir = g_script_dir + "\\test\\jni";
    const char* required[] = {"Android.mk", "Application.mk", "main.cpp"};
    for (const char* name : required) {
        std::string path = jni_dir + "\\" + name;
        if (!file_exists(path)) {
            printf("[Error] Missing handwritten test file: %s\n", path.c_str());
            return false;
        }
    }
    return true;
}

static bool build_and_run_fla_test(int jobs, const TestRunOptions& opts) {
    printf("\n[Test] Building handwritten ALLVM_TEST project...\n");
    std::string ndk_build_bat = g_ndk_dir + "\\ndk-build.bat";
    std::string ndk_build_cmd = g_ndk_dir + "\\ndk-build.cmd";
    if (g_ndk_dir.empty() || (!file_exists(ndk_build_bat) && !file_exists(ndk_build_cmd))) {
        printf("[Error] NDK not found, cannot run emulator test\n");
        return false;
    }

    std::string adb_path = find_adb();
    if (adb_path.empty()) {
        printf("[Error] adb not found in PATH / ANDROID_SDK_ROOT / ANDROID_HOME\n");
        return false;
    }

    std::string serial = opts.serial.empty() ? find_test_device(adb_path) : opts.serial;
    if (serial.empty()) {
        printf("[Error] No connected emulator/device found for adb testing\n");
        return false;
    }
    printf("  -> Using device: %s\n", serial.c_str());

    std::string abi = opts.abi.empty() ? detect_device_abi(adb_path, serial) : normalize_abi(opts.abi);
    if (abi.empty()) {
        printf("[Error] Failed to detect device ABI\n");
        return false;
    }
    printf("  -> Test ABI: %s\n", abi.c_str());

    if (!ensure_fla_test_project_exists()) {
        return false;
    }

    std::string test_dir = g_script_dir + "\\test";
    if (opts.clean_test) {
        run_cmd("if exist obj rmdir /s /q obj", test_dir);
        run_cmd("if exist libs rmdir /s /q libs", test_dir);
    }

    char jbuf[16];
    sprintf(jbuf, "%d", jobs);
    std::string ndk_build = file_exists(ndk_build_bat) ? ndk_build_bat : ndk_build_cmd;
    std::string ndk_cmd = "\"" + ndk_build + "\" NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=jni/Android.mk NDK_APPLICATION_MK=jni/Application.mk APP_ABI=" + abi + " -j" + jbuf;
    dbg_log("ndk_build_start", std::string("cwd=") + test_dir + " cmd=" + ndk_cmd);
    int ret = run_cmd(ndk_cmd, test_dir);
    dbg_log("ndk_build_end", std::string("ret=") + std::to_string(ret));
    if (ret != 0) {
        printf("[Error] ndk-build test project failed (code: %d)\n", ret);
        return false;
    }


    std::string binary = find_test_binary(test_dir, abi);
    if (binary.empty()) {
        printf("[Error] Test binary not found for ABI: %s\n", abi.c_str());
        return false;
    }

    std::string output;
    std::string remote_binary = choose_device_test_path(adb_path, serial, "allvm_test");
    std::string push_cmd = "\"" + adb_path + "\" -s " + serial + " push \"" + binary + "\" \"" + remote_binary + "\"";
    ret = run_cmd_capture(push_cmd, output);
    if (!output.empty()) printf("%s", output.c_str());
    if (ret != 0) {
        printf("[Error] adb push failed (code: %d)\n", ret);
        return false;
    }

    std::string exec_cmd = "\"" + adb_path + "\" -s " + serial + " shell \"chmod 755 " +
                           remote_binary + " && " + remote_binary + "\"";
    output.clear();
    ret = run_cmd_capture(exec_cmd, output);
    if (!output.empty()) printf("%s", output.c_str());
    if (ret != 0 || output.find("ALLVM_TEST_PASS") == std::string::npos) {
        printf("[Error] Emulator test failed\n");
        return false;
    }

    printf("[Done] Emulator ALLVM_TEST passed\n");
    return true;
}

static bool build_and_run_custom_test(const TestRunOptions& opts, int jobs) {
    printf("\n[Test] Custom build/push/run...\n");
    if (!opts.skip_ndk_build) {
        if (opts.project_dir.empty()) {
            printf("[Error] --test-project is required unless --skip-test-build is used with --test-local-binary\n");
            return false;
        }
        if (!build_ndk_test_project(opts, jobs)) return false;
    }
    if (!push_and_run_binary(opts)) return false;
    printf("[Done] Custom device test completed\n");
    return true;
}

// ========== compile_interpreter ==========
static bool compile_interpreter(const std::string& target_triple) {
    printf("\n[1/3] Compiling aVMPInterpreter (target: %s)...\n", target_triple.c_str());

    std::string interp_dir = g_script_dir + "\\aVMPInterpreter";
    std::string bc_file = interp_dir + "\\aVMPInterpreter.bc";
    std::string src_file = interp_dir + "\\aVMPInterpreter.cpp";

    std::string ndk_clang = g_ndk_bin + "\\clang.exe";
    std::string build_clang = g_build_dir + "\\bin\\clang.exe";
    std::string clang_path;

    if (file_exists(ndk_clang)) {
        clang_path = ndk_clang;
    } else if (file_exists(build_clang)) {
        clang_path = build_clang;
    } else {
        printf("[Error] clang not found\n");
        return false;
    }

    if (is_file_newer_or_equal(bc_file, src_file)) {
        printf("[Skip] aVMPInterpreter.bc is up to date\n");
        return true;
    }

    std::string cmd = "\"" + clang_path + "\" -O2 -emit-llvm -c \"" + src_file + "\" -o \"" + bc_file + "\" -target " + target_triple;

    int ret = run_cmd(cmd);
    if (ret != 0) {
        printf("[Error] Compilation failed (code: %d)\n", ret);
        return false;
    }
    printf("[Done] aVMPInterpreter compiled successfully\n");
    return true;
}

// ========== generate_vm_h ==========
static bool generate_vm_h() {
    printf("\n[2/3] Generating vm.h...\n");

    std::string bc_file = g_script_dir + "\\aVMPInterpreter\\aVMPInterpreter.bc";
    std::string vm_h = g_script_dir + "\\llvm\\include\\llvm\\Transforms\\Obfuscation\\aVMP\\vm.h";

    if (!file_exists(bc_file)) {
        printf("[Error] %s not found\n", bc_file.c_str());
        return false;
    }
    if (is_file_newer_or_equal(vm_h, bc_file)) {
        printf("[Skip] vm.h is up to date\n");
        return true;
    }

    std::ifstream in(bc_file, std::ios::binary);
    std::vector data((std::istreambuf_iterator(in)), std::istreambuf_iterator());
    in.close();

    FILE *f = fopen(vm_h.c_str(), "wb");
    if (!f) {
        printf("[Error] Cannot write to %s\n", vm_h.c_str());
        return false;
    }
    fprintf(f, "#include \n");
    fprintf(f, "#include \n\n");
    fprintf(f, "static const int binary_ir_length = %zu;\n", data.size());
    fprintf(f, "static const char binary_ir_data[] =\n");

    for (size_t i = 0; i < data.size(); i++) {
        if (i % 16 == 0) fprintf(f, "\"");
        fprintf(f, "\\x%02x", (unsigned int)(unsigned char)data[i]);
        if (i % 16 == 15) fprintf(f, "\"\n");
    }
    if (data.size() % 16 != 0) fprintf(f, "\"");
    fprintf(f, ";\n\n");

    fprintf(f, "static std::vector get_binary_ir() {\n");
    fprintf(f, "    return std::vector(binary_ir_data, binary_ir_data + binary_ir_length);\n");
    fprintf(f, "}\n");
    fclose(f);

    printf("[Done] vm.h generated (size: %zu bytes)\n", data.size());
    return true;
}

// ========== build_zstd ==========
static bool build_zstd() {
    std::string zstd_src_dir = g_zstd_dir + "\\src";
    std::string zstd_build_dir = g_zstd_dir + "\\build";
    std::string zstd_zip = g_zstd_dir + "\\zstd.zip";
    std::string zstd_url = "https://github.com/facebook/zstd/archive/refs/tags/v1.5.6.zip";

    std::string vcvars = find_vs();
    if (vcvars.empty()) {
        printf("[Error] Visual Studio not found!\n");
        return false;
    }

    if (file_exists(zstd_build_dir + "\\lib\\zstd_static.lib")) {
        printf("[Skip] zstd already compiled\n");
        return true;
    }

    printf("\n[Build] zstd...\n");

    dir_create(g_zstd_dir);

    if (!dir_exists(zstd_src_dir)) {
        printf("  -> Downloading zstd source...\n");
        std::string curl_cmd = "curl -L -o \"" + zstd_zip + "\" \"" + zstd_url + "\"";
        int ret = run_cmd(curl_cmd);
        if (ret != 0) {
            printf("[Error] Download failed (code: %d)\n", ret);
            return false;
        }

        printf("  -> Extracting zstd...\n");
        std::string expand_cmd = "powershell -Command \"Expand-Archive -Path '" + zstd_zip + "' -DestinationPath '" + g_zstd_dir + "' -Force\"";
        ret = run_cmd(expand_cmd);
        if (ret != 0) {
            printf("[Error] Extraction failed (code: %d)\n", ret);
            return false;
        }

        std::string extracted_dir = g_zstd_dir + "\\zstd-1.5.6";
        if (!dir_exists(extracted_dir)) {
            printf("[Error] Extracted directory not found\n");
            return false;
        }
        MoveFileExA(extracted_dir.c_str(), zstd_src_dir.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
        DeleteFileA(zstd_zip.c_str());
    }

    printf("  -> Compiling zstd...\n");
    dir_create(zstd_build_dir);

    std::string cmake_cmd = "cmake -G Ninja -DCMAKE_BUILD_TYPE=Release "
                            "-DZSTD_BUILD_SHARED=OFF -DZSTD_BUILD_STATIC=ON "
                            "-DZSTD_BUILD_PROGRAMS=OFF -DZSTD_BUILD_TESTS=OFF "
                            "\"" + zstd_src_dir + "\\build\\cmake\"";

    int ret = run_cmd_vcvars(vcvars, cmake_cmd, zstd_build_dir);
    if (ret != 0) {
        printf("[Error] CMake configuration failed (code: %d)\n", ret);
        return false;
    }

    std::string ninja_cmd = "ninja";
    ret = run_cmd_vcvars(vcvars, ninja_cmd, zstd_build_dir);
    if (ret != 0) {
        printf("[Error] Compilation failed (code: %d)\n", ret);
        return false;
    }

    printf("[Done] zstd compiled successfully\n");
    return true;
}

// ========== cmake_configure ==========
static bool cmake_configure(bool reconfigure) {
    printf("\n[CMake] Configuring...\n");

    std::string vcvars = find_vs();
    if (vcvars.empty()) {
        printf("[Error] Visual Studio not found!\n");
        return false;
    }

    dir_create(g_build_dir);

    std::string cmake_cache = g_build_dir + "\\CMakeCache.txt";
    if (file_exists(cmake_cache) && !reconfigure) {
        printf("[Skip] CMakeCache.txt exists, use --reconfigure to regenerate\n");
        return true;
    }
    if (file_exists(cmake_cache)) {
        DeleteFileA(cmake_cache.c_str());
    }

    std::string zstd_include_dir = g_zstd_dir + "\\src\\lib";
    std::string zstd_lib = g_zstd_dir + "\\build\\lib\\zstd_static.lib";
    std::string zlib_include_dir = g_zlib_dir;
    std::string zlib_lib;
    const char* zlib_candidates[] = {
        "\\\\build-msvc\\\\libzs.lib",
        "\\\\build-msvc\\\\zlibstatic.lib",
        "\\\\build-msvc\\\\zlib.lib",
        "\\\\build\\\\zlibstatic.lib",
        "\\\\build\\\\zlib.lib",
        "\\\\build\\\\libzs.a"
    };
    for (auto cand : zlib_candidates) {
        std::string p = g_zlib_dir + cand;
        if (file_exists(p)) { zlib_lib = p; break; }
    }
    std::replace(zlib_include_dir.begin(), zlib_include_dir.end(), '\\', '/');
    std::replace(zstd_include_dir.begin(), zstd_include_dir.end(), '\\', '/');
    std::replace(zstd_lib.begin(), zstd_lib.end(), '\\', '/');
    if (!zlib_lib.empty()) std::replace(zlib_lib.begin(), zlib_lib.end(), '\\', '/');

    std::string cmake_cmd = "cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS=/utf-8 "
                            "-DLLVM_ENABLE_RTTI=ON -DLLVM_ENABLE_EH=ON ";
    std::string cache_launcher = find_compiler_cache_launcher();
    if (!cache_launcher.empty()) {
        std::replace(cache_launcher.begin(), cache_launcher.end(), '\\', '/');
        cmake_cmd += "-DCMAKE_C_COMPILER_LAUNCHER=\"" + cache_launcher + "\" "
                     "-DCMAKE_CXX_COMPILER_LAUNCHER=\"" + cache_launcher + "\" ";
        printf("  -> Compiler cache: %s\n", cache_launcher.c_str());
    }
    cmake_cmd += 
                            "-DLLVM_ENABLE_PROJECTS=\"llvm;clang;lld\" "
                            "-DLLVM_TARGETS_TO_BUILD=\"AArch64;ARM;X86\" "
                            "-DLLVM_ENABLE_ZSTD=FORCE_ON "
                            "-DLLVM_USE_STATIC_ZSTD=ON "
                            "-Dzstd_INCLUDE_DIR=\"" + zstd_include_dir + "\" "
                            "-Dzstd_LIBRARY=\"" + zstd_lib + "\" "
                            "-Dzstd_STATIC_LIBRARY=\"" + zstd_lib + "\" ";

    if (!zlib_lib.empty() && zlib_lib.rfind(".a") == std::string::npos) {
        cmake_cmd += "-DLLVM_ENABLE_ZLIB=FORCE_ON "
                     "-DZLIB_INCLUDE_DIR=\"" + zlib_include_dir + "\" "
                     "-DZLIB_LIBRARY=\"" + zlib_lib + "\" ";
    } else {
        cmake_cmd += "-DLLVM_ENABLE_ZLIB=OFF "
                     "-DZLIB_INCLUDE_DIR=NOTFOUND "
                     "-DZLIB_LIBRARY=NOTFOUND ";
    }
    cmake_cmd += "../llvm";

    int ret = run_cmd_vcvars(vcvars, cmake_cmd, g_build_dir);
    if (ret != 0) {
        printf("[Error] CMake configuration failed (code: %d)\n", ret);
        return false;
    }

    printf("[Done] CMake configured successfully\n");
    return true;
}

// ========== build_ollvm ==========
static bool build_ollvm(const std::string& targets, int jobs) {
    printf("\n[3/3] Building OLLVM (parallel: %d)...\n", jobs);

    if (!file_exists(g_build_dir)) {
        printf("[Error] Build directory not found\n");
        return false;
    }

    std::string vcvars = find_vs();
    if (vcvars.empty()) {
        printf("[Error] Visual Studio not found!\n");
        return false;
    }

    char jbuf[16];
    sprintf(jbuf, "%d", jobs);
    std::string ninja_cmd = std::string("ninja -j") + jbuf + " " + targets;

    int ret = run_cmd_vcvars(vcvars, ninja_cmd, g_build_dir);
    if (ret != 0) {
        printf("[Error] Build failed (code: %d)\n", ret);
        return false;
    }

    printf("[Done] OLLVM built successfully\n");
    return true;
}

// ========== replace_ndk_clang ==========
static bool replace_ndk_clang() {
    printf("\n[Replace] NDK toolchain...\n");

    if (g_ndk_bin.empty() || !file_exists(g_ndk_bin)) {
        printf("[Skip] NDK not found\n");
        return true;
    }

    std::string build_bin = g_build_dir + "\\bin";

    // Replace key toolchain binaries so that new driver options are recognized.
    struct ToolReplaceEntry {
        const char* name;
        bool required;
    };
    const ToolReplaceEntry files[] = {
        { "clang.exe", true },
        { "clang++.exe", true },
        { "lld.exe", true },
        { "llvm-strip.exe", false },
        { "llvm-objcopy.exe", false }
    };

    for (const auto& entry : files) {
        std::string src = build_bin + "\\" + entry.name;
        std::string dst = g_ndk_bin + "\\" + entry.name;

        if (!file_exists(src)) {
            if (entry.required) {
                printf("[Error] required tool not found: %s\n", src.c_str());
                return false;
            }
            printf("  -> skip %s (missing in build bin)\n", entry.name);
            continue;
        }

        std::string backup = dst + ".bak";
        if (!file_exists(backup) && file_exists(dst)) {
            if (!copy_file(dst, backup, false)) return false;
        }

        if (!copy_file(src, dst)) return false;
        printf("  -> %s OK\n", entry.name);
    }

    std::string lld_src = build_bin + "\\lld.exe";
    std::string lld_dst = g_ndk_bin + "\\ld.lld.exe";
    if (file_exists(lld_src)) {
        std::string backup = lld_dst + ".bak";
        if (!file_exists(backup) && file_exists(lld_dst)) {
            if (!copy_file(lld_dst, backup, false)) return false;
        }
        if (!copy_file(lld_src, lld_dst)) return false;
        printf("  -> ld.lld.exe OK\n");
    } else {
        printf("[Error] required tool not found: %s\n", lld_src.c_str());
        return false;
    }

    // also copy required runtime DLLs from our build bin to NDK bin to avoid ABI/version mismatch
    WIN32_FIND_DATAA ffd;
    HANDLE hFind = FindFirstFileA((build_bin + "\\*.dll").c_str(), &ffd);
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            std::string name = ffd.cFileName;
            std::string src = build_bin + "\\" + name;
            std::string dst = g_ndk_bin + "\\" + name;
            if (!file_exists(src)) continue;
            std::string backup = dst + ".bak";
            if (!file_exists(backup) && file_exists(dst)) {
                if (!copy_file(dst, backup, false)) return false;
            }
            if (!copy_file(src, dst)) return false;
            printf("  -> %s OK\n", name.c_str());
        } while (FindNextFileA(hFind, &ffd));
        FindClose(hFind);
    }

    // Copy allvm-ui.exe to NDK root directory
    std::string ui_src = build_bin + "\\allvm-ui.exe";
    std::string ui_dst = g_ndk_dir + "\\allvm-ui.exe";
    if (file_exists(ui_src) && !g_ndk_dir.empty()) {
        if (!copy_file(ui_src, ui_dst)) return false;
        printf("  -> allvm-ui.exe OK\n");
    }

    printf("[Done] NDK toolchain replaced\n");
    return true;
}

// ========== build_apk ==========
static bool build_apk() {
    printf("\n[Build] APK...\n");

    if (!dir_exists(g_acode_dir)) {
        printf("[Error] Acode directory not found: %s\n", g_acode_dir.c_str());
        return false;
    }

    dir_create(g_apk_output_dir);

    std::string platforms_dir = g_acode_dir + "\\platforms";
    if (!dir_exists(platforms_dir)) {
        printf("  -> Adding Cordova Android platform...\n");
        run_cmd("npx cordova platform add android", g_acode_dir);
    }

    printf("  -> Building Web resources...\n");
    int ret = run_cmd("npm run build", g_acode_dir);
    if (ret != 0) {
        printf("[Error] Web build failed (code: %d)\n", ret);
        return false;
    }

    printf("  -> Building Android APK...\n");
    ret = run_cmd("npx cordova build android", g_acode_dir);
    if (ret != 0) {
        printf("[Error] Cordova build failed (code: %d)\n", ret);
        return false;
    }

    std::string apk_src = g_acode_dir + "\\platforms\\android\\app\\build\\outputs\\apk\\debug\\app-debug.apk";
    std::string apk_dst = g_apk_output_dir + "\\Acode-OLLVM.apk";

    if (file_exists(apk_src)) {
        copy_file(apk_src, apk_dst);
        printf("[Done] APK saved: %s\n", apk_dst.c_str());
    } else {
        printf("[Warning] APK not found\n");
    }

    return true;
}

// ========== build_apk_release ==========
static bool build_apk_release() {
    printf("\n[Build] Release APK...\n");

    if (!dir_exists(g_acode_dir)) {
        printf("[Error] Acode directory not found: %s\n", g_acode_dir.c_str());
        return false;
    }

    dir_create(g_apk_output_dir);

    printf("  -> Building Web resources...\n");
    int ret = run_cmd("npm run build", g_acode_dir);
    if (ret != 0) {
        printf("[Error] Build failed (code: %d)\n", ret);
        return false;
    }

    printf("  -> Building Release APK...\n");
    ret = run_cmd("npx cordova build android --release", g_acode_dir);
    if (ret != 0) {
        printf("[Error] Release build failed (code: %d)\n", ret);
        return false;
    }

    std::string apk_src = g_acode_dir + "\\platforms\\android\\app\\build\\outputs\\apk\\release\\app-release-unsigned.apk";
    std::string apk_dst = g_apk_output_dir + "\\Acode-OLLVM-release-unsigned.apk";

    if (file_exists(apk_src)) {
        copy_file(apk_src, apk_dst);
        printf("[Done] Release APK saved: %s\n", apk_dst.c_str());
    }

    return true;
}

// ========== main ==========
int main(int argc, char* argv[]) {
    SetConsoleOutputCP(65001);
    SetConsoleCP(65001);

    init_paths();

    printf("\n");
    printf("  +------------------------------------------+\n");
    printf("  |       OLLVM Build Script v21.x           |\n");
    printf("  +------------------------------------------+\n");

    std::string target_triple = "aarch64-linux-android";
    int jobs = 32;
    bool build_apk_flag = false;
    bool build_apk_release_flag = false;
    bool reconfigure = false;
    TestRunOptions test_opts;

    bool step_zstd        = false;
    bool step_cmake       = true;
    bool step_interpreter = true;
    bool step_vmh         = true;
    bool step_build       = true;
    bool step_test        = true;

    std::string ninja_targets = "clang lld";

    for (int i = 1; i < argc; i++) {
        std::string arg(argv[i]);
        if (arg == "--target" && i + 1 < argc) {
            target_triple = argv[++i];
        } else if (arg == "--skip-build") {
            step_cmake = step_interpreter = step_vmh = step_build = step_test = false;
        } else if (arg == "-j" && i + 1 < argc) {
            jobs = atoi(argv[++i]);
        } else if (arg == "--jobs" && i + 1 < argc) {
            jobs = atoi(argv[++i]);
        } else if (arg == "--build" && i + 1 < argc) {
            ninja_targets = argv[++i];
        } else if (arg == "--build-tools") {
            ninja_targets = "clang lld llvm-strip llvm-objcopy llvm-dis llc FileCheck";
        } else if (arg == "--reconfigure") {
            reconfigure = true;
        } else if (arg == "--skip" && i + 1 < argc) {
            std::string skip_arg(argv[++i]);
            step_zstd        = (skip_arg.find("zstd") == std::string::npos);
            step_cmake       = (skip_arg.find("cmake") == std::string::npos);
            step_interpreter = (skip_arg.find("interpreter") == std::string::npos);
            step_vmh         = (skip_arg.find("vmh") == std::string::npos);
            step_build       = (skip_arg.find("build") == std::string::npos);
            step_test        = (skip_arg.find("test") == std::string::npos);
        } else if (arg == "--only" && i + 1 < argc) {
            std::string only_arg(argv[++i]);
            step_zstd        = (only_arg == "zstd");
            step_cmake       = (only_arg == "cmake");
            step_interpreter = (only_arg == "interpreter");
            step_vmh         = (only_arg == "vmh");
            step_build       = (only_arg == "build");
            step_test        = (only_arg == "test");
        } else if (arg == "--apk") {
            build_apk_flag = true;
        } else if (arg == "--apk-release") {
            build_apk_release_flag = true;
        } else if (arg == "--all") {
            build_apk_flag = true;
        } else if (arg == "--test-project" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.project_dir = argv[++i];
        } else if (arg == "--test-build-script" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.app_build_script = argv[++i];
        } else if (arg == "--test-application-mk" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.app_application_mk = argv[++i];
        } else if (arg == "--test-binary" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.binary_name = argv[++i];
        } else if (arg == "--test-local-binary" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.local_binary_path = argv[++i];
        } else if (arg == "--test-device-path" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.device_path = argv[++i];
        } else if (arg == "--test-run-cmd" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.run_command = argv[++i];
        } else if (arg == "--test-serial" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.serial = argv[++i];
        } else if (arg == "--test-abi" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.abi = argv[++i];
        } else if (arg == "--test-timeout" && i + 1 < argc) {
            test_opts.custom_test = true;
            test_opts.timeout_sec = atoi(argv[++i]);
            if (test_opts.timeout_sec < 0) test_opts.timeout_sec = 0;
        } else if (arg == "--skip-test-build") {
            test_opts.custom_test = true;
            test_opts.skip_ndk_build = true;
        } else if (arg == "--clean-test") {
            test_opts.clean_test = true;
        }
    }

    if (step_zstd) {
        if (!build_zstd()) return 1;
    }
    if (step_cmake) {
        if (!cmake_configure(reconfigure)) return 1;
    }
    if (step_interpreter) {
        if (!compile_interpreter(target_triple)) return 1;
    }
    if (step_vmh) {
        if (!generate_vm_h()) return 1;
    }
    if (step_build) {
        if (!build_ollvm(ninja_targets, jobs)) return 1;
        if (!replace_ndk_clang()) return 1;
    }
    if (step_test) {
        if (test_opts.custom_test) {
            if (!build_and_run_custom_test(test_opts, jobs)) return 1;
        } else {
            if (!build_and_run_fla_test(jobs, test_opts)) return 1;
        }
    }

    if (build_apk_flag || build_apk_release_flag) {
        if (build_apk_release_flag) {
            if (!build_apk_release()) return 1;
        } else {
            if (!build_apk()) return 1;
        }
    }

    printf("\n");
    printf("  +------------------------------------------+\n");
    printf("  |            Build Complete!               |\n");
    printf("  +------------------------------------------+\n\n");
    return 0;
}

Web Proxy Viewer  |  New URL  |  Original Page