Skip to content

Commit ce5e484

Browse files
joyeecheungjuanarbol
authored andcommitted
build: add --node-snapshot-main configure option
This adds a --build-snapshot runtime option which is currently only supported by the node_mksnapshot binary, and a --node-snapshot-main configure option that makes use it to run a custom script when building the embedded snapshot. The idea is to have this experimental feature in core as a configure-time feature for now, and investigate the renaming V8 bugs before we make it available to more users via making it a runtime option. PR-URL: #42466 Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com> Reviewed-By: Khaidi Chu <i@2333.moe> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 8970167 commit ce5e484

12 files changed

+365
-36
lines changed

configure.py

+19
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,13 @@
792792
default=False,
793793
help='node will load builtin modules from disk instead of from binary')
794794

795+
parser.add_argument('--node-snapshot-main',
796+
action='store',
797+
dest='node_snapshot_main',
798+
default=None,
799+
help='Run a file when building the embedded snapshot. Currently ' +
800+
'experimental.')
801+
795802
# Create compile_commands.json in out/Debug and out/Release.
796803
parser.add_argument('-C',
797804
action='store_true',
@@ -1219,6 +1226,18 @@ def configure_node(o):
12191226

12201227
o['variables']['want_separate_host_toolset'] = int(cross_compiling)
12211228

1229+
if options.node_snapshot_main is not None:
1230+
if options.shared:
1231+
# This should be possible to fix, but we will need to refactor the
1232+
# libnode target to avoid building it twice.
1233+
error('--node-snapshot-main is incompatible with --shared')
1234+
if options.without_node_snapshot:
1235+
error('--node-snapshot-main is incompatible with ' +
1236+
'--without-node-snapshot')
1237+
if cross_compiling:
1238+
error('--node-snapshot-main is incompatible with cross compilation')
1239+
o['variables']['node_snapshot_main'] = options.node_snapshot_main
1240+
12221241
if options.without_node_snapshot or options.node_builtin_modules_path:
12231242
o['variables']['node_use_node_snapshot'] = 'false'
12241243
else:

lib/internal/bootstrap/pre_execution.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const { Buffer } = require('buffer');
2626
const { ERR_MANIFEST_ASSERT_INTEGRITY } = require('internal/errors').codes;
2727
const assert = require('internal/assert');
2828

29-
function prepareMainThreadExecution(expandArgv1 = false) {
29+
function prepareMainThreadExecution(expandArgv1 = false,
30+
initialzeModules = true) {
3031
refreshRuntimeOptions();
3132

3233
// TODO(joyeecheung): this is also necessary for workers when they deserialize
@@ -80,9 +81,13 @@ function prepareMainThreadExecution(expandArgv1 = false) {
8081
initializeSourceMapsHandlers();
8182
initializeDeprecations();
8283
initializeWASI();
84+
85+
if (!initialzeModules) {
86+
return;
87+
}
88+
8389
initializeCJSLoader();
8490
initializeESMLoader();
85-
8691
const CJSLoader = require('internal/modules/cjs/loader');
8792
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
8893
loadPreloadModules();
@@ -101,7 +106,8 @@ function patchProcessObject(expandArgv1) {
101106

102107
ObjectDefineProperty(process, 'argv0', {
103108
enumerable: true,
104-
configurable: false,
109+
// Only set it to true during snapshot building.
110+
configurable: getOptionValue('--build-snapshot'),
105111
value: process.argv[0]
106112
});
107113
process.argv[0] = process.execPath;

lib/internal/main/mksnapshot.js

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
'use strict';
2+
3+
const {
4+
Error,
5+
SafeSet,
6+
SafeArrayIterator
7+
} = primordials;
8+
9+
const binding = internalBinding('mksnapshot');
10+
const { NativeModule } = require('internal/bootstrap/loaders');
11+
const {
12+
compileSnapshotMain,
13+
} = binding;
14+
15+
const {
16+
getOptionValue
17+
} = require('internal/options');
18+
19+
const {
20+
readFileSync
21+
} = require('fs');
22+
23+
const supportedModules = new SafeSet(new SafeArrayIterator([
24+
// '_http_agent',
25+
// '_http_client',
26+
// '_http_common',
27+
// '_http_incoming',
28+
// '_http_outgoing',
29+
// '_http_server',
30+
'_stream_duplex',
31+
'_stream_passthrough',
32+
'_stream_readable',
33+
'_stream_transform',
34+
'_stream_wrap',
35+
'_stream_writable',
36+
// '_tls_common',
37+
// '_tls_wrap',
38+
'assert',
39+
'assert/strict',
40+
// 'async_hooks',
41+
'buffer',
42+
// 'child_process',
43+
// 'cluster',
44+
'console',
45+
'constants',
46+
'crypto',
47+
// 'dgram',
48+
// 'diagnostics_channel',
49+
// 'dns',
50+
// 'dns/promises',
51+
// 'domain',
52+
'events',
53+
'fs',
54+
'fs/promises',
55+
// 'http',
56+
// 'http2',
57+
// 'https',
58+
// 'inspector',
59+
// 'module',
60+
// 'net',
61+
'os',
62+
'path',
63+
'path/posix',
64+
'path/win32',
65+
// 'perf_hooks',
66+
'process',
67+
'punycode',
68+
'querystring',
69+
// 'readline',
70+
// 'repl',
71+
'stream',
72+
'stream/promises',
73+
'string_decoder',
74+
'sys',
75+
'timers',
76+
'timers/promises',
77+
// 'tls',
78+
// 'trace_events',
79+
// 'tty',
80+
'url',
81+
'util',
82+
'util/types',
83+
'v8',
84+
// 'vm',
85+
// 'worker_threads',
86+
// 'zlib',
87+
]));
88+
89+
const warnedModules = new SafeSet();
90+
function supportedInUserSnapshot(id) {
91+
return supportedModules.has(id);
92+
}
93+
94+
function requireForUserSnapshot(id) {
95+
if (!NativeModule.canBeRequiredByUsers(id)) {
96+
// eslint-disable-next-line no-restricted-syntax
97+
const err = new Error(
98+
`Cannot find module '${id}'. `
99+
);
100+
err.code = 'MODULE_NOT_FOUND';
101+
throw err;
102+
}
103+
if (!supportedInUserSnapshot(id)) {
104+
if (!warnedModules.has(id)) {
105+
process.emitWarning(
106+
`built-in module ${id} is not yet supported in user snapshots`);
107+
warnedModules.add(id);
108+
}
109+
}
110+
111+
return require(id);
112+
}
113+
114+
function main() {
115+
const {
116+
prepareMainThreadExecution
117+
} = require('internal/bootstrap/pre_execution');
118+
119+
prepareMainThreadExecution(true, false);
120+
process.once('beforeExit', function runCleanups() {
121+
for (const cleanup of binding.cleanups) {
122+
cleanup();
123+
}
124+
});
125+
126+
const file = process.argv[1];
127+
const path = require('path');
128+
const filename = path.resolve(file);
129+
const dirname = path.dirname(filename);
130+
const source = readFileSync(file, 'utf-8');
131+
const snapshotMainFunction = compileSnapshotMain(filename, source);
132+
133+
if (getOptionValue('--inspect-brk')) {
134+
internalBinding('inspector').callAndPauseOnStart(
135+
snapshotMainFunction, undefined,
136+
requireForUserSnapshot, filename, dirname);
137+
} else {
138+
snapshotMainFunction(requireForUserSnapshot, filename, dirname);
139+
}
140+
}
141+
142+
main();

node.gyp

+39-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
'node_use_dtrace%': 'false',
88
'node_use_etw%': 'false',
99
'node_no_browser_globals%': 'false',
10+
'node_snapshot_main%': '',
1011
'node_use_node_snapshot%': 'false',
1112
'node_use_v8_platform%': 'true',
1213
'node_use_bundled_v8%': 'true',
@@ -315,23 +316,47 @@
315316
'dependencies': [
316317
'node_mksnapshot',
317318
],
318-
'actions': [
319-
{
320-
'action_name': 'node_mksnapshot',
321-
'process_outputs_as_sources': 1,
322-
'inputs': [
323-
'<(node_mksnapshot_exec)',
324-
],
325-
'outputs': [
326-
'<(SHARED_INTERMEDIATE_DIR)/node_snapshot.cc',
319+
'conditions': [
320+
['node_snapshot_main!=""', {
321+
'actions': [
322+
{
323+
'action_name': 'node_mksnapshot',
324+
'process_outputs_as_sources': 1,
325+
'inputs': [
326+
'<(node_mksnapshot_exec)',
327+
'<(node_snapshot_main)',
328+
],
329+
'outputs': [
330+
'<(SHARED_INTERMEDIATE_DIR)/node_snapshot.cc',
331+
],
332+
'action': [
333+
'<(node_mksnapshot_exec)',
334+
'--build-snapshot',
335+
'<(node_snapshot_main)',
336+
'<@(_outputs)',
337+
],
338+
},
327339
],
328-
'action': [
329-
'<@(_inputs)',
330-
'<@(_outputs)',
340+
}, {
341+
'actions': [
342+
{
343+
'action_name': 'node_mksnapshot',
344+
'process_outputs_as_sources': 1,
345+
'inputs': [
346+
'<(node_mksnapshot_exec)',
347+
],
348+
'outputs': [
349+
'<(SHARED_INTERMEDIATE_DIR)/node_snapshot.cc',
350+
],
351+
'action': [
352+
'<@(_inputs)',
353+
'<@(_outputs)',
354+
],
355+
},
331356
],
332-
},
357+
}],
333358
],
334-
}, {
359+
}, {
335360
'sources': [
336361
'src/node_snapshot_stub.cc'
337362
],

src/node.cc

+10
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,10 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
486486
return StartExecution(env, "internal/main/inspect");
487487
}
488488

489+
if (per_process::cli_options->build_snapshot) {
490+
return StartExecution(env, "internal/main/mksnapshot");
491+
}
492+
489493
if (per_process::cli_options->print_help) {
490494
return StartExecution(env, "internal/main/print_help");
491495
}
@@ -1144,6 +1148,12 @@ int Start(int argc, char** argv) {
11441148
return result.exit_code;
11451149
}
11461150

1151+
if (per_process::cli_options->build_snapshot) {
1152+
fprintf(stderr,
1153+
"--build-snapshot is not yet supported in the node binary\n");
1154+
return 1;
1155+
}
1156+
11471157
{
11481158
bool use_node_snapshot =
11491159
per_process::cli_options->per_isolate->node_snapshot;

src/node_binding.cc

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
V(js_udp_wrap) \
6060
V(messaging) \
6161
V(module_wrap) \
62+
V(mksnapshot) \
6263
V(native_module) \
6364
V(options) \
6465
V(os) \

src/node_external_reference.h

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class ExternalReferenceRegistry {
6666
V(handle_wrap) \
6767
V(heap_utils) \
6868
V(messaging) \
69+
V(mksnapshot) \
6970
V(native_module) \
7071
V(options) \
7172
V(os) \

src/node_options.cc

+5
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,11 @@ PerProcessOptionsParser::PerProcessOptionsParser(
716716
"disable Object.prototype.__proto__",
717717
&PerProcessOptions::disable_proto,
718718
kAllowedInEnvironment);
719+
AddOption("--build-snapshot",
720+
"Generate a snapshot blob when the process exits."
721+
"Currently only supported in the node_mksnapshot binary.",
722+
&PerProcessOptions::build_snapshot,
723+
kDisallowedInEnvironment);
719724

720725
// 12.x renamed this inadvertently, so alias it for consistency within the
721726
// release line, while using the original name for consistency with older

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ class PerProcessOptions : public Options {
224224
bool zero_fill_all_buffers = false;
225225
bool debug_arraybuffer_allocations = false;
226226
std::string disable_proto;
227+
bool build_snapshot;
227228

228229
std::vector<std::string> security_reverts;
229230
bool print_bash_completion = false;

0 commit comments

Comments
 (0)