diff options
| author | Yi Jianlong <[email protected]> | 2025-12-11 22:37:03 +0800 |
|---|---|---|
| committer | Yi Jianlong <[email protected]> | 2025-12-11 22:45:10 +0800 |
| commit | 1d60c8b376198b879685e9bf8a9f199a8697a606 (patch) | |
| tree | cce4974f5f7656c974baa179485a9983f0c50222 | |
| parent | 245d472158edc782c4ee7ddb6d47be65270c5451 (diff) | |
Add rule for android c++:
- Add glue file and jni interface file to target;
- Generate android apk package at install stage;
- Use adb to run the android app.
| -rw-r--r-- | xmake/rules/android/android_install.lua | 92 | ||||
| -rw-r--r-- | xmake/rules/android/android_run.lua | 31 | ||||
| -rw-r--r-- | xmake/rules/android/xmake.lua | 54 |
3 files changed, 177 insertions, 0 deletions
diff --git a/xmake/rules/android/android_install.lua b/xmake/rules/android/android_install.lua new file mode 100644 index 000000000..5c05ba498 --- /dev/null +++ b/xmake/rules/android/android_install.lua @@ -0,0 +1,92 @@ +function main(target, android_sdk_version, android_manifest, android_res, android_assets, attachedjar, keystore, + keystore_pass, apk_output_path) + local outputpath = path.join("build", "android", "output") + if os.exists(outputpath) then + os.rmdir(outputpath) + end + local outputtemppath = path.join(outputpath, "temp") + os.mkdir(outputtemppath) + local libpath = path.join(outputpath, "lib") + os.mkdir(libpath) + local libarchpath = path.join(libpath, target:arch()) + os.mkdir(libarchpath) + os.cp(path.join(target:installdir(), "lib", "*.so"), libarchpath) + + -- rename to libmain.so + local target_filename = target:filename() + local source_lib = path.join(libarchpath, target_filename) + local dest_lib = path.join(libarchpath, "libmain.so") + if os.exists(source_lib) then + os.mv(source_lib, dest_lib) + else + cprint("${red}can't find source lib " .. source_lib) + exit(1) + end + + import("core.tool.toolchain") + + local toolchain_ndk = toolchain.load("ndk", { + plat = target:plat(), + arch = target:arch() + }) + + -- get ndk + local ndk = path.translate(assert(toolchain_ndk:config("ndk"), "cannot get NDK!")) + + -- get android sdk directory + local android_sdkdir = path.translate(assert(toolchain_ndk:config("android_sdk"), + "please run `xmake f --android_sdk=xxx` to set the android sdk directory!")) + + -- get android build-tools version + local android_build_toolver = assert(toolchain_ndk:config("build_toolver"), + "please run `xmake f --build_toolver=xxx` to set the android build-tools version!") + + local androidjar = path.join(android_sdkdir, "platforms", string.format("android-%s", android_sdk_version), + "android.jar") + + local aapt = path.join(android_sdkdir, "build-tools", android_build_toolver, + "aapt" .. (is_host("windows") and ".exe" or "")) + + --------------------------------------- pack resources + local resonly_apk = path.join(outputtemppath, "res_only.apk") + local aapt_argv = {"package", "-f", "-M", android_manifest, "-I", androidjar, "-F", resonly_apk} + + if android_res ~= nil and os.exists(android_res) then + table.insert(aapt_argv, "-S") + table.insert(aapt_argv, android_res) + end + + if android_assets ~= nil and os.exists(android_assets) and os.emptydir(android_assets) == false then + table.insert(aapt_argv, "-A") + table.insert(aapt_argv, android_assets) + end + + cprint("${green}[Android][Packing resources]${white} Save to " .. resonly_apk .. "...") + os.vrunv(aapt, aapt_argv) + + --------------------------------------- pack libs + local curdir = os.cd(outputpath) + os.vrunv(aapt, {"add", "temp/res_only.apk", "lib/arm64-v8a/libmain.so"}) + cprint("${green}[Android][Packing library]${white} Adding lib/arm64-v8a/libmain.so to res_only.apk...") + os.cd(curdir) + + --------------------------------------- align apk + local aligned_apk = path.join(outputtemppath, "unsigned.apk") + local zipalign = path.join(android_sdkdir, "build-tools", android_build_toolver, + "zipalign" .. (is_host("windows") and ".exe" or "")) + local zipalign_argv = {"-f", "4", resonly_apk, aligned_apk} + + cprint("${green}[Android][Align apk]${white} Save to " .. aligned_apk .. "...") + os.vrunv(zipalign, zipalign_argv) + + --------------------------------------- sign apk + local final_apk = path.join(apk_output_path, target:basename() .. ".apk") + local apksigner = path.join(android_sdkdir, "build-tools", android_build_toolver, + "apksigner" .. (is_host("windows") and ".bat" or "")) + local apksigner_argv = {"sign", "--ks", keystore, "--ks-pass", string.format("pass:%s", keystore_pass), "--out", + final_apk, "--in", aligned_apk} + + cprint("${green}[Android][Signing apk]${white} Save to " .. final_apk .. "...") + os.vrunv(apksigner, apksigner_argv) + +end diff --git a/xmake/rules/android/android_run.lua b/xmake/rules/android/android_run.lua new file mode 100644 index 000000000..d758ba404 --- /dev/null +++ b/xmake/rules/android/android_run.lua @@ -0,0 +1,31 @@ +function main(target, apk_output_path, package_name, activity_name) + import("core.tool.toolchain") + local toolchain_ndk = toolchain.load("ndk", {plat = target:plat(), arch = target:arch()}) + local android_sdkdir = path.translate(toolchain_ndk:config("android_sdk")) + local adb = path.join(android_sdkdir, "platform-tools", "adb" .. (is_host("windows") and ".exe" or "")) + + local outputpath = path.join("build", "android", "output") + local outputtemppath = path.join(outputpath, "temp") + assert(os.exists(outputtemppath)) + + local final_output_path = apk_output_path or outputtemppath + local final_apk = path.join(final_output_path, target:basename() .. ".apk") + assert(os.exists(final_apk)) + + local install_argv = { + "install", final_apk, + } + + cprint("{green}[Installing apk] ...") + os.vrunv(adb, install_argv) + + local run_argv = { + "shell", + "am", + "start", "-n", + package_name .. "/" .. activity_name + } + + cprint("{green}[Run apk] ...") + os.vrunv(adb, run_argv) +end diff --git a/xmake/rules/android/xmake.lua b/xmake/rules/android/xmake.lua new file mode 100644 index 000000000..d83041e25 --- /dev/null +++ b/xmake/rules/android/xmake.lua @@ -0,0 +1,54 @@ +rule("android.cpp") + if is_plat("android") then + on_load(function (target) + import("core.tool.toolchain") + local toolchain_ndk = toolchain.load("ndk", {plat = target:plat(), arch = target:arch()}) + if not toolchain_ndk then + raise("NDK toolchain not found! Please configure NDK properly.") + end + + local ndk_root = toolchain_ndk:config("ndk") + if not ndk_root then + raise("NDK path not set! Please set NDK path properly.") + end + + local native_app_glue_path = path.join(ndk_root, "sources", "android", "native_app_glue") + + -- Add glue file and jni interface file to target + local conf = target:extraconf("rules", "android.cpp") + local jni_inferface = conf.jni_interface + target:add("files", jni_inferface) + target:add("files", path.join(native_app_glue_path, "android_native_app_glue.c")) + target:add("includedirs", native_app_glue_path) + end) + + after_install(function (target) + local conf = target:extraconf("rules", "android.cpp") + local android_sdk_version = conf.android_sdk_version + local android_manifest = conf.android_manifest + local android_res = conf.android_res + local android_assets = conf.android_assets + local keystore = conf.keystore + local keystore_pass = conf.keystore_pass or "123456" + local apk_output_path = conf.apk_output_path or "." + local attachedjar = conf.attachedjar + + assert(android_sdk_version, "android sdk version not set") + assert(android_manifest, "android manifest not set") + + import("android_install")(target, android_sdk_version, android_manifest, android_res, + android_assets, attachedjar, keystore, keystore_pass, apk_output_path) + end) + + on_run(function (target) + local conf = target:extraconf("rules", "android.cpp") + local apk_output_path = conf.apk_output_path or "." + local package_name = conf.package_name + local activity_name = conf.activity_name + + assert(package_name, "package name not set") + assert(activity_name, "activity name not set") + + import("android_run")(target, apk_output_path, package_name, activity_name) + end) + end |
