2 * Jake JavaScript build tool
3 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 let util = require('util'); // Native Node util module
21 let spawn = require('child_process').spawn;
22 let EventEmitter = require('events').EventEmitter;
23 let logger = require('./logger');
24 let file = require('./file');
27 const _UUID_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
29 let parseArgs = function (argumentsObj) {
41 args = Array.prototype.slice.call(argumentsObj);
44 // Arrayize if passed a single string command
45 if (typeof cmds == 'string') {
48 // Make a copy if it's an actual list
53 // Get optional callback or opts
54 while((arg = args.shift())) {
55 if (typeof arg == 'function') {
58 else if (typeof arg == 'object') {
59 opts = Object.assign(opts, arg);
63 // Backward-compat shim
64 if (typeof opts.stdout != 'undefined') {
65 opts.printStdout = opts.stdout;
68 if (typeof opts.stderr != 'undefined') {
69 opts.printStderr = opts.stderr;
84 let utils = new (function () {
89 @description Executes shell-commands asynchronously with an optional
92 @param {String[]} cmds The list of shell-commands to execute
93 @param {Object} [opts]
94 @param {Boolean} [opts.printStdout=false] Print stdout from each command
95 @param {Boolean} [opts.printStderr=false] Print stderr from each command
96 @param {Boolean} [opts.breakOnError=true] Stop further execution on
98 @param {Boolean} [opts.windowsVerbatimArguments=false] Don't translate
100 @param {Function} [callback] Callback to run after executing the
105 'echo "showing directories"'
107 , 'echo "moving up a directory"'
110 , callback = function () {
111 console.log('Finished running commands.');
113 jake.exec(cmds, {stdout: true}, callback);
115 this.exec = function (a, b, c) {
116 let parsed = parseArgs(arguments);
117 let cmds = parsed.cmds;
118 let opts = parsed.opts;
119 let callback = parsed.callback;
121 let ex = new Exec(cmds, opts, callback);
123 ex.addListener('error', function (msg, code) {
124 if (opts.breakOnError) {
133 this.createExec = function (a, b, c) {
134 return new Exec(a, b, c);
137 // From Math.uuid.js, https://github.com/broofa/node-uuid
138 // Robert Kieffer (robert@broofa.com), MIT license
139 this.uuid = function (length, radix) {
140 var chars = _UUID_CHARS
145 radix = radix || chars.length;
150 while (++i < length) {
151 uuid[i] = chars[0 | Math.random()*radix];
154 // rfc4122, version 4 form
156 // rfc4122 requires these characters
157 uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
160 // Fill in random data. At i==19 set the high bits of clock sequence as
161 // per rfc4122, sec. 4.1.5
165 r = 0 | Math.random()*16;
166 uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
171 return uuid.join('');
177 let parsed = parseArgs(arguments);
178 let cmds = parsed.cmds;
179 let opts = parsed.opts;
180 let callback = parsed.callback;
183 this._callback = callback;
187 util.inherits(Exec, EventEmitter);
189 Object.assign(Exec.prototype, new (function () {
191 let _run = function () {
196 let next = this._cmds.shift();
197 let config = this._config;
200 let handleStdoutData = function (data) {
201 self.emit('stdout', data);
203 let handleStderrData = function (data) {
204 let d = data.toString();
205 self.emit('stderr', data);
206 // Accumulate the error-data so we can use it as the
207 // stack if the process exits with an error
211 // Keep running as long as there are commands in the array
214 this.emit('cmdStart', next);
216 // Ganking part of Node's child_process.exec to get cmdline args parsed
217 if (process.platform == 'win32') {
220 if (config.windowsVerbatimArguments) {
221 spawnOpts.windowsVerbatimArguments = true;
229 if (config.interactive) {
230 spawnOpts.stdio = 'inherit';
231 sh = spawn(cmd, args, spawnOpts);
237 if (config.printStdout) {
238 shStdio.push(process.stdout);
241 shStdio.push('pipe');
243 if (config.printStderr) {
244 shStdio.push(process.stderr);
247 shStdio.push('pipe');
249 spawnOpts.stdio = shStdio;
250 sh = spawn(cmd, args, spawnOpts);
251 if (!config.printStdout) {
252 sh.stdout.addListener('data', handleStdoutData);
254 if (!config.printStderr) {
255 sh.stderr.addListener('data', handleStderrData);
259 // Exit, handle err or run next
260 sh.on('exit', function (code) {
263 msg = errData || 'Process exited with error.';
265 self.emit('error', msg, code);
267 if (code === 0 || !config.breakOnError) {
268 self.emit('cmdEnd', next);
269 setTimeout(function () { _run.call(self); }, 0);
276 if (typeof self._callback == 'function') {
282 this.append = function (cmd) {
283 this._cmds.push(cmd);
286 this.run = function () {
294 utils.logger = logger;
296 module.exports = utils;