diff --git a/.github/workflows/test-shared.yml b/.github/workflows/test-shared.yml index 99c0920cdf0bec..176f28bcdbec3b 100644 --- a/.github/workflows/test-shared.yml +++ b/.github/workflows/test-shared.yml @@ -192,7 +192,7 @@ jobs: --arg ccache '(import {}).sccache' \ --arg devTools '[]' \ --arg benchmarkTools '[]' \ - ${{ endsWith(matrix.system, '-darwin') && '--arg extraConfigFlags ''["--without-amaro" "--without-inspector"]'' \' || '\' }} + ${{ endsWith(matrix.system, '-darwin') && '--arg extraConfigFlags ''["--without-amaro" "--without-inspector" "--without-node-options"]'' \' || '\' }} --run ' make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p actions --measure-flakiness 9 --skip-tests=$CI_SKIP_TESTS" ' diff --git a/shell.nix b/shell.nix index faa3446d80c952..cae2418ca20f4d 100644 --- a/shell.nix +++ b/shell.nix @@ -1,54 +1,64 @@ { pkgs ? import ./tools/nix/pkgs.nix { }, - loadJSBuiltinsDynamically ? true, # Load `lib/**.js` from disk instead of embedding - withTemporal ? false, - ncu-path ? null, # Provide this if you want to use a local version of NCU - icu ? pkgs.icu, - sharedLibDeps ? import ./tools/nix/sharedLibDeps.nix { inherit pkgs withTemporal; }, + + # Optional build tools / config ccache ? pkgs.ccache, + loadJSBuiltinsDynamically ? true, # Load `lib/**.js` from disk instead of embedding ninja ? pkgs.ninja, - devTools ? import ./tools/nix/devTools.nix { inherit pkgs ncu-path; }, - benchmarkTools ? import ./tools/nix/benchmarkTools.nix { inherit pkgs; }, extraConfigFlags ? [ "--without-npm" "--debug-node" - ] - ++ pkgs.lib.optionals withTemporal [ - "--v8-enable-temporal-support" ], + + # Build options + icu ? pkgs.icu, + withAmaro ? true, + withSQLite ? true, + withSSL ? true, + withTemporal ? false, + sharedLibDeps ? import ./tools/nix/sharedLibDeps.nix { + inherit + pkgs + withSQLite + withSSL + withTemporal + ; + }, + + # dev tools (not needed to build Node.js, useful to maintain it) + ncu-path ? null, # Provide this if you want to use a local version of NCU + devTools ? import ./tools/nix/devTools.nix { inherit pkgs ncu-path; }, + benchmarkTools ? import ./tools/nix/benchmarkTools.nix { inherit pkgs; }, }: let useSharedICU = if builtins.isString icu then icu == "system" else icu != null; useSharedAda = builtins.hasAttr "ada" sharedLibDeps; useSharedOpenSSL = builtins.hasAttr "openssl" sharedLibDeps; + + needsRustCompiler = withTemporal && !builtins.hasAttr "temporal_capi" sharedLibDeps; in pkgs.mkShell { inherit (pkgs.nodejs_latest) nativeBuildInputs; buildInputs = builtins.attrValues sharedLibDeps ++ pkgs.lib.optional useSharedICU icu; - packages = [ - ccache - ] - ++ devTools - ++ benchmarkTools - ++ pkgs.lib.optionals (withTemporal && !builtins.hasAttr "temporal_capi" sharedLibDeps) [ - pkgs.cargo - pkgs.rustc - ]; + packages = + pkgs.lib.optional (ccache != null) ccache + ++ devTools + ++ benchmarkTools + ++ pkgs.lib.optionals needsRustCompiler [ + pkgs.cargo + pkgs.rustc + ]; - shellHook = - if (ccache != null) then - '' - export CC="${pkgs.lib.getExe ccache} $CC" - export CXX="${pkgs.lib.getExe ccache} $CXX" - '' - else - ""; + shellHook = pkgs.lib.optionalString (ccache != null) '' + export CC="${pkgs.lib.getExe ccache} $CC" + export CXX="${pkgs.lib.getExe ccache} $CXX" + ''; BUILD_WITH = if (ninja != null) then "ninja" else "make"; - NINJA = if (ninja != null) then "${pkgs.lib.getExe ninja}" else ""; + NINJA = pkgs.lib.optionalString (ninja != null) "${pkgs.lib.getExe ninja}"; CI_SKIP_TESTS = pkgs.lib.concatStringsSep "," ( [ ] ++ pkgs.lib.optionals useSharedAda [ @@ -70,12 +80,12 @@ pkgs.mkShell { ) ] ++ extraConfigFlags - ++ pkgs.lib.optionals (ninja != null) [ - "--ninja" - ] - ++ pkgs.lib.optionals loadJSBuiltinsDynamically [ - "--node-builtin-modules-path=${builtins.toString ./.}" - ] + ++ pkgs.lib.optional (!withAmaro) "--without-amaro" + ++ pkgs.lib.optional (!withSQLite) "--without-sqlite" + ++ pkgs.lib.optional (!withSSL) "--without-ssl" + ++ pkgs.lib.optional withTemporal "--v8-enable-temporal-support" + ++ pkgs.lib.optional (ninja != null) "--ninja" + ++ pkgs.lib.optional loadJSBuiltinsDynamically "--node-builtin-modules-path=${builtins.toString ./.}" ++ pkgs.lib.concatMap (name: [ "--shared-${builtins.replaceStrings [ "c-ares" ] [ "cares" ] name}" "--shared-${builtins.replaceStrings [ "c-ares" ] [ "cares" ] name}-libpath=${ @@ -86,4 +96,5 @@ pkgs.mkShell { }/include" ]) (builtins.attrNames sharedLibDeps) ); + NOSQLITE = pkgs.lib.optionalString (!withSQLite) "1"; } diff --git a/test/embedding/test-embedding.js b/test/embedding/test-embedding.js index 71c4f7f324c973..49706079fd8b6a 100644 --- a/test/embedding/test-embedding.js +++ b/test/embedding/test-embedding.js @@ -154,7 +154,7 @@ for (const extraSnapshotArgs of [ } // Guarantee NODE_REPL_EXTERNAL_MODULE won't bypass kDisableNodeOptionsEnv -{ +if (!process.config.variables.node_without_node_options) { spawnSyncAndExit( binary, ['require("os")'], diff --git a/test/es-module/test-esm-import-flag.mjs b/test/es-module/test-esm-import-flag.mjs index 81de3b11a38609..c0c22704f53a8a 100644 --- a/test/es-module/test-esm-import-flag.mjs +++ b/test/es-module/test-esm-import-flag.mjs @@ -9,6 +9,7 @@ const cjsImport = fixtures.fileURL('es-modules', 'cjs-file.cjs'); const mjsEntry = fixtures.path('es-modules', 'mjs-file.mjs'); const mjsImport = fixtures.fileURL('es-modules', 'mjs-file.mjs'); +const onlyIfNodeOptionsSupport = { skip: process.config.variables.node_without_node_options }; describe('import modules using --import', { concurrency: !process.env.TEST_PARALLEL }, () => { it('should import when using --eval', async () => { @@ -199,7 +200,7 @@ describe('import modules using --import', { concurrency: !process.env.TEST_PARAL assert.strictEqual(signal, null); }); - it('should import files from the env before ones from the CLI', async () => { + it('should import files from the env before ones from the CLI', onlyIfNodeOptionsSupport, async () => { const { code, signal, stderr, stdout } = await spawnPromisified( execPath, [ diff --git a/test/es-module/test-esm-symlink-type.js b/test/es-module/test-esm-symlink-type.js index 76f03d4f46d457..29819b0b5ecca2 100644 --- a/test/es-module/test-esm-symlink-type.js +++ b/test/es-module/test-esm-symlink-type.js @@ -6,6 +6,10 @@ const assert = require('assert'); const exec = require('child_process').execFile; const fs = require('fs'); +if (process.config.variables.node_without_node_options) { + common.skip('missing NODE_OPTIONS support'); +} + const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); const tmpDir = tmpdir.path; diff --git a/test/es-module/test-esm-type-field-errors.js b/test/es-module/test-esm-type-field-errors.js index 4bf52f3ad6e7d3..dcd411202068b5 100644 --- a/test/es-module/test-esm-type-field-errors.js +++ b/test/es-module/test-esm-type-field-errors.js @@ -4,6 +4,10 @@ const assert = require('assert'); const exec = require('child_process').execFile; const { describe, it } = require('node:test'); +if (process.config.variables.node_without_node_options) { + common.skip('missing NODE_OPTIONS support'); +} + const mjsFile = require.resolve('../fixtures/es-modules/mjs-file.mjs'); const cjsFile = require.resolve('../fixtures/es-modules/cjs-file.cjs'); const packageWithoutTypeMain = diff --git a/test/parallel/test-cli-options-as-flags.js b/test/parallel/test-cli-options-as-flags.js index 04e9e801b8ec81..403a18d162ca61 100644 --- a/test/parallel/test-cli-options-as-flags.js +++ b/test/parallel/test-cli-options-as-flags.js @@ -12,6 +12,8 @@ const fixtureFile = fixtures.path(path.join('options-as-flags', 'fixture.cjs')); const configFile = fixtures.path(path.join('options-as-flags', 'test-config.json')); const envFile = fixtures.path(path.join('options-as-flags', '.test.env')); +const onlyIfNodeOptionsSupport = { skip: process.config.variables.node_without_node_options }; + describe('getOptionsAsFlagsFromBinding', () => { it('should extract flags from command line arguments', async () => { const result = await spawnPromisified(process.execPath, [ @@ -28,7 +30,7 @@ describe('getOptionsAsFlagsFromBinding', () => { assert.strictEqual(flags.includes('--stack-trace-limit=512'), true); }); - it('should extract flags from NODE_OPTIONS environment variable', async () => { + it('should extract flags from NODE_OPTIONS environment variable', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--expose-internals', @@ -49,7 +51,7 @@ describe('getOptionsAsFlagsFromBinding', () => { assert.strictEqual(flags.includes('--no-warnings'), true); }); - it('should extract flags from config file', async () => { + it('should extract flags from config file', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--expose-internals', @@ -69,7 +71,7 @@ describe('getOptionsAsFlagsFromBinding', () => { assert.strictEqual(flags.includes('--no-warnings'), true); }); - it('should extract flags from config file and command line', async () => { + it('should extract flags from config file and command line', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--expose-internals', @@ -92,7 +94,7 @@ describe('getOptionsAsFlagsFromBinding', () => { assert.strictEqual(flags.includes('--test-isolation=none'), true); }); - it('should extract flags from .env file', async () => { + it('should extract flags from .env file', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--expose-internals', diff --git a/test/parallel/test-config-file.js b/test/parallel/test-config-file.js index 9ffc2a90bee9e1..c18cc0d45683b6 100644 --- a/test/parallel/test-config-file.js +++ b/test/parallel/test-config-file.js @@ -13,7 +13,14 @@ const { test, it, describe } = require('node:test'); const { chmodSync, writeFileSync, constants } = require('node:fs'); const { join } = require('node:path'); +const onlyIfNodeOptionsSupport = { skip: process.config.variables.node_without_node_options }; const onlyWithAmaro = { skip: !process.config.variables.node_use_amaro }; +const onlyWithAmaroAndNodeOptions = { + skip: !process.config.variables.node_use_amaro || process.config.variables.node_without_node_options, +}; +const onlyWithInspectorAndNodeOptions = { + skip: !process.features.inspector || process.config.variables.node_without_node_options, +}; test('should handle non existing json', async () => { const result = await spawnPromisified(process.execPath, [ @@ -51,7 +58,7 @@ test('should handle empty object json', async () => { assert.strictEqual(result.code, 0); }); -test('should parse boolean flag', onlyWithAmaro, async () => { +test('should parse boolean flag', onlyWithAmaroAndNodeOptions, async () => { const result = await spawnPromisified(process.execPath, [ '--experimental-config-file', fixtures.path('rc/transform-types.json'), @@ -62,7 +69,7 @@ test('should parse boolean flag', onlyWithAmaro, async () => { assert.strictEqual(result.code, 0); }); -test('should parse boolean flag defaulted to true', async () => { +test('should parse boolean flag defaulted to true', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--experimental-config-file', fixtures.path('rc/warnings-false.json'), @@ -85,7 +92,7 @@ test('should throw an error when a flag is declared twice', async () => { assert.strictEqual(result.code, 9); }); -test('should override env-file', onlyWithAmaro, async () => { +test('should override env-file', onlyWithAmaroAndNodeOptions, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--experimental-config-file', @@ -128,7 +135,7 @@ test('should not override CLI flags', onlyWithAmaro, async () => { assert.strictEqual(result.code, 1); }); -test('should parse array flag correctly', async () => { +test('should parse array flag correctly', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--experimental-config-file', @@ -152,7 +159,7 @@ test('should validate invalid array flag', async () => { assert.strictEqual(result.code, 9); }); -test('should validate array flag as string', async () => { +test('should validate array flag as string', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--experimental-config-file', @@ -188,7 +195,7 @@ test('should throw at flag not available in NODE_OPTIONS', async () => { assert.strictEqual(result.code, 9); }); -test('unsigned flag should be parsed correctly', async () => { +test('unsigned flag should be parsed correctly', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--experimental-config-file', @@ -225,7 +232,7 @@ test('v8 flag should not be allowed in config file', async () => { assert.strictEqual(result.code, 9); }); -test('string flag should be parsed correctly', async () => { +test('string flag should be parsed correctly', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--test', @@ -238,7 +245,7 @@ test('string flag should be parsed correctly', async () => { assert.strictEqual(result.code, 0); }); -test('host port flag should be parsed correctly', { skip: !process.features.inspector }, async () => { +test('host port flag should be parsed correctly', onlyWithInspectorAndNodeOptions, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--expose-internals', @@ -251,7 +258,7 @@ test('host port flag should be parsed correctly', { skip: !process.features.insp assert.strictEqual(result.code, 0); }); -test('--inspect=true should be parsed correctly', { skip: !process.features.inspector }, async () => { +test('--inspect=true should be parsed correctly', onlyWithInspectorAndNodeOptions, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--experimental-config-file', @@ -351,7 +358,7 @@ test('broken value in node_options', async () => { assert.strictEqual(result.code, 9); }); -test('should use node.config.json as default', async () => { +test('should use node.config.json as default', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--experimental-default-config-file', @@ -364,7 +371,7 @@ test('should use node.config.json as default', async () => { assert.strictEqual(result.code, 0); }); -test('should override node.config.json when specificied', async () => { +test('should override node.config.json when specificied', onlyIfNodeOptionsSupport, async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', '--experimental-default-config-file', diff --git a/test/parallel/test-max-old-space-size-percentage.js b/test/parallel/test-max-old-space-size-percentage.js index c5071d2f959ce3..88db60299e1526 100644 --- a/test/parallel/test-max-old-space-size-percentage.js +++ b/test/parallel/test-max-old-space-size-percentage.js @@ -44,6 +44,10 @@ invalidPercentages.forEach((input) => { assert.match(result.stderr.toString(), input[1]); }); +if (process.config.variables.node_without_node_options) { + common.skip('missing NODE_OPTIONS support'); +} + // Test NODE_OPTIONS with valid percentages validPercentages.forEach((input) => { const result = spawnSync(process.execPath, [], { diff --git a/test/parallel/test-permission-allow-child-process-cli.js b/test/parallel/test-permission-allow-child-process-cli.js index f2ec02f02f46f5..d82332ba3d5084 100644 --- a/test/parallel/test-permission-allow-child-process-cli.js +++ b/test/parallel/test-permission-allow-child-process-cli.js @@ -8,6 +8,9 @@ const { isMainThread } = require('worker_threads'); if (!isMainThread) { common.skip('This test only works on a main thread'); } +if (process.config.variables.node_without_node_options) { + common.skip('missing NODE_OPTIONS support'); +} const assert = require('assert'); const childProcess = require('child_process'); diff --git a/test/parallel/test-permission-child-process-inherit-flags.js b/test/parallel/test-permission-child-process-inherit-flags.js index fb1506bf274f43..36e89bfb1c7286 100644 --- a/test/parallel/test-permission-child-process-inherit-flags.js +++ b/test/parallel/test-permission-child-process-inherit-flags.js @@ -7,6 +7,9 @@ const { isMainThread } = require('worker_threads'); if (!isMainThread) { common.skip('This test only works on a main thread'); } +if (process.config.variables.node_without_node_options) { + common.skip('missing NODE_OPTIONS support'); +} const assert = require('assert'); const childProcess = require('child_process'); diff --git a/test/parallel/test-process-warnings.mjs b/test/parallel/test-process-warnings.mjs index 907681b25c4630..75e4986b817dc1 100644 --- a/test/parallel/test-process-warnings.mjs +++ b/test/parallel/test-process-warnings.mjs @@ -9,6 +9,8 @@ const dep1Message = /\(node:\d+\) \[DEP1\] DeprecationWarning/; const dep2Message = /\(node:\d+\) \[DEP2\] DeprecationWarning/; const experimentalWarningMessage = /\(node:\d+\) ExperimentalWarning/; +const onlyIfNodeOptionsSupport = { skip: process.config.variables.node_without_node_options }; + describe('process warnings', { concurrency: !process.env.TEST_PARALLEL }, () => { it('should emit all warnings by default', async () => { @@ -142,7 +144,7 @@ describe('process warnings', { concurrency: !process.env.TEST_PARALLEL }, () => assert.strictEqual(signal, null); }); - it('should be specifiable in NODE_OPTIONS', async () => { + it('should be specifiable in NODE_OPTIONS', onlyIfNodeOptionsSupport, async () => { const { stdout, stderr, code, signal } = await spawnPromisified(process.execPath, [ fixturePath, ], { diff --git a/test/parallel/test-unicode-node-options.js b/test/parallel/test-unicode-node-options.js index e5a40d118791d3..3f8cf46d986cd3 100644 --- a/test/parallel/test-unicode-node-options.js +++ b/test/parallel/test-unicode-node-options.js @@ -1,10 +1,14 @@ 'use strict'; // Flags: --expose-internals -require('../common'); +const common = require('../common'); const { getOptionValue } = require('internal/options'); const assert = require('assert'); const cp = require('child_process'); +if (process.config.variables.node_without_node_options) { + common.skip('missing NODE_OPTIONS support'); +} + const expected_redirect_value = 'foó'; if (process.argv.length === 2) { diff --git a/test/parallel/test-worker-execargv-invalid.js b/test/parallel/test-worker-execargv-invalid.js index be8ab0b8c423b7..06c33c678dbcb3 100644 --- a/test/parallel/test-worker-execargv-invalid.js +++ b/test/parallel/test-worker-execargv-invalid.js @@ -1,9 +1,13 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const { Worker } = require('worker_threads'); +if (process.config.variables.node_without_node_options) { + common.skip('missing NODE_OPTIONS support'); +} + { const expectedErr = { code: 'ERR_INVALID_ARG_TYPE', diff --git a/test/parallel/test-worker-node-options.js b/test/parallel/test-worker-node-options.js index 7a26154e2f4800..73f71b57ac7607 100644 --- a/test/parallel/test-worker-node-options.js +++ b/test/parallel/test-worker-node-options.js @@ -1,10 +1,15 @@ 'use strict'; -require('../common'); +const common = require('../common'); const { spawnSyncAndExitWithoutError, spawnSyncAndAssert, } = require('../common/child_process'); + +if (process.config.variables.node_without_node_options) { + common.skip('missing NODE_OPTIONS support'); +} + const fixtures = require('../common/fixtures'); spawnSyncAndExitWithoutError( process.execPath, diff --git a/tools/nix/sharedLibDeps.nix b/tools/nix/sharedLibDeps.nix index ba006fc9ce39ef..7142c4c766588c 100644 --- a/tools/nix/sharedLibDeps.nix +++ b/tools/nix/sharedLibDeps.nix @@ -1,5 +1,7 @@ { pkgs ? import ./pkgs.nix { }, + withSQLite ? true, + withSSL ? true, withTemporal ? false, }: { @@ -12,7 +14,6 @@ ngtcp2 simdjson simdutf - sqlite uvwasi zlib zstd @@ -28,6 +29,11 @@ }) ]; }; +} +// (pkgs.lib.optionalAttrs withSQLite { + inherit (pkgs) sqlite; +}) +// (pkgs.lib.optionalAttrs withSSL { openssl = pkgs.openssl.overrideAttrs (old: { version = "3.5.4"; src = pkgs.fetchurl { @@ -45,7 +51,7 @@ "dev" ]; }); -} +}) // (pkgs.lib.optionalAttrs withTemporal { inherit (pkgs) temporal_capi; })