aboutsummaryrefslogtreecommitdiff
path: root/cordova/node_modules/shelljs/src/exec.js
diff options
context:
space:
mode:
Diffstat (limited to 'cordova/node_modules/shelljs/src/exec.js')
-rwxr-xr-xcordova/node_modules/shelljs/src/exec.js216
1 files changed, 216 insertions, 0 deletions
diff --git a/cordova/node_modules/shelljs/src/exec.js b/cordova/node_modules/shelljs/src/exec.js
new file mode 100755
index 0000000..d259a9f
--- /dev/null
+++ b/cordova/node_modules/shelljs/src/exec.js
@@ -0,0 +1,216 @@
+var common = require('./common');
+var _tempDir = require('./tempdir');
+var _pwd = require('./pwd');
+var path = require('path');
+var fs = require('fs');
+var child = require('child_process');
+
+// Hack to run child_process.exec() synchronously (sync avoids callback hell)
+// Uses a custom wait loop that checks for a flag file, created when the child process is done.
+// (Can't do a wait loop that checks for internal Node variables/messages as
+// Node is single-threaded; callbacks and other internal state changes are done in the
+// event loop).
+function execSync(cmd, opts) {
+ var tempDir = _tempDir();
+ var stdoutFile = path.resolve(tempDir+'/'+common.randomFileName()),
+ codeFile = path.resolve(tempDir+'/'+common.randomFileName()),
+ scriptFile = path.resolve(tempDir+'/'+common.randomFileName()),
+ sleepFile = path.resolve(tempDir+'/'+common.randomFileName());
+
+ var options = common.extend({
+ silent: common.config.silent
+ }, opts);
+
+ var previousStdoutContent = '';
+ // Echoes stdout changes from running process, if not silent
+ function updateStdout() {
+ if (options.silent || !fs.existsSync(stdoutFile))
+ return;
+
+ var stdoutContent = fs.readFileSync(stdoutFile, 'utf8');
+ // No changes since last time?
+ if (stdoutContent.length <= previousStdoutContent.length)
+ return;
+
+ process.stdout.write(stdoutContent.substr(previousStdoutContent.length));
+ previousStdoutContent = stdoutContent;
+ }
+
+ function escape(str) {
+ return (str+'').replace(/([\\"'])/g, "\\$1").replace(/\0/g, "\\0");
+ }
+
+ if (fs.existsSync(scriptFile)) common.unlinkSync(scriptFile);
+ if (fs.existsSync(stdoutFile)) common.unlinkSync(stdoutFile);
+ if (fs.existsSync(codeFile)) common.unlinkSync(codeFile);
+
+ var execCommand = '"'+process.execPath+'" '+scriptFile;
+ var execOptions = {
+ env: process.env,
+ cwd: _pwd(),
+ maxBuffer: 20*1024*1024
+ };
+
+ if (typeof child.execSync === 'function') {
+ var script = [
+ "var child = require('child_process')",
+ " , fs = require('fs');",
+ "var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {",
+ " fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');",
+ "});",
+ "var stdoutStream = fs.createWriteStream('"+escape(stdoutFile)+"');",
+ "childProcess.stdout.pipe(stdoutStream, {end: false});",
+ "childProcess.stderr.pipe(stdoutStream, {end: false});",
+ "childProcess.stdout.pipe(process.stdout);",
+ "childProcess.stderr.pipe(process.stderr);",
+ "var stdoutEnded = false, stderrEnded = false;",
+ "function tryClosing(){ if(stdoutEnded && stderrEnded){ stdoutStream.end(); } }",
+ "childProcess.stdout.on('end', function(){ stdoutEnded = true; tryClosing(); });",
+ "childProcess.stderr.on('end', function(){ stderrEnded = true; tryClosing(); });"
+ ].join('\n');
+
+ fs.writeFileSync(scriptFile, script);
+
+ if (options.silent) {
+ execOptions.stdio = 'ignore';
+ } else {
+ execOptions.stdio = [0, 1, 2];
+ }
+
+ // Welcome to the future
+ child.execSync(execCommand, execOptions);
+ } else {
+ cmd += ' > '+stdoutFile+' 2>&1'; // works on both win/unix
+
+ var script = [
+ "var child = require('child_process')",
+ " , fs = require('fs');",
+ "var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {",
+ " fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');",
+ "});"
+ ].join('\n');
+
+ fs.writeFileSync(scriptFile, script);
+
+ child.exec(execCommand, execOptions);
+
+ // The wait loop
+ // sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage
+ // (tried many I/O sync ops, writeFileSync() seems to be only one that is effective in reducing
+ // CPU usage, though apparently not so much on Windows)
+ while (!fs.existsSync(codeFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); }
+ while (!fs.existsSync(stdoutFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); }
+ }
+
+ // At this point codeFile exists, but it's not necessarily flushed yet.
+ // Keep reading it until it is.
+ var code = parseInt('', 10);
+ while (isNaN(code)) {
+ code = parseInt(fs.readFileSync(codeFile, 'utf8'), 10);
+ }
+
+ var stdout = fs.readFileSync(stdoutFile, 'utf8');
+
+ // No biggie if we can't erase the files now -- they're in a temp dir anyway
+ try { common.unlinkSync(scriptFile); } catch(e) {}
+ try { common.unlinkSync(stdoutFile); } catch(e) {}
+ try { common.unlinkSync(codeFile); } catch(e) {}
+ try { common.unlinkSync(sleepFile); } catch(e) {}
+
+ // some shell return codes are defined as errors, per http://tldp.org/LDP/abs/html/exitcodes.html
+ if (code === 1 || code === 2 || code >= 126) {
+ common.error('', true); // unix/shell doesn't really give an error message after non-zero exit codes
+ }
+ // True if successful, false if not
+ var obj = {
+ code: code,
+ output: stdout
+ };
+ return obj;
+} // execSync()
+
+// Wrapper around exec() to enable echoing output to console in real time
+function execAsync(cmd, opts, callback) {
+ var output = '';
+
+ var options = common.extend({
+ silent: common.config.silent
+ }, opts);
+
+ var c = child.exec(cmd, {env: process.env, maxBuffer: 20*1024*1024}, function(err) {
+ if (callback)
+ callback(err ? err.code : 0, output);
+ });
+
+ c.stdout.on('data', function(data) {
+ output += data;
+ if (!options.silent)
+ process.stdout.write(data);
+ });
+
+ c.stderr.on('data', function(data) {
+ output += data;
+ if (!options.silent)
+ process.stdout.write(data);
+ });
+
+ return c;
+}
+
+//@
+//@ ### exec(command [, options] [, callback])
+//@ Available options (all `false` by default):
+//@
+//@ + `async`: Asynchronous execution. Defaults to true if a callback is provided.
+//@ + `silent`: Do not echo program output to console.
+//@
+//@ Examples:
+//@
+//@ ```javascript
+//@ var version = exec('node --version', {silent:true}).output;
+//@
+//@ var child = exec('some_long_running_process', {async:true});
+//@ child.stdout.on('data', function(data) {
+//@ /* ... do something with data ... */
+//@ });
+//@
+//@ exec('some_long_running_process', function(code, output) {
+//@ console.log('Exit code:', code);
+//@ console.log('Program output:', output);
+//@ });
+//@ ```
+//@
+//@ Executes the given `command` _synchronously_, unless otherwise specified.
+//@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's
+//@ `output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and
+//@ the `callback` gets the arguments `(code, output)`.
+//@
+//@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as
+//@ the current synchronous implementation uses a lot of CPU. This should be getting
+//@ fixed soon.
+function _exec(command, options, callback) {
+ if (!command)
+ common.error('must specify command');
+
+ // Callback is defined instead of options.
+ if (typeof options === 'function') {
+ callback = options;
+ options = { async: true };
+ }
+
+ // Callback is defined with options.
+ if (typeof options === 'object' && typeof callback === 'function') {
+ options.async = true;
+ }
+
+ options = common.extend({
+ silent: common.config.silent,
+ async: false
+ }, options);
+
+ if (options.async)
+ return execAsync(command, options, callback);
+ else
+ return execSync(command, options);
+}
+module.exports = _exec;