summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYi Jianlong <[email protected]>2025-12-11 22:37:03 +0800
committerYi Jianlong <[email protected]>2025-12-11 22:45:10 +0800
commit1d60c8b376198b879685e9bf8a9f199a8697a606 (patch)
treecce4974f5f7656c974baa179485a9983f0c50222
parent245d472158edc782c4ee7ddb6d47be65270c5451 (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.lua92
-rw-r--r--xmake/rules/android/android_run.lua31
-rw-r--r--xmake/rules/android/xmake.lua54
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