指南

打包

nsh package 把应用变成可以安装的产物:用于 iOS 模拟器的 .app、用于 iOS 真机的已签名 .ipa,或用于 Android 的已签名 .apk。它把应用的 release 构建与 nsh 自带的预构建运行时组合在一起,因此不需要 Rust 工具链,不需要 Xcode 项目,也不需要 Gradle。

nsh package App.tsx --target ios-simulator --run     # build, install and launch in the simulator
nsh package App.tsx --target android --run           # build, install and launch on a device or emulator
nsh package App.tsx --target ios --sign "Apple Development: Jane Doe (ABCDE12345)" --profile Dev.mobileprovision

应用以 release 模式构建(压缩并编译为 QuickJS 字节码),与 nsh build --release 的构建结果完全相同。除非传入 --out,否则输出到 out/。

#应用元数据:nsh.json

nsh package 读取入口文件同目录下的 nsh.json。nsh init 会生成一个:

{
  "name": "Gallery",
  "id": "com.example.gallery",
  "version": "1.0.0",
  "build": 1,
  "icon": "icon.png"
}
键类型默认值用途
namestring入口文件所在目录的名称图标下方显示的名称(CFBundleDisplayName、Android 的 android:label),也用作输出文件名。
idstringcom.example.<name>Bundle identifier / Android 包名。
versionstring"1.0.0"用户可见的版本号(CFBundleShortVersionString、versionName)。
buildnumber1构建号(CFBundleVersion、versionCode)。每次上传新版本都要递增。
iconstring无一张正方形 PNG,最好是 1024×1024,路径相对于 nsh.json。

nsh.json 还可以包含 tailwind(见 Tailwind)和 capabilities(见沙箱与能力);它们由打包器读取。未知的键会导致报错。

每个键都可以在命令行上覆盖,这在 CI 中很方便:

nsh package App.tsx --target android --app-version 1.4.0 --build 42

#选项

参数说明
--target <ios | ios-simulator | android>构建目标(必填)。
-o, --out <dir>输出目录。默认为 out。
--name <name>显示名称。
--id <id>Bundle identifier,如 com.example.app。
--app-version <version>版本号,如 1.2.0。
--build <n>构建号。
--icon <png>应用图标:一张正方形 PNG,最好是 1024×1024。
--sign <identity>iOS:用于真机的代码签名身份。
--profile <file>iOS:用于真机的描述文件(.mobileprovision)。
--keystore <file>Android:release keystore(默认使用 debug 密钥)。
--key-alias <alias>Android:keystore 中的密钥别名。
--keystore-password <password>Android:keystore 密码;也可从 NSH_KEYSTORE_PASSWORD 读取。
--run构建完成后安装并启动:在 iOS 模拟器中,或在 adb 能看到的 Android 设备或模拟器上。不适用于 --target ios。

#iOS 模拟器

nsh package App.tsx --target ios-simulator --run

写出 out/<Name>.app,使用 ad hoc 签名(模拟器需要签名,但不需要签名身份)。加上 --run 时,如果没有正在运行的模拟器,它会先启动一个,再安装并启动应用。需要安装 Xcode。

#iOS 真机

nsh package App.tsx --target ios \
  --sign "Apple Distribution: Example Inc (ABCDE12345)" \
  --profile AppStore.mobileprovision

写出 out/<Name>.ipa,可用于 TestFlight、App Store 上传工具或直接安装(取决于描述文件)。具体过程如下:

  • 描述文件以 embedded.mobileprovision 的名称复制进应用。
  • entitlements 取自描述文件。nsh 会检查描述文件的 application identifier 是否覆盖应用的 id(TEAMID.* 或 TEAMID.com.example.* 这样的通配描述文件也可以),不覆盖则报错退出。
  • 用 codesign 以指定身份签名应用,再压缩成 .ipa(Payload/<Name>.app)。

查看可用的签名身份:

security find-identity -v -p codesigning

如果没有传 --sign 和 --profile,nsh 会写出一个未签名的 out/<Name>.app 并给出提示;它无法直接安装到真机上。

应用支持 iPhone 和 iPad,支持竖屏和横屏(倒置竖屏仅限 iPad),要求 iOS 15.0 或更高版本。

#Android

nsh package App.tsx --target android --run

写出 out/<Name>.apk。APK 直接用 SDK 的构建工具(aapt2、zipalign、apksigner)组装,不经过 Gradle。要求:

  • Android SDK,通过 ANDROID_HOME(或 ANDROID_SDK_ROOT,或 ~/Library/Android/sdk)查找,需要安装 build tools 和 platforms;android-35 包。缺少时 nsh 会告诉你对应的 sdkmanager 命令。
  • 一个 JDK,用于首次创建 debug keystore。

应用的目标 API 为 35,要求 API 28(Android 9)或更高版本;包含适用于 arm64-v8a 和 x86_64 的原生代码。

#发布签名

不传 --keystore 时,APK 使用标准的 debug 密钥(~/.android/debug.keystore,不存在时自动创建)签名,nsh 会警告它不能用于应用商店发布。正式发布时,先创建一次 keystore,然后传入:

keytool -genkeypair -v -keystore release.jks -alias upload -keyalg RSA -keysize 2048 -validity 10000

NSH_KEYSTORE_PASSWORD=… nsh package App.tsx --target android \
  --keystore release.jks --key-alias upload

建议使用环境变量而不是 --keystore-password,以免密码留在 shell 历史记录中。

#图标

在 nsh.json 中("icon": "icon.png")或通过 --icon 提供一张正方形 PNG,最好是 1024×1024:

  • iOS: 由 Xcode 的 actool 编译进应用的资源目录(Assets.car),iOS 所需的各种尺寸都由它生成。
  • Android: 缩放到 192×192,用作启动器图标(mipmap-xxxhdpi)。

没有图标时,系统显示默认图标。

#包里有什么

包中包含应用的字节码、capabilities.json(当应用声明了能力时)以及应用导入的资源,与预构建运行时放在一起:

目标运行时你的应用
iOSnsh-ios-app 可执行文件<Name>.app/nsh/bundle.qbc、nsh/assets/…
Androidclasses.dex、lib/<abi>/libnsh.so(不压缩存储,16 KB 对齐)assets/nsh/bundle.qbc、assets/nsh/assets/…

打包出的应用不注册任何原生组件;如需使用原生组件,请把 nsh 嵌入你自己的应用。

#查找运行时

nsh package 会在 CLI 旁边查找预构建运行时(发行包中的 bin/../runtime)。在 nsh 的源码仓库中,它使用 target/<triple>/<profile> 和 target/android/<profile>,profile 与 CLI 自身一致(debug 版 CLI 使用 debug 版运行时):用 cargo build -p nsh-ios-app --target aarch64-apple-ios-sim(真机用 aarch64-apple-ios)和 cargo xtask android 构建它们(release 版 CLI 需加上 --release)。环境变量 NSH_RUNTIME 可以让 CLI 改用另一个目录,其结构与发行包的 runtime/ 相同(ios/iphoneos、ios/iphonesimulator、android/release 等)。

#另请参阅