将 Gleam 应用打包成单个可执行文件
Packaging a Gleam app into a single executable

原始链接: https://www.dhzdhd.dev/blog/gleam-executable

## 从 Gleam 项目创建可执行文件 Gleam 是一种函数式编程语言,编译为 Erlang 和 JavaScript,本身并不原生创建可执行文件。本文概述了克服此限制的方法。 **Erlang 目标:** 使用 **Gleescript** 可以创建可执行文件,但需要在目标机器上安装 Erlang VM。 **Burrito** 提供了一个自包含的可执行文件,但与 Gleam 的设置目前仍处于实验阶段。 **JavaScript 目标:** 有几种选择。 **Deno compile** 将 Deno 运行时与您的代码捆绑在一起,创建一个可移植的可执行文件,但需要使用 ESbuild、Webpack 或 Parcel 等工具捆绑 Gleam 的输出。 **Node SEA**(Single Executable Applications)是 Node.js v23+ 的一项功能,也需要捆绑程序,但在测试中被证明不稳定。 **Bun build --compile** 是最简单、最快速的方法,将所有内容捆绑到一个包含自身运行时的单个可执行文件中——但会导致文件大小较大(超过 100MB)。 **Nexe** 是另一个用于 Node.js 项目的选项,但尚未与 Gleam 充分测试。 最终,**Bun** 因其速度和易用性而脱颖而出,尽管可执行文件较大。虽然 Deno 是可行的,但 Node SEA 存在问题,而 Burrito/Gleescript 具有依赖性或实验状态。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 将 Gleam 应用打包成单个可执行文件 (dhzdhd.dev) 8 分,by todsacerdoti 1 小时前 | 隐藏 | 过去 | 收藏 | 讨论 帮助 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

Gleam is a new-ish functional programming language that compiles to Erlang and JavaScript. It features a familiar Rust like syntax while being similar to Elm in complexity and in general is a lot of fun to work with.

The problem however is that Gleam does not natively support creating executables.

This note/guide explains how to create a Gleam executable in various ways, along with their advantages and caveats.

I will be using my WIP project to demonstrate the process of creating an executable throughout the guide.

  • Install gleam by following the instructions here
  • Create a new project with gleam new <project_name>
  • Build the project with gleam build --target=erlang|javascript

The target is important as it determines the generated output of the build command and hence, the way of packaging the code into an executable.

Erlang target

Gleescript (requires system Erlang)

Gleescript is a tool that allows you to create a single executable from a Gleam project. It uses the Erlang escript stdlib module to create the escript which can then be run on the Erlang VM.

The caveat is that it requires the Erlang VM to be installed on the target machine.

As quoted by the official docs -

The escript can run on any computer that has the Erlang VM installed. Older versions of the virtual machine may not support the newer bytecode contained in the escript. Typically being within a couple major versions of the version used to build the escript is safe.

Steps

  • Add gleescript as a dependency using gleam add gleescript
  • Build the project with gleam build --target erlang
  • Create the escript with gleam run -m gleescript
  • Run the executable ./your_project

Burrito

Burrito is a tool to wrap Elixir applications in a BEAM burrito so as to speak. Unlike gleescript, it does not require the host machine to have the Erlang VM installed. As quoted from the official docs -

Builds a self-extracting archive for a Mix project, targeting Windows, MacOS, and Linux, containing:

  • Your compiled BEAM code
  • The required ERTS for your project
  • Compilation artifacts for any elixir-make based NIFs used by the project

I have not experimented enough with Burrito to get it working with Gleam but it definitely is worth a try considering it supports Elixir and Erlang projects. Perhaps a project can be converted into an escript and then wrapped with Burrito to create a self-contained executable.

JavaScript target

Deno compile

Deno compile is a command built into Deno that allows you to compile a JavaScript file into a single executable. It bundles a lightweight Deno runtime into the executable, so it can run on any system without requiring Deno to be installed.

You still have to bundle the generated Gleam code into a single file using a bundler like Webpack/Parcel/Rollup/Esbuild. Deno used to support bundling applications with deno bundle but it has been deprecated in favor of other bundlers stated before.

