#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;
}