From 81ff92f941e2e2d57ae2a0228b51a9e32494a723 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 11:45:08 -0700 Subject: [PATCH 01/21] module: new switch; support multiple #! args Interpreters invoked via #! only see one argument on some environments (Linux, Cygwin) This pull request enables a script to pass multiple arguments,such as v8 options, to node using a technique developed for other scripting languages (perl and ruby). Detailed justification and analysis in the attached file doc/Minux-X-Switch-Proposal.md This pull request *DOES NOT* make any changes to the public API of modules, which is a locked subsystem. --- doc/Minus-X-Switch-Proposal.md | 219 ++++++++++++++++++++++ test/fixtures/needs-strip | 5 + test/simple/test-args-strip.js | 47 +++++ test/simple/test-module-removeprologue.js | 34 ++++ 4 files changed, 305 insertions(+) create mode 100644 doc/Minus-X-Switch-Proposal.md create mode 100644 test/fixtures/needs-strip create mode 100644 test/simple/test-args-strip.js create mode 100644 test/simple/test-module-removeprologue.js diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md new file mode 100644 index 000000000000..4e21bf359036 --- /dev/null +++ b/doc/Minus-X-Switch-Proposal.md @@ -0,0 +1,219 @@ +# Summary + + TL;DR - node should have a -x switch to allow script authors to + pass multiple v8 option arguments to the node executable. + +*Note that this file (Minus-X-Switch-Proposal.md) is not intended to be a permanent part of the +codebase, and it will be removed before the final pull request is submitted.* + +# Motivation + + Node 0.12 will have a different version of v8 than 0.10. Some scripts and +modules may need to confugure v8 to behave in specific ways. V8 is configured +by passing command-line options to the node executable. + + There is currently no portable way for a script to pass options to the node executable. + +# Background + +## Shebang Interpretation + + When the operating system loads a script file, it checks for the characters +'#' and '!' at the beginning of the buffer and, if present, runs the script +in the named interpreter. + + This is an operating-system behavior, and it differs between Linux, *BSD, +OSX, and Windows. (To some extent, shells can facilitate or emulate this, +e.g., #! works under cygwin.) + + In particular, on *BSD and OSX, the #! command line behavior supports multiple +arguments, while on Linux and Cygwin only one argument is supported. So a script +named 'script' with the following contents is interpreted differently on different +systems: + + #!/bin/interpreter -1 -2 -3 + # .. more script + + $ ./script + + OSX, FreeBSD: + argv[0] = "/bin/interpreter" + argv[1] = "-1" + argv[2] = "-2" + argv[3] = "-3" + argv[4] = "./script" + + Linux, Cygwin + argv[0] = "/bin/interpreter" + argv[1] = "-1 -2 -3" + argv[2] = "./script" + + The situation on Windows is worse; on the OS level, Windows uses the file extension +to choose the interpreter. #! is not supported either in CMD.EXE or PowerShell. + +## Other Script Interpreters (perl, ruby) + +In order to support passing multiple arguments to the interpreter on all +systems, a common idiom has been developed. This is to supply the "-x" +switch to the interpreter, which causes it to ignore lines from the script +file until it finds a line which both: + + 1. starts with #! + 1. contains the interpreter's name. + +This works around the operating system limitation on the number of arguments +that can be passed to the interpreter. It is typically necessary to write the +header of the script in a shell language such as bash, and invoke the desired +interpreter with 'exec': + + + #!/usr/bin/env bash + exec /usr/bin/env perl -x $0 "$@" + #!perl -s -p -i.orig + # ... rest of script follows + +Ruby uses the same convention as perl. + + #!/usr/bin/env bash + exec /usr/bin/env ruby --disable-gems -x "$0" $* + #!ruby + +# Proposal + + I propose to add a new command-line switch to node (-x) following the same semantics as +used by perl and ruby, namely: + +1. The -x switch causes node to skip everything in the first input file/stream up to +a line starting with '#!' and containing 'node' (as in ruby and perl) + +1. The -x switch has an optional argument which is interpreted as a directory; if supplied, +the process will cwd into this directory before (as in ruby and perl) + + Script authors can control the arguments passed to node using a similar +syntax to that used by ruby and perl: + + + #!/usr/bin/env bash + exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" + #!node + +# Implementation + +Currently in node internals, the v8 engine is configured by the command line arguments +and initialized before the main environment is created. The environment invokes +the main function, which loads and runs the script file using Module.runMain(). + +Ideally it would be possible for the node executable to read command-line options from the +script in one pass. This is not practically possible since the initial script parsing is +done in javascript, with v8 already configured and initialized. Therefore I will assume that +the first pass will be handled by bash or another shell, and when node is starting up it +has already processed the supplied command-line arguments. + +The remaining problem is to ignore the preamble, and parse the script as a valid +javascript file. An example preamble is reproduced below. + + + #!/usr/bin/env bash + exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" + #!node + // javascript beginning here + +## Command Line Option + +Three sections are added to src/node.cc to support the `-x` command line option and +expose it on the process object as `process._stripUntilShebangNode` + +## Change to module parsing + +Some changes are necessary in lib/module.js. The API for this module is Locked, so these +changes do not alter the previous behavior of published API functions. The changes are: + +* Introduce a function `Module._removePrologue(content, doStrip)` + + In the default case, when doStrip is false, this function removes the contents of +the first '#!' line of the file, leaving a newline + + When the second argument is truthy, `_removePrologue` splits the content +into lines and replaces each line with an empty string until it sees a line which both +begins with '#!' and contains 'node'. The lines are joined with '\n' and returned. + +* Call `Module._removePrologue` from `Module.prototype._compile` + + In `Module.prototype._compile`, we detect whether the -x switch is +set and whether we are compiling the main module (`self.id === '.'`). +This result is stored in a boolean, doSpecialStrip, for clarity. + + `Module._removePrologue` is then invoked, passing the file contents and +the value doSpecialStrip. + +# Alternatives + +It is possible to work around the single-argument limit in several other ways. + +## Alias + +Instead of invoking a script directly, an alias can be configured in the user's shell: + + + alias foo='/usr/bin/env node --harmony /path/to/foo' + +This accomplishes the same effect as if the file 'foo' were set up using +a #!shell, exec, and #!node invocation. + +Using an alias has the advantage of requiring one fewer exec(); instead of executing +a new shell instance which exec's node, node is exec'ed directly with the correct +parameters. + +The disadvantage of using an alias is an extra layer of indirection and configuration. +The user's shell must support aliases; the alias must be configured in the user's shell +startup file; in order to change the command-line options passed to node it is necessary to +both change the alias and reload the shell's configuration file, etc. + +The most prominent disadvantage of aliases is that some processes (e.g., cron jobs) may +run with a shell that does not support aliases, or it may not be possible for the user to +configure that shell's environment + +## Wrapper Script + +A wrapper script or shim can be used to invoke a node script. The wrapper script +has full control over the node command line and can pass any arguments that are desired. +For example: + + file foo: + #!/usr/bin/env bash + /usr/bin/env node --harmony ./foo.js "$@" + + file foo.js + // foo.js (needs --harmony) + // more js code + +This proposal would allow a node script to serve as its own wrapper script. + +# References + +Shebang history and details +http://www.in-ulm.de/~mascheck/various/shebang/ + +Shebang FAQ +http://homepages.cwi.nl/~aeb/std/hashexclam-1.html#ss1.4 + +Wikipedia Shebang_(Unix) +https://en.wikipedia.org/wiki/Shebang_(Unix) + +Original Patch submitted to Linux Kernel Mailing list (2004), rejected +https://lkml.org/lkml/2004/2/16/74 + +FreeBSD mailing list discussion of multiple argument support +http://unix.derkeiler.com/Mailing-Lists/FreeBSD/arch/2005-02/0039.html + +Linux Kernel source binfmt_script.c (implements #! processing, one argument hardcoded) +https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c + +Example of need for -x switch (ruby) +https://github.com/garybernhardt/selecta/blob/master/selecta + +Perl Security - switches on the #! line +http://perldoc.perl.org/perlsec.html#Switches-On-the-%22%23!%22-Line + +Python - How do I make python scripts executable? (Windows NT) +http://effbot.org/pyfaq/how-do-i-make-python-scripts-executable.htm diff --git a/test/fixtures/needs-strip b/test/fixtures/needs-strip new file mode 100644 index 000000000000..3740913ac9fe --- /dev/null +++ b/test/fixtures/needs-strip @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +exec /usr/bin/env node -x --no-deprecation $0 "$@" +#!node +// javascript code begins here +console.log("ok"); \ No newline at end of file diff --git a/test/simple/test-args-strip.js b/test/simple/test-args-strip.js new file mode 100644 index 000000000000..6970bcc1bdee --- /dev/null +++ b/test/simple/test-args-strip.js @@ -0,0 +1,47 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var path = require('path'); + +var spawn = require('child_process').spawn; +var args = ['-x', path.join(common.fixturesDir, 'needs-strip')]; +var child = spawn(process.execPath, args); + +var expectOut = /^ok/; + +child.stderr.setEncoding('utf8'); +child.stderr.on('data', function(c) { + throw new Error('expected child.stderr be silent: ' + c); +}); + +child.stdout.setEncoding('utf8'); +var out = ''; +child.stdout.on('data', function(c) { + out += c; +}); +child.stdout.on('end', function() { + assert(expectOut.test(out)); + console.log('ok'); +}); + +child.stdin.end(''); diff --git a/test/simple/test-module-removeprologue.js b/test/simple/test-module-removeprologue.js new file mode 100644 index 000000000000..4e7410705949 --- /dev/null +++ b/test/simple/test-module-removeprologue.js @@ -0,0 +1,34 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var module = require('module'); + +assert.equal('', module._removePrologue('')); +assert.equal('\n// first line', + module._removePrologue('#!/usr/bin/env node\n// first line')); +assert.equal('\n// first line', + module._removePrologue('#!/usr/bin/env node\n// first line', true)); +assert.equal('\n\n\n// first line', + module._removePrologue('#!/usr/bin/env bash\nexec /usr/bin/env node -x\n#!node\n// first line', true)); + +console.log('ok'); From f48d1660fd4cfe0eb6cf75df47f3b852772459d5 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 11:59:00 -0700 Subject: [PATCH 02/21] fix formatting, add remaining issues section --- doc/Minus-X-Switch-Proposal.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index 4e21bf359036..1ba68f4e56b5 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -90,7 +90,7 @@ a line starting with '#!' and containing 'node' (as in ruby and perl) the process will cwd into this directory before (as in ruby and perl) Script authors can control the arguments passed to node using a similar -syntax to that used by ruby and perl: +syntax to that used by ruby and perl. #!/usr/bin/env bash @@ -189,6 +189,13 @@ For example: This proposal would allow a node script to serve as its own wrapper script. +# Remaining Issues + +If this is accepted, then [cmd-shim](https://github.com/ForbesLindesay/cmd-shim) will +need to be updated to support the -x switch and #!node lines. A mechanism using +wrapper scripts is necessary on Windows in any case. I volunteer to do the +necessary work on cmd-shim. + # References Shebang history and details From 50c715a042e44b988cdcdd1227b3a7701f6ba240 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 11:59:20 -0700 Subject: [PATCH 03/21] module: strip leading lines when -x switch set Factor the code that removes '#!' into a function, Module._removePrologue. Module._removePrologue removes only the first #! line unless BOTH a) this is the main module (module.id === '.') AND b) the '-x' switch was passed on the command line. In case both a) and b) are true, Module._removePrologue removes lines until it finds a line which begins with "#!" and contains the substring "node". --- lib/module.js | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/module.js b/lib/module.js index 11b9f8548d40..cd3e36370bc4 100644 --- a/lib/module.js +++ b/lib/module.js @@ -362,12 +362,47 @@ Module.prototype.require = function(path) { // (needed for setting breakpoint when called with --debug-brk) var resolvedArgv; +Module._removePrologue = function (content, doStrip) { + + var lines; + + if (doStrip) { + + lines = content.split(/\n/); + + lines.some(function(line, index) { + + // always null out the current line + lines[index] = ''; + + // stop if a shebang line with 'node' anywhere in it + if (/^#\!.*node.*/.test(line)) { + return true; + } + + // continue + return false; + }); + + content = lines.join('\n'); + + } else { + // remove shebang + content = content.replace(/^\#\!.*/, ''); + } + + return content; +} // Returns exception if any Module.prototype._compile = function(content, filename) { var self = this; - // remove shebang - content = content.replace(/^\#\!.*/, ''); + + // -x command-line switch to pass multiple v8 options + var doSpecialStrip = self.id === '.' && + process._stripUntilShebangNode; + + content = Module._removePrologue(content, doSpecialStrip); function require(path) { return self.require(path); From c70963ad99d9d0256b2691a529168c360498a0c8 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 12:01:44 -0700 Subject: [PATCH 04/21] command-line: add support for -x switch Detect '-x' switch on command line and set global strip_until_shebang_node and property process._stripUntilShebangNode --- src/node.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/node.cc b/src/node.cc index c1ecdcd30a5d..173f466ebfff 100644 --- a/src/node.cc +++ b/src/node.cc @@ -123,6 +123,7 @@ QUEUE req_wrap_queue = { &req_wrap_queue, &req_wrap_queue }; static bool print_eval = false; static bool force_repl = false; +static bool strip_until_shebang_node = false; static bool trace_deprecation = false; static bool throw_deprecation = false; static const char* eval_string = NULL; @@ -2557,6 +2558,11 @@ void SetupProcessObject(Environment* env, READONLY_PROPERTY(process, "_forceRepl", True(node_isolate)); } + // -x + if (strip_until_shebang_node) { + READONLY_PROPERTY(process, "_stripUntilShebangNode", True(node_isolate)); + } + // --no-deprecation if (no_deprecation) { READONLY_PROPERTY(process, "noDeprecation", True(node_isolate)); @@ -2881,6 +2887,8 @@ static void ParseArgs(int* argc, } } else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) { force_repl = true; + } else if (strcmp(arg, "-x") == 0) { + strip_until_shebang_node = true; } else if (strcmp(arg, "--no-deprecation") == 0) { no_deprecation = true; } else if (strcmp(arg, "--trace-deprecation") == 0) { From e556eeb2a85150d9aa80c6ed3cb45bee45c6747d Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 12:04:21 -0700 Subject: [PATCH 05/21] PrintHelp: add documentation of -x switch Add documentation of -x switch: "-x ignore text before #!node line" --- src/node.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node.cc b/src/node.cc index 173f466ebfff..287f40d10774 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2791,6 +2791,7 @@ static void PrintHelp() { " -p, --print evaluate script and print result\n" " -i, --interactive always enter the REPL even if stdin\n" " does not appear to be a terminal\n" + " -x ignore text before #!node line\n" " --no-deprecation silence deprecation warnings\n" " --trace-deprecation show stack traces on deprecations\n" " --v8-options print v8 command line options\n" From 44d467407da71717eda5014910ae5f9320954e13 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 12:36:22 -0700 Subject: [PATCH 06/21] module: (_removePrologue) fix js style errors --- lib/module.js | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/module.js b/lib/module.js index cd3e36370bc4..ed8e00f44904 100644 --- a/lib/module.js +++ b/lib/module.js @@ -362,37 +362,36 @@ Module.prototype.require = function(path) { // (needed for setting breakpoint when called with --debug-brk) var resolvedArgv; -Module._removePrologue = function (content, doStrip) { +Module._removePrologue = function(content, doStrip) { - var lines; + var lines; if (doStrip) { - lines = content.split(/\n/); + lines = content.split(/\n/); - lines.some(function(line, index) { + lines.some(function(line, index) { - // always null out the current line - lines[index] = ''; + // always null out the current line + lines[index] = ''; - // stop if a shebang line with 'node' anywhere in it - if (/^#\!.*node.*/.test(line)) { - return true; - } - - // continue - return false; - }); + // stop if a shebang line with 'node' anywhere in it + if (/^#\!.*node.*/.test(line)) { + return; + } - content = lines.join('\n'); + // continue + return false; + }); + content = lines.join('\n'); } else { - // remove shebang - content = content.replace(/^\#\!.*/, ''); + // remove shebang + content = content.replace(/^\#\!.*/, ''); } return content; -} +}; // Returns exception if any Module.prototype._compile = function(content, filename) { From adee60b9253d7ff346d1f17e0e2255c26c365598 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 12:36:51 -0700 Subject: [PATCH 07/21] indent code example further --- doc/Minus-X-Switch-Proposal.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index 1ba68f4e56b5..64fd5263fded 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -92,10 +92,9 @@ the process will cwd into this directory before (as in ruby and perl) Script authors can control the arguments passed to node using a similar syntax to that used by ruby and perl. - - #!/usr/bin/env bash - exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" - #!node + #!/usr/bin/env bash + exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" + #!node # Implementation From 6f26d0d3ee96be8e7a46ea21a8e3a3755d944a6b Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 15:50:17 -0700 Subject: [PATCH 08/21] add section, fix EOL --- doc/Minus-X-Switch-Proposal.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index 64fd5263fded..49e624c0c7fc 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -111,12 +111,15 @@ has already processed the supplied command-line arguments. The remaining problem is to ignore the preamble, and parse the script as a valid javascript file. An example preamble is reproduced below. - + $ cat preamble.js #!/usr/bin/env bash exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" #!node // javascript beginning here +To preserve line numbering, the implementation will replace each line up to and including +the '#!node' line with a single newline. + ## Command Line Option Three sections are added to src/node.cc to support the `-x` command line option and From cc4a40a541cbe7bbe5cf909b4741406a886e9be7 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 15:57:24 -0700 Subject: [PATCH 09/21] module: stop stripping lines when #!node seen Explicit 'true' return value omitted when refactoring. --- lib/module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/module.js b/lib/module.js index ed8e00f44904..c017bef9cc8d 100644 --- a/lib/module.js +++ b/lib/module.js @@ -377,7 +377,7 @@ Module._removePrologue = function(content, doStrip) { // stop if a shebang line with 'node' anywhere in it if (/^#\!.*node.*/.test(line)) { - return; + return true; } // continue From 365113b660730a8adea946637bd138036e1573ca Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 16:18:12 -0700 Subject: [PATCH 10/21] node: -x switch; no optional arg Remove incorrect documentation referring to optional arg to proposed -x switch. --- doc/Minus-X-Switch-Proposal.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index 49e624c0c7fc..d8e254debe75 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -80,14 +80,15 @@ Ruby uses the same convention as perl. # Proposal - I propose to add a new command-line switch to node (-x) following the same semantics as + I propose to add a new command-line switch to node (-x) following similar semantics as used by perl and ruby, namely: -1. The -x switch causes node to skip everything in the first input file/stream up to +* The -x switch causes node to skip everything in the first input file/stream up to a line starting with '#!' and containing 'node' (as in ruby and perl) -1. The -x switch has an optional argument which is interpreted as a directory; if supplied, -the process will cwd into this directory before (as in ruby and perl) +However, + +* The -x switch in node does *NOT* take an optional argument as in ruby and perl. Script authors can control the arguments passed to node using a similar syntax to that used by ruby and perl. From 048663732123f2d3eaf07584eacb612f032a84f9 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 16:20:40 -0700 Subject: [PATCH 11/21] fix indentation --- doc/Minus-X-Switch-Proposal.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index d8e254debe75..5983c2360b1c 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -90,9 +90,10 @@ However, * The -x switch in node does *NOT* take an optional argument as in ruby and perl. - Script authors can control the arguments passed to node using a similar +Script authors can control the arguments passed to node using a similar syntax to that used by ruby and perl. + #!/usr/bin/env bash exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" #!node From 7b322adc04777ce57157e83650b730aa6cdc2f0d Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 11:59:00 -0700 Subject: [PATCH 12/21] doc: fix formatting, add remaining issues --- doc/Minus-X-Switch-Proposal.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index 4e21bf359036..1ba68f4e56b5 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -90,7 +90,7 @@ a line starting with '#!' and containing 'node' (as in ruby and perl) the process will cwd into this directory before (as in ruby and perl) Script authors can control the arguments passed to node using a similar -syntax to that used by ruby and perl: +syntax to that used by ruby and perl. #!/usr/bin/env bash @@ -189,6 +189,13 @@ For example: This proposal would allow a node script to serve as its own wrapper script. +# Remaining Issues + +If this is accepted, then [cmd-shim](https://github.com/ForbesLindesay/cmd-shim) will +need to be updated to support the -x switch and #!node lines. A mechanism using +wrapper scripts is necessary on Windows in any case. I volunteer to do the +necessary work on cmd-shim. + # References Shebang history and details From a8e2660a2ec5c6b096ff770bdf2dcdbdfc1b6380 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 11:59:20 -0700 Subject: [PATCH 13/21] module: strip leading lines when -x switch set Factor the code that removes '#!' into a function, Module._removePrologue. Module._removePrologue removes only the first #! line unless BOTH a) this is the main module (module.id === '.') AND b) the '-x' switch was passed on the command line. In case both a) and b) are true, Module._removePrologue removes lines until it finds a line which begins with "#!" and contains the substring "node". --- lib/module.js | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/module.js b/lib/module.js index 11b9f8548d40..cd3e36370bc4 100644 --- a/lib/module.js +++ b/lib/module.js @@ -362,12 +362,47 @@ Module.prototype.require = function(path) { // (needed for setting breakpoint when called with --debug-brk) var resolvedArgv; +Module._removePrologue = function (content, doStrip) { + + var lines; + + if (doStrip) { + + lines = content.split(/\n/); + + lines.some(function(line, index) { + + // always null out the current line + lines[index] = ''; + + // stop if a shebang line with 'node' anywhere in it + if (/^#\!.*node.*/.test(line)) { + return true; + } + + // continue + return false; + }); + + content = lines.join('\n'); + + } else { + // remove shebang + content = content.replace(/^\#\!.*/, ''); + } + + return content; +} // Returns exception if any Module.prototype._compile = function(content, filename) { var self = this; - // remove shebang - content = content.replace(/^\#\!.*/, ''); + + // -x command-line switch to pass multiple v8 options + var doSpecialStrip = self.id === '.' && + process._stripUntilShebangNode; + + content = Module._removePrologue(content, doSpecialStrip); function require(path) { return self.require(path); From e4aac3c52dc73c8033c65bb198a7e00c42e6a771 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 12:01:44 -0700 Subject: [PATCH 14/21] command-line: add support for -x switch Detect '-x' switch on command line and set global strip_until_shebang_node and property process._stripUntilShebangNode --- src/node.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/node.cc b/src/node.cc index c1ecdcd30a5d..173f466ebfff 100644 --- a/src/node.cc +++ b/src/node.cc @@ -123,6 +123,7 @@ QUEUE req_wrap_queue = { &req_wrap_queue, &req_wrap_queue }; static bool print_eval = false; static bool force_repl = false; +static bool strip_until_shebang_node = false; static bool trace_deprecation = false; static bool throw_deprecation = false; static const char* eval_string = NULL; @@ -2557,6 +2558,11 @@ void SetupProcessObject(Environment* env, READONLY_PROPERTY(process, "_forceRepl", True(node_isolate)); } + // -x + if (strip_until_shebang_node) { + READONLY_PROPERTY(process, "_stripUntilShebangNode", True(node_isolate)); + } + // --no-deprecation if (no_deprecation) { READONLY_PROPERTY(process, "noDeprecation", True(node_isolate)); @@ -2881,6 +2887,8 @@ static void ParseArgs(int* argc, } } else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) { force_repl = true; + } else if (strcmp(arg, "-x") == 0) { + strip_until_shebang_node = true; } else if (strcmp(arg, "--no-deprecation") == 0) { no_deprecation = true; } else if (strcmp(arg, "--trace-deprecation") == 0) { From 8614f7cb8cecab39fc7053fa3b9cf02ae989a2b9 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 12:04:21 -0700 Subject: [PATCH 15/21] PrintHelp: add documentation of -x switch Add documentation of -x switch: "-x ignore text before #!node line" --- src/node.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node.cc b/src/node.cc index 173f466ebfff..287f40d10774 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2791,6 +2791,7 @@ static void PrintHelp() { " -p, --print evaluate script and print result\n" " -i, --interactive always enter the REPL even if stdin\n" " does not appear to be a terminal\n" + " -x ignore text before #!node line\n" " --no-deprecation silence deprecation warnings\n" " --trace-deprecation show stack traces on deprecations\n" " --v8-options print v8 command line options\n" From 04fc38619be328feaf66d255256817ac01dd484a Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 12:36:22 -0700 Subject: [PATCH 16/21] module: (_removePrologue) fix js style errors --- lib/module.js | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/module.js b/lib/module.js index cd3e36370bc4..ed8e00f44904 100644 --- a/lib/module.js +++ b/lib/module.js @@ -362,37 +362,36 @@ Module.prototype.require = function(path) { // (needed for setting breakpoint when called with --debug-brk) var resolvedArgv; -Module._removePrologue = function (content, doStrip) { +Module._removePrologue = function(content, doStrip) { - var lines; + var lines; if (doStrip) { - lines = content.split(/\n/); + lines = content.split(/\n/); - lines.some(function(line, index) { + lines.some(function(line, index) { - // always null out the current line - lines[index] = ''; + // always null out the current line + lines[index] = ''; - // stop if a shebang line with 'node' anywhere in it - if (/^#\!.*node.*/.test(line)) { - return true; - } - - // continue - return false; - }); + // stop if a shebang line with 'node' anywhere in it + if (/^#\!.*node.*/.test(line)) { + return; + } - content = lines.join('\n'); + // continue + return false; + }); + content = lines.join('\n'); } else { - // remove shebang - content = content.replace(/^\#\!.*/, ''); + // remove shebang + content = content.replace(/^\#\!.*/, ''); } return content; -} +}; // Returns exception if any Module.prototype._compile = function(content, filename) { From e6dee25ed474108c2e6d60d52c8a72280a396d7c Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 12:36:51 -0700 Subject: [PATCH 17/21] doc: indent code example further --- doc/Minus-X-Switch-Proposal.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index 1ba68f4e56b5..64fd5263fded 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -92,10 +92,9 @@ the process will cwd into this directory before (as in ruby and perl) Script authors can control the arguments passed to node using a similar syntax to that used by ruby and perl. - - #!/usr/bin/env bash - exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" - #!node + #!/usr/bin/env bash + exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" + #!node # Implementation From 6e6542ac50a068e2207142fac5a4b50f458146ee Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 15:50:17 -0700 Subject: [PATCH 18/21] doc: add section, fix EOL --- doc/Minus-X-Switch-Proposal.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index 64fd5263fded..49e624c0c7fc 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -111,12 +111,15 @@ has already processed the supplied command-line arguments. The remaining problem is to ignore the preamble, and parse the script as a valid javascript file. An example preamble is reproduced below. - + $ cat preamble.js #!/usr/bin/env bash exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" #!node // javascript beginning here +To preserve line numbering, the implementation will replace each line up to and including +the '#!node' line with a single newline. + ## Command Line Option Three sections are added to src/node.cc to support the `-x` command line option and From ac85dad57c6a6192263520ebffe4b2be4fc3dd35 Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 15:57:24 -0700 Subject: [PATCH 19/21] module: stop stripping lines when #!node seen Explicit 'true' return value omitted when refactoring. --- lib/module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/module.js b/lib/module.js index ed8e00f44904..c017bef9cc8d 100644 --- a/lib/module.js +++ b/lib/module.js @@ -377,7 +377,7 @@ Module._removePrologue = function(content, doStrip) { // stop if a shebang line with 'node' anywhere in it if (/^#\!.*node.*/.test(line)) { - return; + return true; } // continue From 3478c0ff55824fb618d8ce68fedc4778bdce449d Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 16:18:12 -0700 Subject: [PATCH 20/21] node: -x switch; no optional arg Remove incorrect documentation referring to optional arg to proposed -x switch. --- doc/Minus-X-Switch-Proposal.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index 49e624c0c7fc..d8e254debe75 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -80,14 +80,15 @@ Ruby uses the same convention as perl. # Proposal - I propose to add a new command-line switch to node (-x) following the same semantics as + I propose to add a new command-line switch to node (-x) following similar semantics as used by perl and ruby, namely: -1. The -x switch causes node to skip everything in the first input file/stream up to +* The -x switch causes node to skip everything in the first input file/stream up to a line starting with '#!' and containing 'node' (as in ruby and perl) -1. The -x switch has an optional argument which is interpreted as a directory; if supplied, -the process will cwd into this directory before (as in ruby and perl) +However, + +* The -x switch in node does *NOT* take an optional argument as in ruby and perl. Script authors can control the arguments passed to node using a similar syntax to that used by ruby and perl. From 5eaf3133726b1f5db7fe96a6d2e32bcf5f673f1c Mon Sep 17 00:00:00 2001 From: smikes Date: Thu, 30 Jan 2014 16:20:40 -0700 Subject: [PATCH 21/21] doc: fix indentation --- doc/Minus-X-Switch-Proposal.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/Minus-X-Switch-Proposal.md b/doc/Minus-X-Switch-Proposal.md index d8e254debe75..5983c2360b1c 100644 --- a/doc/Minus-X-Switch-Proposal.md +++ b/doc/Minus-X-Switch-Proposal.md @@ -90,9 +90,10 @@ However, * The -x switch in node does *NOT* take an optional argument as in ruby and perl. - Script authors can control the arguments passed to node using a similar +Script authors can control the arguments passed to node using a similar syntax to that used by ruby and perl. + #!/usr/bin/env bash exec /usr/bin/env node -x --harmony --harmony_typeof -- $0 "$@" #!node