Skip to content

可执行文件

Bun 可执行文件生成指南

Bun 提供了强大的打包工具,可以将 TypeScript 或 JavaScript 文件编译成独立的可执行文件。这一功能通过 --compile 标志实现,让开发者能够轻松创建跨平台的应用程序。

基本使用

INFO

什么是可执行文件?可执行文件是一种无需解释器直接运行的二进制文件。对于 Bun 项目,这意味着用户无需安装 Bun 即可运行您的应用程序。

要创建一个简单的可执行文件,使用以下命令:

bash
bun build ./cli.ts --compile --outfile mycli
ts
console.log("Hello world!");

生成的可执行文件可以直接执行:

bash
$ ./mycli
Hello world!

Bun 会将所有导入的文件和包,以及 Bun 运行时捆绑到可执行文件中。所有内置的 Bun 和 Node.js API 都受支持。

跨平台编译

使用 --target 标志可以为不同的操作系统、架构或 Bun 版本编译可执行文件。这使您能在一台机器上为多种目标平台构建应用。

为 Linux x64 平台构建

bash
# 基本构建
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp

# 支持2013年前的CPU (baseline版本,兼容性更好)
bun build --compile --target=bun-linux-x64-baseline ./index.ts --outfile myapp

# 仅支持2013年及之后的CPU (modern版本,性能更好)
bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp

为 Linux ARM64 平台构建

bash
# 适用于Graviton或Raspberry Pi等设备
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp

为 Windows 平台构建

bash
# 基本构建
bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp

# 支持2013年前的CPU (baseline版本)
bun build --compile --target=bun-windows-x64-baseline ./path/to/my/app.ts --outfile myapp

# 仅支持2013年及之后的CPU (modern版本)
bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfile myapp

对于 Windows 可执行文件,如果没有提供`.exe`扩展名,Bun 会自动添加。

为 macOS 平台构建

bash
# ARM64架构(例如M系列芯片)
bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp

# x64架构(Intel芯片)
bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp

支持的目标平台

下表展示了所有受支持的目标平台配置:

目标平台操作系统架构Modern 支持Baseline 支持Libc
bun-linux-x64Linuxx64glibc
bun-linux-arm64Linuxarm64N/Aglibc
bun-windows-x64Windowsx64-
bun-windows-arm64Windowsarm64-
bun-darwin-x64macOSx64-
bun-darwin-arm64macOSarm64N/A-
bun-linux-x64-muslLinuxx64musl
bun-linux-arm64-muslLinuxarm64N/Amusl

对于 x64 平台,Bun 利用 SIMD 优化来提升性能,这需要支持 AVX2 指令集的现代 CPU。如果目标设备较旧,应使用`-baseline`版本。如果您或用户遇到"Illegal instruction"错误,很可能需要使用 baseline 版本。

部署到生产环境

编译后的可执行文件有助于减少内存使用并改善 Bun 的启动时间。通常,Bun 会在importrequire时读取并转译 JavaScript 和 TypeScript 文件。这使得 Bun"开箱即用",但会消耗时间和内存。

使用编译后的可执行文件,您可以将这些成本从运行时转移到构建时。

生产环境部署推荐命令

bash
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp

字节码编译

要进一步提高启动时间,可以启用字节码编译:

bash
bun build --compile --minify --sourcemap --bytecode ./path/to/my/app.ts --outfile myapp

使用字节码编译,应用程序如tsc的启动速度可提高 2 倍:

字节码编译是 Bun v1.1.30 引入的实验性功能,目前仅支持`cjs`格式(意味着不支持顶层 await)。

参数详解

  • --minify: 优化转译输出代码的大小。对于大型应用,可以节省兆字节级空间。
  • --sourcemap: 嵌入用 zstd 压缩的源映射,使错误和堆栈跟踪指向原始位置。
  • --bytecode: 启用字节码编译,将源代码解析工作从运行时转移到构建时,节省启动时间。

Worker 支持

要在独立可执行文件中使用工作线程,需要将 worker 的入口点添加到 CLI 参数中:

bash
bun build --compile ./index.ts ./my-worker.ts --outfile myapp

然后在代码中引用 worker:

ts
console.log("Hello from Bun!");

// 以下任何一种方式都有效:
new Worker("./my-worker.ts");
new Worker(new URL("./my-worker.ts", import.meta.url));
new Worker(new URL("./my-worker.ts", import.meta.url).href);

