---
url: https://nsh.dev/zh-CN/docs/packaging
description: "用 nsh package 为 iOS 真机、iOS 模拟器、Android、macOS、Windows、Linux 和 Web 构建可安装的应用，包括应用元数据、图标和签名。"
---

# 打包

`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。

```sh
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/`。

> [!NOTE]
> 如果想把 NSH 放*进*现有的原生应用，请参阅[平台](https://nsh.dev/zh-CN/docs/platforms.md#嵌入原生应用)。如果想发布由应用下载并验证的更新，请参阅[签名包](https://nsh.dev/zh-CN/docs/signed-packages.md)。

## 应用元数据：`nsh.json`

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

```json
{
  "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](https://nsh.dev/zh-CN/docs/tailwind.md)）和 `capabilities`（见[沙箱与能力](https://nsh.dev/zh-CN/docs/sandbox-and-capabilities.md)）；它们由打包器读取。未知的键会导致报错。

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

```sh
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 模拟器

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

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

## iOS 真机

```sh
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`）。

查看可用的签名身份：

```sh
security find-identity -v -p codesigning
```

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

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

## Android

```sh
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，然后传入：

```sh
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

```sh
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](https://nsh.dev/zh-CN/docs/macos.md#打包)。

## Linux

```sh
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](https://nsh.dev/zh-CN/docs/linux.md#打包)。

## Windows

```sh
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](https://nsh.dev/zh-CN/docs/windows.md#打包)。

## GPUI

```sh
nsh package App.tsx --target gpui --run
```

写出的内容与本机上 `--target macos`、`--target windows` 或 `--target linux` 相同，只是可执行文件换成 GPUI 的发布版运行器
（`nsh-gpui-app`），而不是原生后端的。请在应用面向的操作系统上打包：运行器是为构建它的机器构建的。见 [GPUI](https://nsh.dev/zh-CN/docs/gpui.md#打包)。

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

```sh
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](https://nsh.dev/zh-CN/docs/web.md#发布应用)。

## 图标

在 `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` |

打包出的应用不注册任何[原生组件](https://nsh.dev/zh-CN/docs/native-components.md)；如需使用原生组件，请把 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` 等）。

## 另请参阅

- [CLI](https://nsh.dev/zh-CN/docs/cli.md)：所有命令
- [签名包](https://nsh.dev/zh-CN/docs/signed-packages.md)：应用下载的更新
- [项目结构](https://nsh.dev/zh-CN/docs/project-structure.md)：`nsh.json` 与应用的各个文件
