Skip to content

Commit

Permalink
fixup! src: use JSON configuration and blob content for SEA
Browse files Browse the repository at this point in the history
  • Loading branch information
joyeecheung committed Mar 28, 2023
1 parent 63988d2 commit c43ae7c
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 17 deletions.
12 changes: 12 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,16 @@ added: v16.6.0

Use this flag to disable top-level await in REPL.

### `--experimental-sea-config`

<!-- YAML
added: REPLACEME
-->

Use this flag to generate a blob that can be injected in to the Node.js
binary to produce a [single executable application][]. See the documentation
about [this configuration][`--experimental-sea-config`] for details.

### `--experimental-shadow-realm`

<!-- YAML
Expand Down Expand Up @@ -2546,6 +2556,7 @@ done
[`--cpu-prof-dir`]: #--cpu-prof-dir
[`--diagnostic-dir`]: #--diagnostic-dirdirectory
[`--experimental-wasm-modules`]: #--experimental-wasm-modules
[`--experimental-sea-config`]: single-executable-applications.md#experimental-sea-config`
[`--heap-prof-dir`]: #--heap-prof-dir
[`--import`]: #--importmodule
[`--openssl-config`]: #--openssl-configfile
Expand Down Expand Up @@ -2583,6 +2594,7 @@ done
[scavenge garbage collector]: https://v8.dev/blog/orinoco-parallel-scavenger
[security warning]: #warning-binding-inspector-to-a-public-ipport-combination-is-insecure
[semi-space]: https://www.memorymanagement.org/glossary/s.html#semi.space
[single executable applications]: single-executable-applications.md
[test reporters]: test.md#test-reporters
[timezone IDs]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
[tracking issue for user-land snapshots]: https://github.com/nodejs/node/issues/44014
Expand Down
66 changes: 50 additions & 16 deletions doc/api/single-executable-applications.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ This feature allows the distribution of a Node.js application conveniently to a
system that does not have Node.js installed.

Node.js supports the creation of [single executable applications][] by allowing
the injection of a JavaScript file into the `node` binary. During start up, the
program checks if anything has been injected. If the script is found, it
executes its contents. Otherwise Node.js operates as it normally does.
the injection of a blob prepared by Node.js, which can contain a bundled script,
into the `node` binary. During start up, the program checks if anything has been
injected. If the blob is found, it executes the script in the blob. Otherwise
Node.js operates as it normally does.

The single executable application feature only supports running a single
embedded [CommonJS][] file.
The single executable application feature currently only supports running a
single embedded script using the [CommonJS][] module system.

A bundled JavaScript file can be turned into a single executable application
with any tool which can inject resources into the `node` binary.
Users can create a single executable application from their bundled script
with the `node` binary itself and any tool which can inject resources into the
binary.

Here are the steps for creating a single executable application using one such
tool, [postject][]:
Expand All @@ -28,12 +30,23 @@ tool, [postject][]:
$ echo 'console.log(`Hello, ${process.argv[2]}!`);' > hello.js
```

2. Create a copy of the `node` executable and name it according to your needs:
2. Create a configuration file building a blob that can be injected into the
single executable application:
```console
$ echo '{ "main": "hello.js", "output": "sea-prep.blob" }' > sea-config.json
```

3. Generate the blob to be injected:
```console
$ node --experimental-sea-config sea-config.json
```

4. Create a copy of the `node` executable and name it according to your needs:
```console
$ cp $(command -v node) hello
```

3. Remove the signature of the binary:
5. Remove the signature of the binary:

* On macOS:

Expand All @@ -50,17 +63,17 @@ tool, [postject][]:
$ signtool remove /s hello
```

4. Inject the JavaScript file into the copied binary by running `postject` with
6. Inject the blob into the copied binary by running `postject` with
the following options:

* `hello` - The name of the copy of the `node` executable created in step 2.
* `NODE_SEA_BLOB` - The name of the resource / note / section in the binary
where the contents of the JavaScript file will be stored.
* `hello.js` - The name of the JavaScript file created in step 1.
where the contents of the blob will be stored.
* `hello.js` - The name of the blob created in step 1.
* `--sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2` - The
[fuse][] used by the Node.js project to detect if a file has been injected.
* `--macho-segment-name NODE_SEA` (only needed on macOS) - The name of the
segment in the binary where the contents of the JavaScript file will be
segment in the binary where the contents of the blob will be
stored.

To summarize, here is the required command for each platform:
Expand All @@ -78,7 +91,7 @@ tool, [postject][]:
--macho-segment-name NODE_SEA
```

5. Sign the binary:
7. Sign the binary:

* On macOS:

Expand All @@ -95,12 +108,32 @@ tool, [postject][]:
$ signtool sign /fd SHA256 hello
```

6. Run the binary:
8. Run the binary:
```console
$ ./hello world
Hello, world!
```

<a id="experimental-sea-config"></a>
## Using `--experimental-sea-config` to generate a blob for single executables

`--experimental-sea-config` takes a path to a configuration file in JSON format.
If the path passed to it isn't absolute, Node.js will use the path relative to the
current working directory.

The configuration currently reads the following top-level fields:

```
{
"main": "/path/to/bundled/script.js",
"output": "/path/to/write/the/generated/blob.blob"
}
```

If the paths are not absolute, Node.js will use the path relative to the
current working directory. The version of the Node.js binary used to produce
the blob must be the same as the one to which the blob will be injected.

## Notes

### `require(id)` in the injected module is not file based
Expand Down Expand Up @@ -135,7 +168,8 @@ of [`process.execPath`][].
### Single executable application creation process

A tool aiming to create a single executable Node.js application must
inject the contents of a JavaScript file into:
inject the contents of the blob prepared with `--experimental-sea-config"`
into:

* a resource named `NODE_SEA_BLOB` if the `node` binary is a [PE][] file
* a section named `NODE_SEA_BLOB` in the `NODE_SEA` segment if the `node` binary
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-single-executable-application.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const common = require('../common');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { copyFileSync, readFileSync, writeFileSync, existsSync } = require('fs');
const { execFileSync, exec } = require('child_process');
const { execFileSync } = require('child_process');
const { join } = require('path');
const { strictEqual } = require('assert');
const assert = require('assert');
Expand Down

0 comments on commit c43ae7c

Please sign in to comment.