文档
打包
nsh package 把应用变成可以安装的产物:用于 iOS 模拟器的 .app、用于 iOS 真机的已签名 .ipa、用于 Android 的已签名
.apk、用于 macOS 的 ad hoc 签名 .app、用于 Linux 的目录和 .tar.gz、用于 Windows 的目录和 .zip,或用于 Web 的静态站点;--target gpui 生成本机的桌面包,只是换成 GPUI 运行器。它把应用的 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 macos --run # build and launch on this Mac
nsh package App.tsx --target linux --run # build and launch on this machine
nsh package App.tsx --target windows --run # build and launch on this machine
nsh package App.tsx --target web --run # build, serve on this machine and open in the browser
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"
}
| 键 | 类型 | 默认值 | 用途 |
|---|---|---|---|
name | string | 入口文件所在目录的名称 | 图标下方显示的名称(CFBundleDisplayName、Android 的 android:label),也用作输出文件名。 |
id | string | com.example.<name> | Bundle identifier / Android 包名。 |
version | string | "1.0.0" | 用户可见的版本号(CFBundleShortVersionString、versionName)。 |
build | number | 1 | 构建号(CFBundleVersion、versionCode)。每次上传新版本都要递增。 |
icon | string | 无 | 一张正方形 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 | macos | linux | windows | gpui | web> | 构建目标(必填)。 |
-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 设备或模拟器上;macOS、Linux、Windows 和 GPUI 应用在本机启动;Web 站点在本机提供服务并在浏览器中打开。不适用于 --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 历史记录中。
#macOS
nsh package App.tsx --target macos --run
写出 out/<Name>.app:可执行文件位于 Contents/MacOS/,nsh/bundle.nshc、nsh/assets/… 和 nsh/app.json(窗口标题和应用的外观)
位于 Contents/Resources/。应用使用 ad hoc 签名,要求 macOS 14 及以上;要分发它,请用 Developer ID 签名并进行公证。见
macOS。
#Linux
nsh package App.tsx --target linux --run
写出 out/<Name>/(可执行文件 <Name>、nsh/bundle.nshc、nsh/assets/…、包含窗口标题和应用 id 的 nsh/app.json、<id>.desktop
和图标 <id>.png),以及同样内容的 out/<Name>.tar.gz。请在 Linux 上打包:运行器是为构建它的机器构建的。运行应用的机器上必须装有
GTK 4(4.14 及以上)、Pango 和 WebKitGTK 6.0(用于 Web 视图)。如何安装应用及其桌面条目:见 Linux。
#Windows
nsh package App.tsx --target windows --run
写出 out\<Name>\(带应用图标的可执行文件 <Name>.exe、nsh\bundle.nshc、nsh\assets\…,以及包含窗口标题、应用 id、版本和外观的
nsh\app.json),以及同样内容的 out\<Name>.zip。不需要任何工具。请在 Windows 上打包:运行器是为构建它的机器构建的,而且只有
Windows 能嵌入图标。应用可以从目录被复制到的任何位置运行,不需要安装。见 Windows。
#GPUI
nsh package App.tsx --target gpui --run
写出的内容与本机上 --target macos、--target windows 或 --target linux 相同,只是可执行文件换成 GPUI 的发布版运行器
(nsh-gpui-app),而不是原生后端的。请在应用面向的操作系统上打包:运行器是为构建它的机器构建的。见 GPUI。
Linux 上的 GPUI 包动态链接 WebKitGTK 4.1(Web 视图)和 GStreamer(视频),因此运行它的机器需要安装它们,以及 GTK 3 和 GStreamer 的 base、good 插件(Debian / Ubuntu:libwebkit2gtk-4.1-0、libgstreamer1.0-0、libgstreamer-plugins-base1.0-0、gstreamer1.0-plugins-good),此外还需要 Vulkan 驱动和 libxkbcommon。
#Web
nsh package App.tsx --target web --run
写出 out/<Name>/,一个静态站点:index.html、manifest.webmanifest,以及运行时(nsh.<digest>.js、nsh.<digest>.wasm)、应用
(app.<digest>.js,即其 JS 源码)、资源和图标,每个文件都以其内容命名,托管方可以永久缓存它们。把目录部署到任意 Web 服务器或 CDN
即可;--run 在 http://127.0.0.1:4173/ 上提供服务并打开浏览器。缓存头和嵌入方式见 Web。
#图标
在 nsh.json 中("icon": "icon.png")或通过 --icon 提供一张正方形 PNG,最好是 1024×1024:
- iOS: 由 Xcode 的
actool编译进应用的资源目录(Assets.car),iOS 所需的各种尺寸都由它生成。 - Android: 缩放到 192×192,用作启动器图标(
mipmap-xxxhdpi)。 - macOS: 由
sips转换为AppIcon.icns。 - Linux: 复制为
<id>.png,.desktop文件以它作为Icon。 - Windows: 缩放为 16 到 64 像素以及 256 像素,作为图标嵌入可执行文件,窗口、资源管理器和任务栏都会显示它。
- Web: 缩放为 32 像素(favicon)、180 像素(
apple-touch-icon)以及 192 和 512 像素(Web 应用清单)。
没有图标时,系统显示默认图标。
#包里有什么
包中包含应用的字节码、capabilities.json(当应用声明了能力时)以及应用导入的资源,与预构建运行时放在一起:
| 目标 | 运行时 | 你的应用 |
|---|---|---|
| iOS | nsh-ios-app 可执行文件 | <Name>.app/nsh/bundle.nshc、nsh/assets/… |
| Android | classes.dex、lib/<abi>/libnsh.so(不压缩存储,16 KB 对齐) | assets/nsh/bundle.nshc、assets/nsh/assets/… |
| macOS | nsh-macos-app 可执行文件,重命名为 <Name> | <Name>.app/Contents/Resources/nsh/bundle.nshc、nsh/assets/… |
| Linux | nsh-linux-app 可执行文件,重命名为 <Name> | <Name>/nsh/bundle.nshc、nsh/assets/… |
| Windows | nsh-windows-app.exe 可执行文件,重命名为 <Name>.exe | <Name>\nsh\bundle.nshc、nsh\assets\… |
| GPUI | nsh-gpui-app 可执行文件,按本机操作系统的方式重命名 | 与本机操作系统相同 |
| Web | nsh.<digest>.wasm、nsh.<digest>.js | <Name>/app.<digest>.js(JS 源码)、assets/…、index.html |
打包出的应用不注册任何原生组件;如需使用原生组件,请把 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-macos-app(在 macOS 上)、nsh-linux-app(在 Linux 上)、nsh-windows-app(在 Windows 上)和 nsh-gpui-app(在三者任一上)为本机构建,位于 target/<profile>(cargo build -p nsh-macos-app、cargo build -p nsh-linux-app、cargo build -p nsh-windows-app、cargo build --manifest-path crates/gpui/Cargo.toml --target-dir target -p nsh-gpui-app);Web 运行时不论 CLI 的 profile 如何,都以 web profile 构建,位于 target/wasm32-unknown-unknown/web/。环境变量 NSH_RUNTIME 可以让 CLI 改用另一个目录,其结构与发行包的
runtime/ 相同(ios/iphoneos、ios/iphonesimulator、android/release、macos、linux、windows、gpui、web 等)。