从 Bun v1.1.25 开始,添加到可执行文件的多个入口点将被分别打包。如果使用相对路径引用未包含在可执行文件中的文件,Bun 会尝试从进程的当前工作目录相对加载该路径。

SQLite 支持

bun build --compile支持bun:sqlite导入。默认情况下,数据库相对于进程的当前工作目录进行解析。

js
import db from "./my.db" with { type: "sqlite" };

console.log(db.query("select * from users LIMIT 1").get());

示例:如果可执行文件位于/usr/bin/hello,用户终端位于/home/me/Desktop,它将查找/home/me/Desktop/my.db

bash
$ cd /home/me/Desktop
$ ./hello

嵌入资源和文件

基本文件嵌入

独立可执行文件支持嵌入文件。要在可执行文件中嵌入文件,只需在代码中导入:

ts
// 这会成为一个内部文件路径
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";

export default {
  fetch(req) {
    // 嵌入文件可以从Response对象中流式传输
    return new Response(file(icon));
  },
};

嵌入文件可以使用Bun.file函数或 Node.js 的fs.readFile函数读取:

js
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";

const bytes = await file(icon).arrayBuffer();
// 或使用: await fs.promises.readFile(icon)
// 或使用: fs.readFileSync(icon)

嵌入 SQLite 数据库

如果应用程序需要嵌入 SQLite 数据库,在导入属性中设置type: "sqlite"embed: "true"

js
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };

console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());

嵌入的数据库支持读写,但所有更改在可执行文件退出后都会丢失(因为它存储在内存中)。

嵌入 N-API 附加组件

自 Bun v1.0.23 起,可以将.node文件嵌入到可执行文件中:

js
const addon = require("./addon.node");

console.log(addon.hello());

如果使用`@mapbox/node-pre-gyp`或类似工具,需确保直接 require `.node`文件,否则打包可能不正确。

嵌入目录

要嵌入目录,在bun build命令中使用 shell 通配符:

bash
bun build --compile ./index.ts ./public/**/*.png

然后在代码中引用文件:

ts
import icon from "./public/assets/icon.png" with { type: "file" };
import { file } from "bun";

export default {
  fetch(req) {
    return new Response(file(icon));
  },
};

列出嵌入文件

要获取所有嵌入文件的列表,使用Bun.embeddedFiles

js
import "./icon.png" with { type: "file" };
import { embeddedFiles } from "bun";

console.log(embeddedFiles[0].name); // `icon-${hash}.png`

Bun.embeddedFiles返回一个Blob对象数组,可用于获取文件的大小、内容和其他属性:

ts
embeddedFiles: Blob[]

嵌入文件列表不包括打包的源代码,如`.ts`和`.js`文件。

内容哈希

默认情况下,嵌入文件的名称会附加内容哈希值。这在提供文件 URL 服务或 CDN 时很有用,可减少缓存失效问题。如需禁用内容哈希,使用--asset-naming参数:

bash
bun build --compile --asset-naming="[name].[ext]" ./index.ts

代码压缩

要稍微减小可执行文件的大小,向bun build --compile传递--minify参数。这使用 Bun 的压缩器减少代码大小。

Windows 特定标志

在 Windows 上编译独立可执行文件时,有两个平台特定选项可用于自定义生成的.exe文件元数据:

  • --windows-icon=path/to/icon.ico:自定义可执行文件图标。
  • --windows-hide-console:禁用背景终端,适用于不需要 TTY 的应用程序。

WARNING

这些标志目前不能用于跨平台编译,因为它们依赖于 Windows API。

macOS 代码签名

要在 macOS 上为独立可执行文件进行代码签名(修复 Gatekeeper 警告),使用codesign命令:

bash
codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp

建议包含具有 JIT 权限的entitlements.plist文件:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
</dict>
</plist>

要使用 JIT 支持进行代码签名,向codesign传递--entitlements标志:

bash
codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp

代码签名后,验证可执行文件:

bash
codesign -vvv --verify ./myapp
./myapp: valid on disk
./myapp: satisfies its Designated Requirement

TIP

代码签名支持需要 Bun v1.2.4 或更新版本。

不支持的 CLI 参数

目前,--compile标志每次只能接受一个入口点,且不支持以下标志:

  • --outdir — 请使用outfile代替
  • --splitting
  • --public-path
  • --target=node--target=browser
  • --format - 始终输出二进制可执行文件(内部几乎是 esm)
  • --no-bundle - 始终将所有内容打包到可执行文件中