From 489bd0f0fe1b059578c4fbdb248eab96b2a7432a Mon Sep 17 00:00:00 2001 From: Jared Allard Date: Fri, 26 Jun 2015 18:27:26 -0700 Subject: [PATCH] exe: fixes #122, implement base nexe package field --- .gitignore | 5 +- README.md | 28 ++++++++-- bin/nexe | 52 +++++++++++++------ lib/bundle.js | 23 +++++++++ lib/embed.js | 23 +++++++++ lib/exe.js | 125 +++++++++++++++++++++++++++++++++++++++------ lib/log.js | 36 +++++++++++-- lib/monkeypatch.js | 42 ++++++++++++--- package.json | 12 ++++- 9 files changed, 297 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index 1c0b1c5..1aa9837 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ node_modules .DS_Store *.nex -npm-debug.log \ No newline at end of file +src +nexe.exe +nexe +npm-debug.log diff --git a/README.md b/README.md index be59b7b..240125f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ### Nexe [![Join the chat at https://gitter.im/crcn/nexe](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/crcn/nexe?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Dependency Status](https://david-dm.org/crcn/nexe.svg)](https://david-dm.org/crcn/nexe) -[![Stories in Ready](https://badge.waffle.io/crcn/nexe.svg?label=ready&title=Ready)](http://waffle.io/crcn/nexe) +[![Stories in Ready](https://badge.waffle.io/crcn/nexe.svg?label=ready&title=Ready)](http://waffle.io/crcn/nexe) Nexe is a command-line utility that compiles your Node.js application into a single executable file. @@ -116,9 +116,9 @@ Usage: nexe -i [sources] -o [binary] [options] Options: -i, --input The entry javascript files [default: cwd] - -o, --output The output binary [default: cwd/release/app.nex] + -o, --output The output binary [default: out.nex] -r, --runtime The node.js runtime to use [default: "latest"] - -t, --temp The path to store node.js sources [default: cwd/tmp/nexe] + -t, --temp The path to store node.js sources [default: ./tmp/nexe] -f, --flags Don't parse node and v8 flags, pass through app flags [default: false] -v, --version Display version number -p, --python Set path of python to use. [default: "python"] @@ -136,8 +136,8 @@ var nexe = require('nexe'); nexe.compile({ input: 'input.js', output: 'path/to/bin', - nodeVersion: '0.12.0', - nodeTempDir: __dirname, + nodeVersion: '0.12.5', + nodeTempDir: 'src', python: 'path/to/python', resourceFiles: [ 'path/to/a/file' ], flags: true, @@ -148,6 +148,24 @@ nexe.compile({ ``` +### package.json inclusion + +As of 0.4.0 you can now embed nexe options into package.json. Note that this Format +is still in works, so it is likely to change. + +``` +"nexe": { + "input": "./bin/nexe", + "output": "nexe^$", + "temp": "src", + "runtime": { + "framework": "iojs", + "version": "2.3.1", + "ignoreFlags": true + } +} +``` + ## Maintainers * __Jared Allard__ ([@jaredallard](https://github.com/jaredallard)) <[jaredallard@outlook.com](mailto:jaredallard@outlook.com)> (Active) diff --git a/bin/nexe b/bin/nexe index 589ee2c..c05c531 100755 --- a/bin/nexe +++ b/bin/nexe @@ -1,8 +1,10 @@ #!/usr/bin/env node -var path = require('path'); - -var cli = require('yargs'). +var path = require('path'), + fs = require('fs'), + nexe = require('../lib'), + _log = require("../lib/log"), + cli = require('yargs'). usage('Usage: $0 -i [sources] -o [binary]'). options('i', { demand: true, @@ -14,7 +16,7 @@ options('o', { demand: true, alias: 'output', desc: 'The output binary', - default: "..."+(process.cwd() + '/release/' + path.basename(process.cwd()) + '.nex').substr(-47) // cleaner for long directories + default: "out.nex" }). options('r', { alias: 'runtime', @@ -23,7 +25,7 @@ options('r', { }). options('t', { alias: 'temp', - default: '/tmp/nexe', + default: './tmp/nexe', description: 'The path to store node.js sources' }). options('f', { @@ -47,7 +49,6 @@ options('F', { }); var argv = cli.argv; - if(argv.h || argv.help) { cli.showHelp(); process.exit(); @@ -57,19 +58,36 @@ if(argv.h || argv.help) { process.exit(); } - function toAbsolute(pt) { if(pt.substr(0, 1) == "/") return pt; return path.join(process.cwd(), pt); } -require('../lib').compile({ - input : require.resolve(toAbsolute(argv.i)), - output : toAbsolute(argv.o), - flags : argv.f, - nodeVersion : argv.r, - python : argv.python, - nodeTempDir : toAbsolute(argv.t), - framework : argv.F }, function(error) { - if(error) console.log(error.message); -}); +if(fs.lstatSync(argv.i).isDirectory()) { + _log("log", "checking package.json"); + opts = nexe.package(path.join(argv.i, "package.json"), argv); + nexe.compile(opts, function(error) { + if(error) { + console.log(error.message); + } + }); +} else { + /** + * Call the core + **/ + nexe.compile({ + input : require.resolve(toAbsolute(argv.i)), + output : toAbsolute(argv.o), + flags : argv.f, + nodeVersion : argv.r, + python : argv.python, + nodeTempDir : toAbsolute(argv.t), + framework : argv.F + }, + function(error) { + if(error) { + console.log(error.message); + } + } + ); +} diff --git a/lib/bundle.js b/lib/bundle.js index 64dd201..d18b2f6 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -1,3 +1,26 @@ +/** + * Copyright (c) 2013 Craig Condon + * + * 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 mdeps = require("module-deps"), through = require("through"), async = require("async"), diff --git a/lib/embed.js b/lib/embed.js index 3c78a2d..c7c5a2f 100644 --- a/lib/embed.js +++ b/lib/embed.js @@ -1,3 +1,26 @@ +/** + * Copyright (c) 2013 Craig Condon + * + * 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 path = require("path"), fs = require("fs"); diff --git a/lib/exe.js b/lib/exe.js index dabde8b..b36064a 100644 --- a/lib/exe.js +++ b/lib/exe.js @@ -1,3 +1,26 @@ +/** + * Copyright (c) 2013 Craig Condon + * + * 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 async = require("async"), outcome = require("outcome"), mkdirp = require("mkdirp"), @@ -12,17 +35,25 @@ bundle = require("./bundle"), embed = require("./embed"), os = require("os"), targz = require('tar.gz'), +_log = require("./log"), +_monkeypatch = require("./monkeypatch"), spawn = child_process.spawn; - -var _log = require("./log"), -_monkeypatch = require("./monkeypatch"); - var isWin = /^win/.test(process.platform); var isPy; var framework; var version; +option_list = [ + "python", + "input", + "output", + "nodeTempDir", + "flags", + "framework", + "nodeVersion" +] + /** * Compiliation process. */ @@ -36,6 +67,19 @@ exports.compile = function (options, complete) { *check relevant options */ function checkOpts (next) { + /* failsafe */ + if(options === undefined) { + _log("error", "no options given to .compile()"); + process.exit() + } + + option_list.forEach(function(v) { + if(options[v] === undefined) { + _log("error", "option "+v+" was empty") + process.exit(); + } + }); + /** * Have we been given a custom flag for python executable? */ @@ -372,11 +416,8 @@ function _getNodeCompiler (nodeFileDir, complete, type) { /* patch the file */ var py = fs.readFileSync(dir+"/"+v, {encoding: 'utf8'}); - //py = py.replace(/^#!\/usr\/bin\/env python$/gm, "#!"+isPy); // may not be needed - //py = py.replace(/^#!\/usr\/bin\/python$/gm, "#!"+isPy); // may not be needed py = py.replace(/([a-z]|\/)*python(\w|)/gm, isPy); // this is definently needed - //py = py.replace(/^PYTHON \?= python$/gm, "PYTHON ?= "+isPy); // may not be needed (pre-hook) - fs.writeFileSync(dir+"/"+v, py, {encoding: 'utf8'}); + fs.writeFileSync(dir+"/"+v, py, {encoding: 'utf8'}); // write to file delete pv; } else if(stat.isDirectory()) { @@ -428,11 +469,9 @@ function _getNodeCompiler (nodeFileDir, complete, type) { function _monkeyPatchNodeConfig (compiler, complete) { async.waterfall([ - /** * monkeypatch the gyp file to include the nexe.js and nexeres.js files */ - function (next) { _monkeyPatchGyp(compiler, next) }, @@ -440,11 +479,9 @@ function _monkeyPatchNodeConfig (compiler, complete) { /** * monkeypatch main entry point */ - function (next) { _monkeyPatchMainJs(compiler, next) } - ], complete); } @@ -504,6 +541,7 @@ function _monkeyPatchChildProcess(compiler, complete) { } /** + * Patch node.cc to not check the internal arguments. */ function _monkeyPatchMainCc(compiler, complete) { @@ -515,15 +553,23 @@ function _monkeyPatchMainCc(compiler, complete) { }, function (content, next) { var lines = content.split('\n'); - /* These lines exist until v0.11.6 */ - var startLine = lines.indexOf(' // TODO use parse opts'); + + /* pre 0.11.6 compat? */ var endLine = lines.indexOf(' option_end_index = i;'); + lines[endLine] = ' option_end_index = 1;'; + + /** + * This is the new method of passing the args. Tested on node.js 0.12.5 + * and iojs 2.3.1 + **/ + var startLine = lines.indexOf(' while (index < nargs && argv[index][0] == \'-\') {'); + var endLine = lines.indexOf(' // Copy remaining arguments.'); + endLine = endLine-1; // space, then it's at the } for (var i = startLine; i < endLine; i++) { lines[i] = '//' + lines[i]; } - lines[endLine] = ' option_end_index = 1;'; lines = lines.join('\n'); next(null, lines); }, @@ -567,3 +613,52 @@ function _logProgress (req) { return req; } + +/** + * Attempt to parse the package.json for nexe information. + * + * @param {string} path - path to package.json + * @param {object} options - fallback options + * + * @return {object} nexe.compile options object +**/ +exports.package = function(path, options) { + var _package; // scope + + // check if the file exists + if(fs.existsSync(path)===false) { + _log("warn", "no package.json found."); + } else { + _package = require(path); + } + + // replace ^$ w/ os specific extension on output + if(isWin) { + _package.nexe.output = _package.nexe.output.replace(/\^\$/, '.exe') // exe + } else { + _package.nexe.output = _package.nexe.output.replace(/\^\$/, '') // none + } + + // construct the object + var obj = { + input: (_package.nexe.input || options.i), + output: (_package.nexe.output || options.o), + flags: (_package.nexe.runtime.ignoreFlags || options.f), + resourceFiles: (_package.nexe.resourceFiles), + nodeVersion: (_package.nexe.runtime.version || options.r), + python: (_package.nexe.python || options.p), + nodeTempDir: (_package.nexe.temp || options.t), + framework: (_package.nexe.runtime.framework || options.f) + } + + Object.keys(_package.nexe).forEach(function(v,i) { + if(v!=="runtime") { + _log("log", v+" => '"+_package.nexe[v]+"'"); + } + }); + Object.keys(_package.nexe.runtime).forEach(function(v,i) { + _log("log", "runtime."+v+" => '"+_package.nexe.runtime[v]+"'"); + }); + + return obj; +} diff --git a/lib/log.js b/lib/log.js index b318031..b46d393 100644 --- a/lib/log.js +++ b/lib/log.js @@ -1,9 +1,34 @@ -module.exports = _log; +/** + * Copyright (c) 2013 Craig Condon + * + * 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. + * + **/ /** - * logging aka stdout wrapper - */ - + * standard output, takes 3 different types. + * log, error, and warn + * + * @param {any} arguments - Text to output. + * @return undefined + **/ function _log () { var colors = require('colors'); @@ -26,3 +51,6 @@ function _log () { console[level].apply(console, args); } + +// export the log function, for require() +module.exports = _log; diff --git a/lib/monkeypatch.js b/lib/monkeypatch.js index be57993..1ba9872 100644 --- a/lib/monkeypatch.js +++ b/lib/monkeypatch.js @@ -1,14 +1,41 @@ +/** + * Copyright (c) 2013 Craig Condon + * + * 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 async = require("async"), fs = require("fs"), _log = require("./log"); -module.exports = _monkeypatch; - - /** + * Monkey patch a file. + * + * @param {string} filePath - path to file. + * @param {function} monkeyPatched - function to process contents + * @param {function} processor - TODO: detail what this is + * @param {function} complete - callback + * + * @return undefined */ - - function _monkeypatch (filePath, monkeyPatched, processor, complete) { async.waterfall([ @@ -30,4 +57,7 @@ function _monkeypatch (filePath, monkeyPatched, processor, complete) { fs.writeFile(filePath, content, "utf8", next); } ], complete); -} \ No newline at end of file +} + +// export the function for require() +module.exports = _monkeypatch; diff --git a/package.json b/package.json index 272b723..5200e30 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "nexe", "description": "Roll node.js applications into a single executable", "license": "MIT", - "version": "0.3.9", + "version": "0.4.0", "repository": { "type": "git", "url": "git://github.com/crcn/nexe.git" @@ -26,6 +26,16 @@ "through": "^2.3.6", "yargs": "^3.2.1" }, + "nexe": { + "input": "./bin/nexe", + "output": "nexe^$", + "temp": "src", + "runtime": { + "framework": "iojs", + "version": "1.0.1", + "ignoreFlags": true + } + }, "preferGlobal": "true", "bin": { "nexe": "./bin/nexe"