Skip to content

Commit 6b14bd8

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 <[email protected]> Reviewed-By: Mohammed Keyvanzadeh <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent 86553c9 commit 6b14bd8

12 files changed

+365
-36
lines changed

configure.py

+19
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,13 @@
787787
default=False,
788788
help='node will load builtin modules from disk instead of from binary')
789789

790+
parser.add_argument('--node-snapshot-main',
791+
action='store',
792+
dest='node_snapshot_main',
793+
default=None,
794+
help='Run a file when building the embedded snapshot. Currently ' +
795+
'experimental.')
796+
790797
# Create compile_commands.json in out/Debug and out/Release.
791798
parser.add_argument('-C',
792799
action='store_true',
@@ -1215,6 +1222,18 @@ def configure_node(o):
12151222

12161223
o['variables']['want_separate_host_toolset'] = int(cross_compiling)
12171224

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

lib/internal/bootstrap/pre_execution.js

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

27-
function prepareMainThreadExecution(expandArgv1 = false) {
27+
function prepareMainThreadExecution(expandArgv1 = false,
28+
initialzeModules = true) {
2829
refreshRuntimeOptions();
2930

3031
// TODO(joyeecheung): this is also necessary for workers when they deserialize
@@ -78,9 +79,13 @@ function prepareMainThreadExecution(expandArgv1 = false) {
7879
initializeSourceMapsHandlers();
7980
initializeDeprecations();
8081
initializeWASI();
82+
83+
if (!initialzeModules) {
84+
return;
85+
}
86+
8187
initializeCJSLoader();
8288
initializeESMLoader();
83-
8489
const CJSLoader = require('internal/modules/cjs/loader');
8590
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
8691
loadPreloadModules();
@@ -99,7 +104,8 @@ function patchProcessObject(expandArgv1) {
99104

100105
ObjectDefineProperty(process, 'argv0', {
101106
enumerable: true,
102-
configurable: false,
107+
// Only set it to true during snapshot building.
108+
configurable: getOptionValue('--build-snapshot'),
103109
value: process.argv[0]
104110
});
105111
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
@@ -724,6 +724,11 @@ PerProcessOptionsParser::PerProcessOptionsParser(
724724
"disable Object.prototype.__proto__",
725725
&PerProcessOptions::disable_proto,
726726
kAllowedInEnvironment);
727+
AddOption("--build-snapshot",
728+
"Generate a snapshot blob when the process exits."
729+
"Currently only supported in the node_mksnapshot binary.",
730+
&PerProcessOptions::build_snapshot,
731+
kDisallowedInEnvironment);
727732

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

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ class PerProcessOptions : public Options {
229229
bool zero_fill_all_buffers = false;
230230
bool debug_arraybuffer_allocations = false;
231231
std::string disable_proto;
232+
bool build_snapshot;
232233

233234
std::vector<std::string> security_reverts;
234235
bool print_bash_completion = false;

0 commit comments

Comments
 (0)