diff --git a/configure b/configure index 7272e19261b238..1eaf26c0955575 100755 --- a/configure +++ b/configure @@ -444,6 +444,11 @@ parser.add_option('--without-ssl', dest='without_ssl', help='build without SSL (disables crypto, https, inspector, etc.)') +parser.add_option('--without-node-options', + action='store_true', + dest='without_node_options', + help='build without NODE_OPTIONS support') + parser.add_option('--xcode', action='store_true', dest='use_xcode', @@ -975,6 +980,9 @@ def configure_openssl(o): o['variables']['openssl_no_asm'] = 1 if options.openssl_no_asm else 0 if options.use_openssl_ca_store: o['defines'] += ['NODE_OPENSSL_CERT_STORE'] + o['variables']['node_without_node_options'] = b(options.without_node_options) + if options.without_node_options: + o['defines'] += ['NODE_WITHOUT_NODE_OPTIONS'] if options.openssl_fips: o['variables']['openssl_fips'] = options.openssl_fips fips_dir = os.path.join(root_dir, 'deps', 'openssl', 'fips') diff --git a/doc/api/cli.md b/doc/api/cli.md index 650e0cdbdb607e..56c4ea941ca65b 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -122,6 +122,16 @@ added: v6.0.0 Print stack traces for process warnings (including deprecations). +### `--redirect-warnings=file` + + +Write process warnings to the given file instead of printing to stderr. The +file will be created if it does not exist, and will be appended to if it does. +If an error occurs while attempting to write the warning to the file, the +warning will be written to stderr instead. + ### `--trace-sync-io` + +`options...` are interpreted as if they had been specified on the command line +before the actual command line (so they can be overriden). Node will exit with +an error if an option that is not allowed in the environment is used, such as +`-p` or a script file. + +Node options that are allowed are: +- `--enable-fips` +- `--force-fips` +- `--icu-data-dir` +- `--debug-brk` +- `--debug-port` +- `--debug` +- `--napi-modules` +- `--no-deprecation` +- `--no-warnings` +- `--openssl-config` +- `--redirect-warnings` +- `--require`, `-r` +- `--throw-deprecation` +- `--tls-cipher-list` +- `--trace-deprecation` +- `--trace-sync-io` +- `--trace-warnings` +- `--track-heap-objects` +- `--use-bundled-ca` +- `--use-openssl-ca` +- `--v8-pool-size` +- `--zero-fill-buffers` + +V8 options that are allowed are: +- `--abort-on-uncaught-exception` +- `--max_old_space_size` + + ### `NODE_REPL_HISTORY=file` + +When set, process warnings will be emitted to the given file instead of +printing to stderr. The file will be created if it does not exist, and will be +appended to if it does. If an error occurs while attempting to write the +warning to the file, the warning will be written to stderr instead. This is +equivalent to using the `--redirect-warnings=file` command-line flag. + [emit_warning]: process.html#process_process_emitwarning_warning_name_ctor [Buffer]: buffer.html#buffer_buffer [debugger]: debugger.html diff --git a/doc/node.1 b/doc/node.1 index 2167f245216e04..1e1877376dc3aa 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -112,6 +112,10 @@ Silence all process warnings (including deprecations). .BR \-\-trace\-warnings Print stack traces for process warnings (including deprecations). +.TP +.BR \-\-redirect\-warnings=\fIfile\fR +Write process warnings to the given file instead of printing to stderr. + .TP .BR \-\-trace\-sync\-io Print a stack trace whenever synchronous I/O is detected after the first turn @@ -222,6 +226,13 @@ with small\-icu support. .BR NODE_NO_WARNINGS =\fI1\fR When set to \fI1\fR, process warnings are silenced. +.TP +.BR NODE_OPTIONS =\fIoptions...\fR +\fBoptions...\fR are interpreted as if they had been specified on the command +line before the actual command line (so they can be overriden). Node will exit +with an error if an option that is not allowed in the environment is used, such +as \fB-p\fR or a script file. + .TP .BR NODE_REPL_HISTORY =\fIfile\fR Path to the file used to store the persistent REPL history. The default path @@ -255,6 +266,12 @@ containing trusted certificates. If \fB\-\-use\-openssl\-ca\fR is enabled, this overrides and sets OpenSSL's file containing trusted certificates. +.TP +.BR NODE_REDIRECT_WARNINGS=\fIfile\fR +Write process warnings to the given file instead of printing to stderr. +(equivalent to using the \-\-redirect\-warnings=\fIfile\fR command-line +argument). + .SH BUGS Bugs are tracked in GitHub Issues: .ur https://github.com/nodejs/node/issues diff --git a/lib/internal/process/warning.js b/lib/internal/process/warning.js index bce470cd1c50ac..dd9aa484b7ea21 100644 --- a/lib/internal/process/warning.js +++ b/lib/internal/process/warning.js @@ -4,10 +4,81 @@ const traceWarnings = process.traceProcessWarnings; const noDeprecation = process.noDeprecation; const traceDeprecation = process.traceDeprecation; const throwDeprecation = process.throwDeprecation; +const config = process.binding('config'); const prefix = `(${process.release.name}:${process.pid}) `; exports.setup = setupProcessWarnings; +var fs; +var cachedFd; +var acquiringFd = false; +function nop() {} + +function lazyFs() { + if (!fs) + fs = require('fs'); + return fs; +} + +function writeOut(message) { + if (console && typeof console.error === 'function') + return console.error(message); + process._rawDebug(message); +} + +function onClose(fd) { + return function() { + lazyFs().close(fd, nop); + }; +} + +function onOpen(cb) { + return function(err, fd) { + acquiringFd = false; + if (fd !== undefined) { + cachedFd = fd; + process.on('exit', onClose(fd)); + } + cb(err, fd); + process.emit('_node_warning_fd_acquired', err, fd); + }; +} + +function onAcquired(message) { + // make a best effort attempt at writing the message + // to the fd. Errors are ignored at this point. + return function(err, fd) { + if (err) + return writeOut(message); + lazyFs().appendFile(fd, `${message}\n`, nop); + }; +} + +function acquireFd(cb) { + if (cachedFd === undefined && !acquiringFd) { + acquiringFd = true; + lazyFs().open(config.warningFile, 'a', onOpen(cb)); + } else if (cachedFd !== undefined && !acquiringFd) { + cb(null, cachedFd); + } else { + process.once('_node_warning_fd_acquired', cb); + } +} + +function output(message) { + if (typeof config.warningFile === 'string') { + acquireFd(onAcquired(message)); + return; + } + writeOut(message); +} + +function doEmitWarning(warning) { + return function() { + process.emit('warning', warning); + }; +} + function setupProcessWarnings() { if (!process.noProcessWarnings && process.env.NODE_NO_WARNINGS !== '1') { process.on('warning', (warning) => { @@ -21,7 +92,7 @@ function setupProcessWarnings() { var toString = warning.toString; if (typeof toString !== 'function') toString = Error.prototype.toString; - console.error(`${prefix}${toString.apply(warning)}`); + output(`${prefix}${toString.apply(warning)}`); } }); } @@ -44,6 +115,6 @@ function setupProcessWarnings() { if (throwDeprecation && warning.name === 'DeprecationWarning') throw warning; else - process.nextTick(() => process.emit('warning', warning)); + process.nextTick(doEmitWarning(warning)); }; } diff --git a/src/node.cc b/src/node.cc index def8e60b42c236..882c72a67eb330 100644 --- a/src/node.cc +++ b/src/node.cc @@ -142,8 +142,7 @@ static bool throw_deprecation = false; static bool trace_sync_io = false; static bool track_heap_objects = false; static const char* eval_string = nullptr; -static unsigned int preload_module_count = 0; -static const char** preload_modules = nullptr; +static std::vector preload_modules; #if HAVE_INSPECTOR static bool use_inspector = false; #else @@ -199,12 +198,16 @@ bool trace_warnings = false; // that is used by lib/module.js bool config_preserve_symlinks = false; + // Set in node.cc by ParseArgs when --expose-internals or --expose_internals is // used. // Used in node_config.cc to set a constant on process.binding('config') // that is used by lib/internal/bootstrap_node.js bool config_expose_internals = false; +// Set in node.cc by ParseArgs when --redirect-warnings= is used. +std::string config_warning_file; // NOLINT(runtime/string) + // process-relative uptime base, initialized at start-up static double prog_start_time; static bool debugger_running; @@ -3324,21 +3327,18 @@ void SetupProcessObject(Environment* env, } // -r, --require - if (preload_module_count) { - CHECK(preload_modules); + if (!preload_modules.empty()) { Local array = Array::New(env->isolate()); - for (unsigned int i = 0; i < preload_module_count; ++i) { + for (unsigned int i = 0; i < preload_modules.size(); ++i) { Local module = String::NewFromUtf8(env->isolate(), - preload_modules[i]); + preload_modules[i].c_str()); array->Set(i, module); } READONLY_PROPERTY(process, "_preload_modules", array); - delete[] preload_modules; - preload_modules = nullptr; - preload_module_count = 0; + preload_modules.clear(); } // --no-deprecation @@ -3701,6 +3701,8 @@ static void PrintHelp() { "function is used\n" " --no-warnings silence all process warnings\n" " --trace-warnings show stack traces on process warnings\n" + " --redirect-warnings=path\n" + " write warnings to path instead of stderr\n" " --trace-sync-io show stack trace when use of sync IO\n" " is detected after the first tick\n" " --track-heap-objects track heap object allocations for heap " @@ -3757,6 +3759,9 @@ static void PrintHelp() { #endif #endif "NODE_NO_WARNINGS set to 1 to silence process warnings\n" +#if !defined(NODE_WITHOUT_NODE_OPTIONS) + "NODE_OPTIONS set CLI options in the environment\n" +#endif #ifdef _WIN32 "NODE_PATH ';'-separated list of directories\n" #else @@ -3765,11 +3770,62 @@ static void PrintHelp() { " prefixed to the module search path\n" "NODE_REPL_HISTORY path to the persistent REPL history file\n" "OPENSSL_CONF load OpenSSL configuration from file\n" + "NODE_REDIRECT_WARNINGS write warnings to path instead of stderr\n" "\n" "Documentation can be found at https://nodejs.org/\n"); } +static void CheckIfAllowedInEnv(const char* exe, bool is_env, + const char* arg) { + if (!is_env) + return; + + // Find the arg prefix when its --some_arg=val + const char* eq = strchr(arg, '='); + size_t arglen = eq ? eq - arg : strlen(arg); + + static const char* whitelist[] = { + // Node options, sorted in `node --help` order for ease of comparison. + "--require", "-r", + "--debug", + "--debug-brk", + "--debug-port", + "--no-deprecation", + "--trace-deprecation", + "--throw-deprecation", + "--no-warnings", + "--napi-modules", + "--trace-warnings", + "--redirect-warnings", + "--trace-sync-io", + "--track-heap-objects", + "--zero-fill-buffers", + "--v8-pool-size", + "--tls-cipher-list", + "--use-bundled-ca", + "--use-openssl-ca", + "--enable-fips", + "--force-fips", + "--openssl-config", + "--icu-data-dir", + + // V8 options + "--abort-on-uncaught-exception", + "--max_old_space_size", + }; + + for (unsigned i = 0; i < arraysize(whitelist); i++) { + const char* allowed = whitelist[i]; + if (strlen(allowed) == arglen && strncmp(allowed, arg, arglen) == 0) + return; + } + + fprintf(stderr, "%s: %s is not allowed in NODE_OPTIONS\n", exe, arg); + exit(9); +} + + // Parse command line arguments. // // argv is modified in place. exec_argv and v8_argv are out arguments that @@ -3786,18 +3842,17 @@ static void ParseArgs(int* argc, int* exec_argc, const char*** exec_argv, int* v8_argc, - const char*** v8_argv) { + const char*** v8_argv, + bool is_env) { const unsigned int nargs = static_cast(*argc); const char** new_exec_argv = new const char*[nargs]; const char** new_v8_argv = new const char*[nargs]; const char** new_argv = new const char*[nargs]; - const char** local_preload_modules = new const char*[nargs]; for (unsigned int i = 0; i < nargs; ++i) { new_exec_argv[i] = nullptr; new_v8_argv[i] = nullptr; new_argv[i] = nullptr; - local_preload_modules[i] = nullptr; } // exec_argv starts with the first option, the other two start with argv[0]. @@ -3813,6 +3868,8 @@ static void ParseArgs(int* argc, const char* const arg = argv[index]; unsigned int args_consumed = 1; + CheckIfAllowedInEnv(argv[0], is_env, arg); + if (ParseDebugOpt(arg)) { // Done, consumed by ParseDebugOpt(). } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { @@ -3855,7 +3912,7 @@ static void ParseArgs(int* argc, exit(9); } args_consumed += 1; - local_preload_modules[preload_module_count++] = module; + preload_modules.push_back(module); } else if (strcmp(arg, "--check") == 0 || strcmp(arg, "-c") == 0) { syntax_check_only = true; } else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) { @@ -3866,6 +3923,8 @@ static void ParseArgs(int* argc, no_process_warnings = true; } else if (strcmp(arg, "--trace-warnings") == 0) { trace_warnings = true; + } else if (strncmp(arg, "--redirect-warnings=", 20) == 0) { + config_warning_file = arg + 20; } else if (strcmp(arg, "--trace-deprecation") == 0) { trace_deprecation = true; } else if (strcmp(arg, "--trace-sync-io") == 0) { @@ -3931,6 +3990,13 @@ static void ParseArgs(int* argc, // Copy remaining arguments. const unsigned int args_left = nargs - index; + + if (is_env && args_left) { + fprintf(stderr, "%s: %s is not supported in NODE_OPTIONS\n", + argv[0], argv[index]); + exit(9); + } + memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv)); new_argc += args_left; @@ -3943,16 +4009,6 @@ static void ParseArgs(int* argc, memcpy(argv, new_argv, new_argc * sizeof(*argv)); delete[] new_argv; *argc = static_cast(new_argc); - - // Copy the preload_modules from the local array to an appropriately sized - // global array. - if (preload_module_count > 0) { - CHECK(!preload_modules); - preload_modules = new const char*[preload_module_count]; - memcpy(preload_modules, local_preload_modules, - preload_module_count * sizeof(*preload_modules)); - } - delete[] local_preload_modules; } @@ -4374,6 +4430,54 @@ inline void PlatformInit() { } +void ProcessArgv(int* argc, + const char** argv, + int* exec_argc, + const char*** exec_argv, + bool is_env = false) { + // Parse a few arguments which are specific to Node. + int v8_argc; + const char** v8_argv; + ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv, is_env); + + // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler + // manually? That would give us a little more control over its runtime + // behavior but it could also interfere with the user's intentions in ways + // we fail to anticipate. Dillema. + for (int i = 1; i < v8_argc; ++i) { + if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) { + v8_is_profiling = true; + break; + } + } + +#ifdef __POSIX__ + // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the + // performance penalty of frequent EINTR wakeups when the profiler is running. + // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users. + if (v8_is_profiling) { + uv_loop_configure(uv_default_loop(), UV_LOOP_BLOCK_SIGNAL, SIGPROF); + } +#endif + + // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify + // the argv array or the elements it points to. + if (v8_argc > 1) + V8::SetFlagsFromCommandLine(&v8_argc, const_cast(v8_argv), true); + + // Anything that's still in v8_argv is not a V8 or a node option. + for (int i = 1; i < v8_argc; i++) { + fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]); + } + delete[] v8_argv; + v8_argv = nullptr; + + if (v8_argc > 1) { + exit(9); + } +} + + void Init(int* argc, const char** argv, int* exec_argc, @@ -4401,31 +4505,39 @@ void Init(int* argc, if (openssl_config.empty()) SafeGetenv("OPENSSL_CONF", &openssl_config); - // Parse a few arguments which are specific to Node. - int v8_argc; - const char** v8_argv; - ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv); - - // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler - // manually? That would give us a little more control over its runtime - // behavior but it could also interfere with the user's intentions in ways - // we fail to anticipate. Dillema. - for (int i = 1; i < v8_argc; ++i) { - if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) { - v8_is_profiling = true; - break; + if (config_warning_file.empty()) + SafeGetenv("NODE_REDIRECT_WARNINGS", &config_warning_file); + +#if !defined(NODE_WITHOUT_NODE_OPTIONS) + std::string node_options; + if (SafeGetenv("NODE_OPTIONS", &node_options)) { + // Smallest tokens are 2-chars (a not space and a space), plus 2 extra + // pointers, for the prepended executable name, and appended NULL pointer. + size_t max_len = 2 + (node_options.length() + 1) / 2; + const char** argv_from_env = new const char*[max_len]; + int argc_from_env = 0; + // [0] is expected to be the program name, fill it in from the real argv. + argv_from_env[argc_from_env++] = argv[0]; + + char* cstr = strdup(node_options.c_str()); + char* initptr = cstr; + char* token; + while ((token = strtok(initptr, " "))) { // NOLINT(runtime/threadsafe_fn) + initptr = nullptr; + argv_from_env[argc_from_env++] = token; } - } - -#ifdef __POSIX__ - // Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the - // performance penalty of frequent EINTR wakeups when the profiler is running. - // Only do this for v8.log profiling, as it breaks v8::CpuProfiler users. - if (v8_is_profiling) { - uv_loop_configure(uv_default_loop(), UV_LOOP_BLOCK_SIGNAL, SIGPROF); + argv_from_env[argc_from_env] = nullptr; + int exec_argc_; + const char** exec_argv_ = nullptr; + ProcessArgv(&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true); + delete[] exec_argv_; + delete[] argv_from_env; + free(cstr); } #endif + ProcessArgv(argc, argv, exec_argc, exec_argv); + #if defined(NODE_HAVE_I18N_SUPPORT) // If the parameter isn't given, use the env variable. if (icu_data_dir.empty()) @@ -4437,21 +4549,6 @@ void Init(int* argc, "(check NODE_ICU_DATA or --icu-data-dir parameters)\n"); } #endif - // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify - // the argv array or the elements it points to. - if (v8_argc > 1) - V8::SetFlagsFromCommandLine(&v8_argc, const_cast(v8_argv), true); - - // Anything that's still in v8_argv is not a V8 or a node option. - for (int i = 1; i < v8_argc; i++) { - fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]); - } - delete[] v8_argv; - v8_argv = nullptr; - - if (v8_argc > 1) { - exit(9); - } // Unconditionally force typed arrays to allocate outside the v8 heap. This // is to prevent memory pointers from being moved around that are returned by diff --git a/src/node_config.cc b/src/node_config.cc index 4408bc3cf33ffc..0e6184709642ea 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -12,6 +12,7 @@ using v8::Context; using v8::Local; using v8::Object; using v8::ReadOnly; +using v8::String; using v8::Value; // The config binding is used to provide an internal view of compile or runtime @@ -47,6 +48,16 @@ void InitConfig(Local target, if (config_expose_internals) READONLY_BOOLEAN_PROPERTY("exposeInternals"); + + if (!config_warning_file.empty()) { + Local name = OneByteString(env->isolate(), "warningFile"); + Local value = String::NewFromUtf8(env->isolate(), + config_warning_file.data(), + v8::NewStringType::kNormal, + config_warning_file.size()) + .ToLocalChecked(); + target->DefineOwnProperty(env->context(), name, value).FromJust(); + } } // InitConfig } // namespace node diff --git a/src/node_internals.h b/src/node_internals.h index e93244a94c6927..adcb7f835a3451 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -49,6 +49,11 @@ extern bool config_preserve_symlinks; // that is used by lib/internal/bootstrap_node.js extern bool config_expose_internals; +// Set in node.cc by ParseArgs when --redirect-warnings= is used. +// Used to redirect warning output to a file rather than sending +// it to stderr. +extern std::string config_warning_file; + // Forward declaration class Environment; diff --git a/test/parallel/test-cli-node-options.js b/test/parallel/test-cli-node-options.js new file mode 100644 index 00000000000000..648a99e8620a6f --- /dev/null +++ b/test/parallel/test-cli-node-options.js @@ -0,0 +1,80 @@ +'use strict'; +const common = require('../common'); +if (process.config.variables.node_without_node_options) + return common.skip('missing NODE_OPTIONS support'); + +// Test options specified by env variable. + +const assert = require('assert'); +const exec = require('child_process').execFile; + +common.refreshTmpDir(); +process.chdir(common.tmpDir); + +disallow('--version'); +disallow('-v'); +disallow('--help'); +disallow('-h'); +disallow('--eval'); +disallow('-e'); +disallow('--print'); +disallow('-p'); +disallow('-pe'); +disallow('--check'); +disallow('-c'); +disallow('--interactive'); +disallow('-i'); +disallow('--v8-options'); +disallow('--'); + +function disallow(opt) { + const options = {env: {NODE_OPTIONS: opt}}; + exec(process.execPath, options, common.mustCall(function(err) { + const message = err.message.split(/\r?\n/)[1]; + const expect = process.execPath + ': ' + opt + + ' is not allowed in NODE_OPTIONS'; + + assert.strictEqual(err.code, 9); + assert.strictEqual(message, expect); + })); +} + +const printA = require.resolve('../fixtures/printA.js'); + +expect(`-r ${printA}`, 'A\nB\n'); +expect('--abort-on-uncaught-exception', 'B\n'); +expect('--no-deprecation', 'B\n'); +expect('--no-warnings', 'B\n'); +expect('--trace-warnings', 'B\n'); +expect('--trace-deprecation', 'B\n'); +expect('--trace-sync-io', 'B\n'); +expect('--track-heap-objects', 'B\n'); +expect('--throw-deprecation', 'B\n'); +expect('--zero-fill-buffers', 'B\n'); +expect('--v8-pool-size=10', 'B\n'); +if (common.hasCrypto) { + expect('--use-openssl-ca', 'B\n'); + expect('--use-bundled-ca', 'B\n'); + expect('--openssl-config=_ossl_cfg', 'B\n'); +} +expect('--icu-data-dir=_d', 'B\n'); + + // V8 options +expect('--max_old_space_size=0', 'B\n'); + +function expect(opt, want) { + const printB = require.resolve('../fixtures/printB.js'); + const argv = [printB]; + const opts = { + env: {NODE_OPTIONS: opt}, + maxBuffer: 1000000000, + }; + exec(process.execPath, argv, opts, common.mustCall(function(err, stdout) { + assert.ifError(err); + if (!RegExp(want).test(stdout)) { + console.error('For %j, failed to find %j in: <\n%s\n>', + opt, expect, stdout); + assert(false, 'Expected ' + expect); + } + })); +} diff --git a/test/parallel/test-process-redirect-warnings-env.js b/test/parallel/test-process-redirect-warnings-env.js new file mode 100644 index 00000000000000..86942dc9e88e11 --- /dev/null +++ b/test/parallel/test-process-redirect-warnings-env.js @@ -0,0 +1,25 @@ +'use strict'; + +// Tests the NODE_REDIRECT_WARNINGS environment variable by spawning +// a new child node process that emits a warning into a temporary +// warnings file. Once the process completes, the warning file is +// opened and the contents are validated + +const common = require('../common'); +const fs = require('fs'); +const fork = require('child_process').fork; +const path = require('path'); +const assert = require('assert'); + +common.refreshTmpDir(); + +const warnmod = require.resolve(common.fixturesDir + '/warnings.js'); +const warnpath = path.join(common.tmpDir, 'warnings.txt'); + +fork(warnmod, {env: {NODE_REDIRECT_WARNINGS: warnpath}}) + .on('exit', common.mustCall(() => { + fs.readFile(warnpath, 'utf8', common.mustCall((err, data) => { + assert.ifError(err); + assert(/\(node:\d+\) Warning: a bad practice warning/.test(data)); + })); + })); diff --git a/test/parallel/test-process-redirect-warnings.js b/test/parallel/test-process-redirect-warnings.js new file mode 100644 index 00000000000000..b798e41bf6b5e4 --- /dev/null +++ b/test/parallel/test-process-redirect-warnings.js @@ -0,0 +1,25 @@ +'use strict'; + +// Tests the --redirect-warnings command line flag by spawning +// a new child node process that emits a warning into a temporary +// warnings file. Once the process completes, the warning file is +// opened and the contents are validated + +const common = require('../common'); +const fs = require('fs'); +const fork = require('child_process').fork; +const path = require('path'); +const assert = require('assert'); + +common.refreshTmpDir(); + +const warnmod = require.resolve(common.fixturesDir + '/warnings.js'); +const warnpath = path.join(common.tmpDir, 'warnings.txt'); + +fork(warnmod, {execArgv: [`--redirect-warnings=${warnpath}`]}) + .on('exit', common.mustCall(() => { + fs.readFile(warnpath, 'utf8', common.mustCall((err, data) => { + assert.ifError(err); + assert(/\(node:\d+\) Warning: a bad practice warning/.test(data)); + })); + })); diff --git a/vcbuild.bat b/vcbuild.bat index 8fbb8d365f76e4..502f1d0ec2eab2 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -87,7 +87,8 @@ if /i "%1"=="download-all" set download_arg="--download=all"&goto arg-ok if /i "%1"=="ignore-flaky" set test_args=%test_args% --flaky-tests=dontcare&goto arg-ok if /i "%1"=="enable-vtune" set enable_vtune_arg=1&goto arg-ok if /i "%1"=="dll" set dll=1&goto arg-ok -if /i "%1"=="static" set enable_static=1&goto arg-ok +if /i "%1"=="static" set enable_static=1&goto arg-ok +if /i "%1"=="no-NODE-OPTIONS" set no_NODE_OPTIONS=1&goto arg-ok echo Error: invalid command line option `%1`. exit /b 1 @@ -120,6 +121,7 @@ if defined download_arg set configure_flags=%configure_flags% %download_arg% if defined enable_vtune_arg set configure_flags=%configure_flags% --enable-vtune-profiling if defined dll set configure_flags=%configure_flags% --shared if defined enable_static set configure_flags=%configure_flags% --enable-static +if defined no_NODE_OPTIONS set configure_flags=%configure_flags% --without-node-options if "%i18n_arg%"=="full-icu" set configure_flags=%configure_flags% --with-intl=full-icu if "%i18n_arg%"=="small-icu" set configure_flags=%configure_flags% --with-intl=small-icu @@ -414,7 +416,7 @@ echo Failed to create vc project files. goto exit :help -echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-inspector/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [small-icu/full-icu/without-intl] [nobuild] [nosign] [x86/x64] [vc2015] [download-all] [enable-vtune] [lint/lint-ci] +echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-inspector/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [small-icu/full-icu/without-intl] [nobuild] [nosign] [x86/x64] [vc2015] [download-all] [enable-vtune] [lint/lint-ci] [no-NODE-OPTIONS] echo Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build