android-ndk开发(2): macOS 安装 ndk
android-ndk开发(2): macOS 安装 ndk
2025/05/05
1. 概要
对于 android-ndk 在 r23 之前的版本,官方提供了 .zip 文件, 解压即安装。
对于 android-ndk 在 r23 以及之后的版本, 官方只提供了 .dmg 文件, 不能简单的解压完成安装。
2. ndk >= r23, macOS 怎么安装 ?
android ndk 从 r23 开始, 不再提供 macOS 平台的 .zip 安装包(如android-ndk-r22b-darwin-x86_64.zip), 只提供 .dmg 格式的安装包。链接 [1] 可以确认。这导致无法像 Windows 和 Linux 一样, 解压 .zip 就完成 ndk 的安装。
以 android-ndk-r27c-darwin.dmg 为例, 双击后显示两个文件: AndroidNDK12479018.app 和 source.properties。 双击 AndroidNDK12479018.app 后感觉完成了安装, 但在 /Application
, /Library
, ~/Application
, ~/Library
4个目录找不到安装的内容。
手动解压 AndroidNDK12479018.app,里面的 NDK 目录是工具链, CodeSign 这玩意儿暂时不知道用处,感觉就是故意使绊子。
为了一劳永逸解决 macOS 上 ndk >= r23 版本的安装, 上述过程写为 Python 脚本。基本思路是:挂载 .dmg -> 找到 .app 文件 -> 找 NDK 目录 -> 拷贝到目标目录。
import subprocess
import shutil
import os
import timedef mount_and_copy(dmg_path, target_dir):target_dir = os.path.expanduser(target_dir)try:print(f"⏳ 正在挂载 {dmg_path}...")mount_result = subprocess.run(["hdiutil", "attach", "-nobrowse", dmg_path],capture_output=True,text=True,check=True)# 解析挂载路径mount_path = Nonefor line in mount_result.stdout.splitlines():if "/Volumes" in line:mount_path = line.split("\t")[-1].strip()breakif not mount_path:raise FileNotFoundError("无法定位挂载点")time.sleep(2) # 等待文件系统稳定# 动态查找AndroidNDK*.appapp_files = [f for f in os.listdir(mount_path)if f.startswith("AndroidNDK") and f.endswith(".app")]# e.g. AndroidNDK12479018.app for android-ndk-r27cif not app_files:available = "\n".join(os.listdir(mount_path))raise FileNotFoundError(f"未找到AndroidNDK*.app文件,当前卷内容:\n{available}")app_path = os.path.join(mount_path, app_files[0])print(f" 定位到应用包:{app_path}")# 验证NDK路径ndk_source = os.path.join(app_path, "Contents", "NDK")if not os.path.exists(ndk_source):raise FileNotFoundError(f"NDK目录不存在:{ndk_source}")# 准备目标目录os.makedirs(target_dir, exist_ok=True)print(f" 目标目录:{target_dir}")# 执行复制(保留元数据)print("⚙️ 开始复制NDK文件...")shutil.copytree(ndk_source,target_dir,dirs_exist_ok=True,copy_function=shutil.copy2)# 卸载镜像print("⏳ 正在卸载...")subprocess.run(["hdiutil", "detach", mount_path], check=True)print(f" 安装完成!路径:{target_dir}")except subprocess.CalledProcessError as e:print(f"❌ 命令执行失败:{e.stderr}")if 'mount_path' in locals() and os.path.exists(mount_path):print(f"⚠️ 尝试手动卸载:hdiutil detach {mount_path}")except Exception as e:print(f"❌ 错误:{str(e)}")if __name__ == "__main__":dmg_path = os.path.join(os.getcwd(), "android-ndk-r27c-darwin.dmg")mount_and_copy(dmg_path, "/Users/zz/soft/toolchains/android-ndk-r27c")
References
- https://github.com/android/ndk/wiki/Unsupported-Downloads