使用 Deno Compile 构建自包含可执行程序

使用 Deno Compile 构建自包含可执行程序

使用 deno compile 创建跨平台单文件可执行程序

从 Deno v1.6 开始,deno compile 命令就让开发者能够将 JavaScript 和 TypeScript 程序转换成一个独立的二进制可执行文件,在各主要平台运行,无须另外安装依赖或运行时。这带来了几项重要能力:

  • 跨平台兼容:分发独立二进制文件,目标系统无须安装 Deno 运行时或依赖。
  • 资源打包:把应用所需内容放进二进制文件,方便携带。
  • 简化部署:向生产环境交付一个文件,减少复杂性。
  • 改善启动时间:相比典型的服务器或运行时配置,启动更快。

自 Deno 2 发布以来,deno compile 又获得了多项改进,包括 npm 包支持、Web Worker 支持、交叉编译、更小的二进制文件,以及带有自定义图标的代码签名。这些升级意味着,你现在不仅可以编译脚本,还可以把桌面游戏等完整应用直接编译为本地二进制程序。

本文介绍 deno compile 的独特之处、工作原理,以及不同应用场景的示例,说明它相较于生态中其他方案的优势。

什么是 deno compile?

Deno 为 JavaScript 和 TypeScript 提供零配置工具链,让你不必先拼装各种工具才能开始写代码。其中一个子命令就是 deno compile,可以将 JavaScript 或 TypeScript 代码构建为一个二进制文件。与 Node 的八步编译流程相比,deno compile 只需一个命令。

该命令让你跳过安装要求,在未安装 Deno 的系统上运行二进制程序,支持 Windows、macOS 和 Linux 等平台。Deno 编译出来的二进制文件也针对体积做了优化。对于来自 Node 的开发者,这种简洁性可以降低在不同环境间迁移代码的阻力,尤其是在为其他操作系统进行交叉编译时。

deno compile 如何工作

虽然名字叫“compile”,但 deno compile 并不是在传统意义上编译 JavaScript。它将 JavaScript 和 TypeScript 嵌入一个特殊的 Deno 运行时二进制文件,名为 denort,即“Deno runtime”的缩写。

执行 deno compile 时,过程大致如下:

  1. 下载精简版运行时 denort。如果为其他操作系统交叉编译,就下载对应平台的 denort。
  2. 将脚本及依赖打包为 eszip 文件。这是一种用于序列化 ECMAScript 模块图的无损格式。
  3. 使用 Sui 将打包内容注入 denort 二进制文件。

由于打包内容嵌入的是可执行文件的一个节区,最终文件仍然能够进行代码签名。

示例

基本的自包含二进制文件

先来看将 TypeScript 脚本转换成单个可执行文件有多简单。

// main.ts
import open from "jsr:@rdsq/open";

await open("https://www.youtube.com/watch?v=dQw4w9WgXcQ");

编译只需:

deno compile -A -o runme main.ts

这会生成包含所有必要依赖的可执行二进制文件 runme。现在可以分享这一个文件,最终用户无须安装 Deno。

注意,可以在 deno compile 命令中传入需要明确启用的权限选项,例如 --allow-net。这些权限选项会保留在编译后的二进制文件中,为限制二进制文件的使用方式提供额外的安全控制。

为 Windows 交叉编译

假设要把应用分享给 Windows 用户,可以使用 --target 选项进行交叉编译:

deno compile --allow-all -o runme --target x86_64-pc-windows-msvc main.ts

这样就得到了兼容 Windows 的 runme.exe。还可以通过 --icon 选项添加自定义图标,让应用更完善:

deno compile --allow-all -o runme --target x86_64-pc-windows-msvc --icon icon.ico main.ts

代码签名

代码签名用于验证二进制文件及其可信来源,对于软件分发十分重要。Deno 简化了这一过程;在 macOS 上,也可以像处理其他应用一样使用 codesign。

下面是原文展示的二进制完整性验证及输出:

codesign --verify -vv ./runme
./runme: valid on disk
./runme: satisfies its Designated Requirement

原文示例中,编译得到的二进制文件签名有效,可以准备分发。

编译 npm 包

deno compile 当然也能使用 npm 包。这适合对性能敏感的 CI 环境,或者需要提供某个工具的单一可移植版本的场景。

例如,可以用 deno compile 编译 npm 本身:

deno compile -A npm:npm

运行编译后的 npm 二进制文件很简单,原文测试中还观察到了性能提升:通过 Deno 编译的 npm 二进制文件运行速度约为常规 npm 的 1.9 倍。

# hyperfine "./npm -v" "npm -v"
Benchmark 1: ./npm -v
  Time (mean ± σ):      40.1 ms ±  2.0 ms    [User: 37.7 ms, System: 5.3 ms]
  Range (min … max):    38.9 ms … 51.8 ms    71 runs

Benchmark 2: npm -v
  Time (mean ± σ):      75.2 ms ±  7.6 ms    [User: 59.1 ms, System: 9.0 ms]
  Range (min … max):    69.2 ms … 105.0 ms   40 runs

Summary
  ./npm -v ran
    1.87 ± 0.21 times faster than npm -v

这种方式也适用于 prettier、eslint 等常见 npm 工具,可以获得运行更快、便于携带的工具版本。

游戏和桌面应用

deno compile 也让基于 Web 的游戏和应用有机会被打包成原生桌面可执行文件。例如,可以把 HTML/CSS/JavaScript 游戏转换成二进制文件,方便在桌面上分享和启动。

原文展示了一个将 HTML 游戏通过 deno compile 打包的演示。这个 Flappybird 克隆项目可以使用 Deno 编译为桌面应用。对应编译命令为:

deno compile --unstable-ffi --env -o flappybird -A main.js

打包资源

在原文发表于2024年11月时,deno compile 尚不直接支持资源打包。不过,可以将 .png 或其他资源文件转换成 Base64 编码的 .js 文件,再将它们导入项目,使它们被包含在最终二进制文件中。虽然属于变通方式,这仍让开发者能够把游戏和其他资源打包为单个可执行文件。

接下来会怎样

Deno 团队会继续依据反馈改进 deno compile,计划简化资源打包、支持 Svelte 等更多框架、缩小二进制文件体积,并通过缓存进一步改善启动时间。

原文:Self-contained Executable Programs with Deno Compile,Ryan Dahl、Andy Jiang,2024年11月14日。原网站页脚注明 © Deno Land Inc. All rights reserved。译文依据转载授权制作;历史功能范围和性能数据沿用原文。代码与命令未在本次工作中运行。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容