Steps

  • Build the Gleam project with gleam build --target=javascript
  • Bundle the generated JavaScript files into a single file using a bundler (I am using ESbuild here) esbuild build/dev/javascript/<project_name>/<project_name>.mjs --platform=node --minify-whitespace --minify-syntax --bundle --outfile=bundle.cjs --format=cjs --footer:js=\"main();\"
    • Specify the entrypoint to the Gleam project as the first argument.
    • Next specify the platform as node as my specific project is using Node.js API’s internally (Gleam simplifile package)
    • Minify the output to reduce the size of the executable. Notice that I have minified only the whitespace and syntax and left the identifiers as is which is required for the last step.
    • Specify bundle because we obviously want to bundle the code into a single file.
    • Specify the output file as bundle.cjs and the format as cjs (CommonJS). This was a result of me trying out Node SEA first (the next method) but ESM probably works here too as Deno does not have any explicit declaration that it only supports CommonJS.
    • Specify a footer that calls the main method in the generated file as we are not using the bundled file as a module. Usually the footer is used for adding comments and there might be a better way of doing this that I am not aware of. This however serves the purpose very well.
  • Compile the bundled file into a single executable using deno compile --target=<target_architecture> --output <executable_name> bundle.cjs
    • A lot more flags can be included which are described in the docs
    • Permissions should also be added if the executable needs to access the file system or network. They can be found here

Node SEA

Node Single Executable Applications (SEA) is an experimental Node v23+ feature that allows the distribution of a Node.js application to a system that does not have Node.js installed.

The caveat with this method is that it supports only CommonJS files and you will have to bundle the generated Gleam JavaScript files into a single file using a bundler like Webpack/Parcel/Rollup/Esbuild.

Steps

  • Build the Gleam project with gleam build --target=javascript
  • Bundle the generated JavaScript files into a single file using a bundler (I am using ESbuild here) esbuild build/dev/javascript/<project_name>/<project_name>.mjs --platform=node --minify-whitespace --minify-syntax --bundle --outfile=bundle.cjs --format=cjs --footer:js=\"main();\"
    • Specify the entrypoint to the Gleam project as the first argument.
    • Next specify the platform as node as my specific project is using Node.js API’s internally (Gleam simplifile package)
    • Minify the output to reduce the size of the executable. Notice that I have minified only the whitespace and syntax and left the identifiers as is which is required for the last step.
    • Specify bundle because we obviously want to bundle the code into a single file.
    • Specify the output file as bundle.cjs and the format as cjs (CommonJS). This is required for Node SEA.
    • Specify a footer that calls the main method in the generated file as we are not using the bundled file as a module. Usually the footer is used for adding comments and there might be a better way of doing this that I am not aware of. This however serves the purpose very well.

The next steps are a lot more complicated that Deno and you can find them here. For my project however, they are -

  • Create a sea-config.json and populate as per instructions in the docs. The main and output are the important fields here.
  • Generate the blob to be injected into the copied Node.js binary using node --experimental-sea-config sea-config.json
  • Create a copy of the Node executable using cp $(command -v node) executable_name
  • Remove the binary signature by following instructions in the docs.
  • Inject the blob into the copied Node.js binary using npx postject executable_name NODE_SEA_BLOB <output>.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
  • Sign the binary if required
  • Run the binary using ./executable_name

Following the steps above resulted in a segfault for me and I am not sure why. I will update this section once I study SEA in more detail. It is clear however that Deno is way simpler to use compared to Node SEA.

Bun build —compile

Bun build with the compile flag is a Bun feature to bundle a bunch of JS files and then compile them into a single executable. It is similar to Deno compile but does not require a separate bundler.

All imported files and packages are bundled into the executable, along with a copy of the Bun runtime. All built-in Bun and Node.js APIs are supported.

Steps

  • Build the Gleam project with gleam build --target=javascript
  • bun build --compile --outfile=bundle build/dev/javascript/<project_name>/<project_name>.mjs --footer="main();"

That’s it. Bun is incredibly convinient and crazy fast compared to Deno or Node or any of the methods mentioned so far.

Nexe

Nexe is a command-line utility that compiles your Node.js application into a single executable file. Like Burrito, I have not played around with Nexe enough to get it working with my project but it should be way more straightforward than making Burrito work with Gleam.

Out of all the tools/libraries that I used, I found Bun to be incredibly fast and also very easy to use. The only problem with Bun and Deno is that due to them bundling their own runtimes, the executables are large, usually exceeding 100MB. I personally do not mind this as I am not using the executables in production but it is something to keep in mind.

联系我们 contact @ memedata.com