573 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			573 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								"use strict";
							 | 
						||
| 
								 | 
							
								var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
							 | 
						||
| 
								 | 
							
								    return new (P || (P = Promise))(function (resolve, reject) {
							 | 
						||
| 
								 | 
							
								        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
							 | 
						||
| 
								 | 
							
								        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
							 | 
						||
| 
								 | 
							
								        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
							 | 
						||
| 
								 | 
							
								        step((generator = generator.apply(thisArg, _arguments || [])).next());
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								Object.defineProperty(exports, "__esModule", { value: true });
							 | 
						||
| 
								 | 
							
								const os = require("os");
							 | 
						||
| 
								 | 
							
								const events = require("events");
							 | 
						||
| 
								 | 
							
								const child = require("child_process");
							 | 
						||
| 
								 | 
							
								/* eslint-disable @typescript-eslint/unbound-method */
							 | 
						||
| 
								 | 
							
								const IS_WINDOWS = process.platform === 'win32';
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class ToolRunner extends events.EventEmitter {
							 | 
						||
| 
								 | 
							
								    constructor(toolPath, args, options) {
							 | 
						||
| 
								 | 
							
								        super();
							 | 
						||
| 
								 | 
							
								        if (!toolPath) {
							 | 
						||
| 
								 | 
							
								            throw new Error("Parameter 'toolPath' cannot be null or empty.");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.toolPath = toolPath;
							 | 
						||
| 
								 | 
							
								        this.args = args || [];
							 | 
						||
| 
								 | 
							
								        this.options = options || {};
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _debug(message) {
							 | 
						||
| 
								 | 
							
								        if (this.options.listeners && this.options.listeners.debug) {
							 | 
						||
| 
								 | 
							
								            this.options.listeners.debug(message);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _getCommandString(options, noPrefix) {
							 | 
						||
| 
								 | 
							
								        const toolPath = this._getSpawnFileName();
							 | 
						||
| 
								 | 
							
								        const args = this._getSpawnArgs(options);
							 | 
						||
| 
								 | 
							
								        let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool
							 | 
						||
| 
								 | 
							
								        if (IS_WINDOWS) {
							 | 
						||
| 
								 | 
							
								            // Windows + cmd file
							 | 
						||
| 
								 | 
							
								            if (this._isCmdFile()) {
							 | 
						||
| 
								 | 
							
								                cmd += toolPath;
							 | 
						||
| 
								 | 
							
								                for (const a of args) {
							 | 
						||
| 
								 | 
							
								                    cmd += ` ${a}`;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // Windows + verbatim
							 | 
						||
| 
								 | 
							
								            else if (options.windowsVerbatimArguments) {
							 | 
						||
| 
								 | 
							
								                cmd += `"${toolPath}"`;
							 | 
						||
| 
								 | 
							
								                for (const a of args) {
							 | 
						||
| 
								 | 
							
								                    cmd += ` ${a}`;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // Windows (regular)
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                cmd += this._windowsQuoteCmdArg(toolPath);
							 | 
						||
| 
								 | 
							
								                for (const a of args) {
							 | 
						||
| 
								 | 
							
								                    cmd += ` ${this._windowsQuoteCmdArg(a)}`;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            // OSX/Linux - this can likely be improved with some form of quoting.
							 | 
						||
| 
								 | 
							
								            // creating processes on Unix is fundamentally different than Windows.
							 | 
						||
| 
								 | 
							
								            // on Unix, execvp() takes an arg array.
							 | 
						||
| 
								 | 
							
								            cmd += toolPath;
							 | 
						||
| 
								 | 
							
								            for (const a of args) {
							 | 
						||
| 
								 | 
							
								                cmd += ` ${a}`;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return cmd;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _processLineBuffer(data, strBuffer, onLine) {
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								            let s = strBuffer + data.toString();
							 | 
						||
| 
								 | 
							
								            let n = s.indexOf(os.EOL);
							 | 
						||
| 
								 | 
							
								            while (n > -1) {
							 | 
						||
| 
								 | 
							
								                const line = s.substring(0, n);
							 | 
						||
| 
								 | 
							
								                onLine(line);
							 | 
						||
| 
								 | 
							
								                // the rest of the string ...
							 | 
						||
| 
								 | 
							
								                s = s.substring(n + os.EOL.length);
							 | 
						||
| 
								 | 
							
								                n = s.indexOf(os.EOL);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            strBuffer = s;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        catch (err) {
							 | 
						||
| 
								 | 
							
								            // streaming lines to console is best effort.  Don't fail a build.
							 | 
						||
| 
								 | 
							
								            this._debug(`error processing line. Failed with error ${err}`);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _getSpawnFileName() {
							 | 
						||
| 
								 | 
							
								        if (IS_WINDOWS) {
							 | 
						||
| 
								 | 
							
								            if (this._isCmdFile()) {
							 | 
						||
| 
								 | 
							
								                return process.env['COMSPEC'] || 'cmd.exe';
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return this.toolPath;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _getSpawnArgs(options) {
							 | 
						||
| 
								 | 
							
								        if (IS_WINDOWS) {
							 | 
						||
| 
								 | 
							
								            if (this._isCmdFile()) {
							 | 
						||
| 
								 | 
							
								                let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`;
							 | 
						||
| 
								 | 
							
								                for (const a of this.args) {
							 | 
						||
| 
								 | 
							
								                    argline += ' ';
							 | 
						||
| 
								 | 
							
								                    argline += options.windowsVerbatimArguments
							 | 
						||
| 
								 | 
							
								                        ? a
							 | 
						||
| 
								 | 
							
								                        : this._windowsQuoteCmdArg(a);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                argline += '"';
							 | 
						||
| 
								 | 
							
								                return [argline];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return this.args;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _endsWith(str, end) {
							 | 
						||
| 
								 | 
							
								        return str.endsWith(end);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _isCmdFile() {
							 | 
						||
| 
								 | 
							
								        const upperToolPath = this.toolPath.toUpperCase();
							 | 
						||
| 
								 | 
							
								        return (this._endsWith(upperToolPath, '.CMD') ||
							 | 
						||
| 
								 | 
							
								            this._endsWith(upperToolPath, '.BAT'));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _windowsQuoteCmdArg(arg) {
							 | 
						||
| 
								 | 
							
								        // for .exe, apply the normal quoting rules that libuv applies
							 | 
						||
| 
								 | 
							
								        if (!this._isCmdFile()) {
							 | 
						||
| 
								 | 
							
								            return this._uvQuoteCmdArg(arg);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // otherwise apply quoting rules specific to the cmd.exe command line parser.
							 | 
						||
| 
								 | 
							
								        // the libuv rules are generic and are not designed specifically for cmd.exe
							 | 
						||
| 
								 | 
							
								        // command line parser.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // for a detailed description of the cmd.exe command line parser, refer to
							 | 
						||
| 
								 | 
							
								        // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912
							 | 
						||
| 
								 | 
							
								        // need quotes for empty arg
							 | 
						||
| 
								 | 
							
								        if (!arg) {
							 | 
						||
| 
								 | 
							
								            return '""';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // determine whether the arg needs to be quoted
							 | 
						||
| 
								 | 
							
								        const cmdSpecialChars = [
							 | 
						||
| 
								 | 
							
								            ' ',
							 | 
						||
| 
								 | 
							
								            '\t',
							 | 
						||
| 
								 | 
							
								            '&',
							 | 
						||
| 
								 | 
							
								            '(',
							 | 
						||
| 
								 | 
							
								            ')',
							 | 
						||
| 
								 | 
							
								            '[',
							 | 
						||
| 
								 | 
							
								            ']',
							 | 
						||
| 
								 | 
							
								            '{',
							 | 
						||
| 
								 | 
							
								            '}',
							 | 
						||
| 
								 | 
							
								            '^',
							 | 
						||
| 
								 | 
							
								            '=',
							 | 
						||
| 
								 | 
							
								            ';',
							 | 
						||
| 
								 | 
							
								            '!',
							 | 
						||
| 
								 | 
							
								            "'",
							 | 
						||
| 
								 | 
							
								            '+',
							 | 
						||
| 
								 | 
							
								            ',',
							 | 
						||
| 
								 | 
							
								            '`',
							 | 
						||
| 
								 | 
							
								            '~',
							 | 
						||
| 
								 | 
							
								            '|',
							 | 
						||
| 
								 | 
							
								            '<',
							 | 
						||
| 
								 | 
							
								            '>',
							 | 
						||
| 
								 | 
							
								            '"'
							 | 
						||
| 
								 | 
							
								        ];
							 | 
						||
| 
								 | 
							
								        let needsQuotes = false;
							 | 
						||
| 
								 | 
							
								        for (const char of arg) {
							 | 
						||
| 
								 | 
							
								            if (cmdSpecialChars.some(x => x === char)) {
							 | 
						||
| 
								 | 
							
								                needsQuotes = true;
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // short-circuit if quotes not needed
							 | 
						||
| 
								 | 
							
								        if (!needsQuotes) {
							 | 
						||
| 
								 | 
							
								            return arg;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // the following quoting rules are very similar to the rules that by libuv applies.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // 1) wrap the string in quotes
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // 2) double-up quotes - i.e. " => ""
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately
							 | 
						||
| 
								 | 
							
								        //    doesn't work well with a cmd.exe command line.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    note, replacing " with "" also works well if the arg is passed to a downstream .NET console app.
							 | 
						||
| 
								 | 
							
								        //    for example, the command line:
							 | 
						||
| 
								 | 
							
								        //          foo.exe "myarg:""my val"""
							 | 
						||
| 
								 | 
							
								        //    is parsed by a .NET console app into an arg array:
							 | 
						||
| 
								 | 
							
								        //          [ "myarg:\"my val\"" ]
							 | 
						||
| 
								 | 
							
								        //    which is the same end result when applying libuv quoting rules. although the actual
							 | 
						||
| 
								 | 
							
								        //    command line from libuv quoting rules would look like:
							 | 
						||
| 
								 | 
							
								        //          foo.exe "myarg:\"my val\""
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // 3) double-up slashes that preceed a quote,
							 | 
						||
| 
								 | 
							
								        //    e.g.  hello \world    => "hello \world"
							 | 
						||
| 
								 | 
							
								        //          hello\"world    => "hello\\""world"
							 | 
						||
| 
								 | 
							
								        //          hello\\"world   => "hello\\\\""world"
							 | 
						||
| 
								 | 
							
								        //          hello world\    => "hello world\\"
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    technically this is not required for a cmd.exe command line, or the batch argument parser.
							 | 
						||
| 
								 | 
							
								        //    the reasons for including this as a .cmd quoting rule are:
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    a) this is optimized for the scenario where the argument is passed from the .cmd file to an
							 | 
						||
| 
								 | 
							
								        //       external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //    b) it's what we've been doing previously (by deferring to node default behavior) and we
							 | 
						||
| 
								 | 
							
								        //       haven't heard any complaints about that aspect.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be
							 | 
						||
| 
								 | 
							
								        // escaped when used on the command line directly - even though within a .cmd file % can be escaped
							 | 
						||
| 
								 | 
							
								        // by using %%.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts
							 | 
						||
| 
								 | 
							
								        // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would
							 | 
						||
| 
								 | 
							
								        // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the
							 | 
						||
| 
								 | 
							
								        // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args
							 | 
						||
| 
								 | 
							
								        // to an external program.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file.
							 | 
						||
| 
								 | 
							
								        // % can be escaped within a .cmd file.
							 | 
						||
| 
								 | 
							
								        let reverse = '"';
							 | 
						||
| 
								 | 
							
								        let quoteHit = true;
							 | 
						||
| 
								 | 
							
								        for (let i = arg.length; i > 0; i--) {
							 | 
						||
| 
								 | 
							
								            // walk the string in reverse
							 | 
						||
| 
								 | 
							
								            reverse += arg[i - 1];
							 | 
						||
| 
								 | 
							
								            if (quoteHit && arg[i - 1] === '\\') {
							 | 
						||
| 
								 | 
							
								                reverse += '\\'; // double the slash
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if (arg[i - 1] === '"') {
							 | 
						||
| 
								 | 
							
								                quoteHit = true;
							 | 
						||
| 
								 | 
							
								                reverse += '"'; // double the quote
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                quoteHit = false;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        reverse += '"';
							 | 
						||
| 
								 | 
							
								        return reverse
							 | 
						||
| 
								 | 
							
								            .split('')
							 | 
						||
| 
								 | 
							
								            .reverse()
							 | 
						||
| 
								 | 
							
								            .join('');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _uvQuoteCmdArg(arg) {
							 | 
						||
| 
								 | 
							
								        // Tool runner wraps child_process.spawn() and needs to apply the same quoting as
							 | 
						||
| 
								 | 
							
								        // Node in certain cases where the undocumented spawn option windowsVerbatimArguments
							 | 
						||
| 
								 | 
							
								        // is used.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        // Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV,
							 | 
						||
| 
								 | 
							
								        // see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details),
							 | 
						||
| 
								 | 
							
								        // pasting copyright notice from Node within this function:
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //      Copyright Joyent, Inc. and other Node contributors. All rights reserved.
							 | 
						||
| 
								 | 
							
								        //
							 | 
						||
| 
								 | 
							
								        //      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.
							 | 
						||
| 
								 | 
							
								        if (!arg) {
							 | 
						||
| 
								 | 
							
								            // Need double quotation for empty argument
							 | 
						||
| 
								 | 
							
								            return '""';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!arg.includes(' ') && !arg.includes('\t') && !arg.includes('"')) {
							 | 
						||
| 
								 | 
							
								            // No quotation needed
							 | 
						||
| 
								 | 
							
								            return arg;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!arg.includes('"') && !arg.includes('\\')) {
							 | 
						||
| 
								 | 
							
								            // No embedded double quotes or backslashes, so I can just wrap
							 | 
						||
| 
								 | 
							
								            // quote marks around the whole thing.
							 | 
						||
| 
								 | 
							
								            return `"${arg}"`;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // Expected input/output:
							 | 
						||
| 
								 | 
							
								        //   input : hello"world
							 | 
						||
| 
								 | 
							
								        //   output: "hello\"world"
							 | 
						||
| 
								 | 
							
								        //   input : hello""world
							 | 
						||
| 
								 | 
							
								        //   output: "hello\"\"world"
							 | 
						||
| 
								 | 
							
								        //   input : hello\world
							 | 
						||
| 
								 | 
							
								        //   output: hello\world
							 | 
						||
| 
								 | 
							
								        //   input : hello\\world
							 | 
						||
| 
								 | 
							
								        //   output: hello\\world
							 | 
						||
| 
								 | 
							
								        //   input : hello\"world
							 | 
						||
| 
								 | 
							
								        //   output: "hello\\\"world"
							 | 
						||
| 
								 | 
							
								        //   input : hello\\"world
							 | 
						||
| 
								 | 
							
								        //   output: "hello\\\\\"world"
							 | 
						||
| 
								 | 
							
								        //   input : hello world\
							 | 
						||
| 
								 | 
							
								        //   output: "hello world\\" - note the comment in libuv actually reads "hello world\"
							 | 
						||
| 
								 | 
							
								        //                             but it appears the comment is wrong, it should be "hello world\\"
							 | 
						||
| 
								 | 
							
								        let reverse = '"';
							 | 
						||
| 
								 | 
							
								        let quoteHit = true;
							 | 
						||
| 
								 | 
							
								        for (let i = arg.length; i > 0; i--) {
							 | 
						||
| 
								 | 
							
								            // walk the string in reverse
							 | 
						||
| 
								 | 
							
								            reverse += arg[i - 1];
							 | 
						||
| 
								 | 
							
								            if (quoteHit && arg[i - 1] === '\\') {
							 | 
						||
| 
								 | 
							
								                reverse += '\\';
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if (arg[i - 1] === '"') {
							 | 
						||
| 
								 | 
							
								                quoteHit = true;
							 | 
						||
| 
								 | 
							
								                reverse += '\\';
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                quoteHit = false;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        reverse += '"';
							 | 
						||
| 
								 | 
							
								        return reverse
							 | 
						||
| 
								 | 
							
								            .split('')
							 | 
						||
| 
								 | 
							
								            .reverse()
							 | 
						||
| 
								 | 
							
								            .join('');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _cloneExecOptions(options) {
							 | 
						||
| 
								 | 
							
								        options = options || {};
							 | 
						||
| 
								 | 
							
								        const result = {
							 | 
						||
| 
								 | 
							
								            cwd: options.cwd || process.cwd(),
							 | 
						||
| 
								 | 
							
								            env: options.env || process.env,
							 | 
						||
| 
								 | 
							
								            silent: options.silent || false,
							 | 
						||
| 
								 | 
							
								            windowsVerbatimArguments: options.windowsVerbatimArguments || false,
							 | 
						||
| 
								 | 
							
								            failOnStdErr: options.failOnStdErr || false,
							 | 
						||
| 
								 | 
							
								            ignoreReturnCode: options.ignoreReturnCode || false,
							 | 
						||
| 
								 | 
							
								            delay: options.delay || 10000
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        result.outStream = options.outStream || process.stdout;
							 | 
						||
| 
								 | 
							
								        result.errStream = options.errStream || process.stderr;
							 | 
						||
| 
								 | 
							
								        return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _getSpawnOptions(options, toolPath) {
							 | 
						||
| 
								 | 
							
								        options = options || {};
							 | 
						||
| 
								 | 
							
								        const result = {};
							 | 
						||
| 
								 | 
							
								        result.cwd = options.cwd;
							 | 
						||
| 
								 | 
							
								        result.env = options.env;
							 | 
						||
| 
								 | 
							
								        result['windowsVerbatimArguments'] =
							 | 
						||
| 
								 | 
							
								            options.windowsVerbatimArguments || this._isCmdFile();
							 | 
						||
| 
								 | 
							
								        if (options.windowsVerbatimArguments) {
							 | 
						||
| 
								 | 
							
								            result.argv0 = `"${toolPath}"`;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Exec a tool.
							 | 
						||
| 
								 | 
							
								     * Output will be streamed to the live console.
							 | 
						||
| 
								 | 
							
								     * Returns promise with return code
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param     tool     path to tool to exec
							 | 
						||
| 
								 | 
							
								     * @param     options  optional exec options.  See ExecOptions
							 | 
						||
| 
								 | 
							
								     * @returns   number
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    exec() {
							 | 
						||
| 
								 | 
							
								        return __awaiter(this, void 0, void 0, function* () {
							 | 
						||
| 
								 | 
							
								            return new Promise((resolve, reject) => {
							 | 
						||
| 
								 | 
							
								                this._debug(`exec tool: ${this.toolPath}`);
							 | 
						||
| 
								 | 
							
								                this._debug('arguments:');
							 | 
						||
| 
								 | 
							
								                for (const arg of this.args) {
							 | 
						||
| 
								 | 
							
								                    this._debug(`   ${arg}`);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                const optionsNonNull = this._cloneExecOptions(this.options);
							 | 
						||
| 
								 | 
							
								                if (!optionsNonNull.silent && optionsNonNull.outStream) {
							 | 
						||
| 
								 | 
							
								                    optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                const state = new ExecState(optionsNonNull, this.toolPath);
							 | 
						||
| 
								 | 
							
								                state.on('debug', (message) => {
							 | 
						||
| 
								 | 
							
								                    this._debug(message);
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								                const fileName = this._getSpawnFileName();
							 | 
						||
| 
								 | 
							
								                const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName));
							 | 
						||
| 
								 | 
							
								                const stdbuffer = '';
							 | 
						||
| 
								 | 
							
								                if (cp.stdout) {
							 | 
						||
| 
								 | 
							
								                    cp.stdout.on('data', (data) => {
							 | 
						||
| 
								 | 
							
								                        if (this.options.listeners && this.options.listeners.stdout) {
							 | 
						||
| 
								 | 
							
								                            this.options.listeners.stdout(data);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        if (!optionsNonNull.silent && optionsNonNull.outStream) {
							 | 
						||
| 
								 | 
							
								                            optionsNonNull.outStream.write(data);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        this._processLineBuffer(data, stdbuffer, (line) => {
							 | 
						||
| 
								 | 
							
								                            if (this.options.listeners && this.options.listeners.stdline) {
							 | 
						||
| 
								 | 
							
								                                this.options.listeners.stdline(line);
							 | 
						||
| 
								 | 
							
								                            }
							 | 
						||
| 
								 | 
							
								                        });
							 | 
						||
| 
								 | 
							
								                    });
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                const errbuffer = '';
							 | 
						||
| 
								 | 
							
								                if (cp.stderr) {
							 | 
						||
| 
								 | 
							
								                    cp.stderr.on('data', (data) => {
							 | 
						||
| 
								 | 
							
								                        state.processStderr = true;
							 | 
						||
| 
								 | 
							
								                        if (this.options.listeners && this.options.listeners.stderr) {
							 | 
						||
| 
								 | 
							
								                            this.options.listeners.stderr(data);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        if (!optionsNonNull.silent &&
							 | 
						||
| 
								 | 
							
								                            optionsNonNull.errStream &&
							 | 
						||
| 
								 | 
							
								                            optionsNonNull.outStream) {
							 | 
						||
| 
								 | 
							
								                            const s = optionsNonNull.failOnStdErr
							 | 
						||
| 
								 | 
							
								                                ? optionsNonNull.errStream
							 | 
						||
| 
								 | 
							
								                                : optionsNonNull.outStream;
							 | 
						||
| 
								 | 
							
								                            s.write(data);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        this._processLineBuffer(data, errbuffer, (line) => {
							 | 
						||
| 
								 | 
							
								                            if (this.options.listeners && this.options.listeners.errline) {
							 | 
						||
| 
								 | 
							
								                                this.options.listeners.errline(line);
							 | 
						||
| 
								 | 
							
								                            }
							 | 
						||
| 
								 | 
							
								                        });
							 | 
						||
| 
								 | 
							
								                    });
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                cp.on('error', (err) => {
							 | 
						||
| 
								 | 
							
								                    state.processError = err.message;
							 | 
						||
| 
								 | 
							
								                    state.processExited = true;
							 | 
						||
| 
								 | 
							
								                    state.processClosed = true;
							 | 
						||
| 
								 | 
							
								                    state.CheckComplete();
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								                cp.on('exit', (code) => {
							 | 
						||
| 
								 | 
							
								                    state.processExitCode = code;
							 | 
						||
| 
								 | 
							
								                    state.processExited = true;
							 | 
						||
| 
								 | 
							
								                    this._debug(`Exit code ${code} received from tool '${this.toolPath}'`);
							 | 
						||
| 
								 | 
							
								                    state.CheckComplete();
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								                cp.on('close', (code) => {
							 | 
						||
| 
								 | 
							
								                    state.processExitCode = code;
							 | 
						||
| 
								 | 
							
								                    state.processExited = true;
							 | 
						||
| 
								 | 
							
								                    state.processClosed = true;
							 | 
						||
| 
								 | 
							
								                    this._debug(`STDIO streams have closed for tool '${this.toolPath}'`);
							 | 
						||
| 
								 | 
							
								                    state.CheckComplete();
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								                state.on('done', (error, exitCode) => {
							 | 
						||
| 
								 | 
							
								                    if (stdbuffer.length > 0) {
							 | 
						||
| 
								 | 
							
								                        this.emit('stdline', stdbuffer);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    if (errbuffer.length > 0) {
							 | 
						||
| 
								 | 
							
								                        this.emit('errline', errbuffer);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    cp.removeAllListeners();
							 | 
						||
| 
								 | 
							
								                    if (error) {
							 | 
						||
| 
								 | 
							
								                        reject(error);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    else {
							 | 
						||
| 
								 | 
							
								                        resolve(exitCode);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.ToolRunner = ToolRunner;
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Convert an arg string to an array of args. Handles escaping
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param    argString   string of arguments
							 | 
						||
| 
								 | 
							
								 * @returns  string[]    array of arguments
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function argStringToArray(argString) {
							 | 
						||
| 
								 | 
							
								    const args = [];
							 | 
						||
| 
								 | 
							
								    let inQuotes = false;
							 | 
						||
| 
								 | 
							
								    let escaped = false;
							 | 
						||
| 
								 | 
							
								    let arg = '';
							 | 
						||
| 
								 | 
							
								    function append(c) {
							 | 
						||
| 
								 | 
							
								        // we only escape double quotes.
							 | 
						||
| 
								 | 
							
								        if (escaped && c !== '"') {
							 | 
						||
| 
								 | 
							
								            arg += '\\';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        arg += c;
							 | 
						||
| 
								 | 
							
								        escaped = false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < argString.length; i++) {
							 | 
						||
| 
								 | 
							
								        const c = argString.charAt(i);
							 | 
						||
| 
								 | 
							
								        if (c === '"') {
							 | 
						||
| 
								 | 
							
								            if (!escaped) {
							 | 
						||
| 
								 | 
							
								                inQuotes = !inQuotes;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                append(c);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (c === '\\' && escaped) {
							 | 
						||
| 
								 | 
							
								            append(c);
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (c === '\\' && inQuotes) {
							 | 
						||
| 
								 | 
							
								            escaped = true;
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (c === ' ' && !inQuotes) {
							 | 
						||
| 
								 | 
							
								            if (arg.length > 0) {
							 | 
						||
| 
								 | 
							
								                args.push(arg);
							 | 
						||
| 
								 | 
							
								                arg = '';
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        append(c);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (arg.length > 0) {
							 | 
						||
| 
								 | 
							
								        args.push(arg.trim());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return args;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								exports.argStringToArray = argStringToArray;
							 | 
						||
| 
								 | 
							
								class ExecState extends events.EventEmitter {
							 | 
						||
| 
								 | 
							
								    constructor(options, toolPath) {
							 | 
						||
| 
								 | 
							
								        super();
							 | 
						||
| 
								 | 
							
								        this.processClosed = false; // tracks whether the process has exited and stdio is closed
							 | 
						||
| 
								 | 
							
								        this.processError = '';
							 | 
						||
| 
								 | 
							
								        this.processExitCode = 0;
							 | 
						||
| 
								 | 
							
								        this.processExited = false; // tracks whether the process has exited
							 | 
						||
| 
								 | 
							
								        this.processStderr = false; // tracks whether stderr was written to
							 | 
						||
| 
								 | 
							
								        this.delay = 10000; // 10 seconds
							 | 
						||
| 
								 | 
							
								        this.done = false;
							 | 
						||
| 
								 | 
							
								        this.timeout = null;
							 | 
						||
| 
								 | 
							
								        if (!toolPath) {
							 | 
						||
| 
								 | 
							
								            throw new Error('toolPath must not be empty');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.options = options;
							 | 
						||
| 
								 | 
							
								        this.toolPath = toolPath;
							 | 
						||
| 
								 | 
							
								        if (options.delay) {
							 | 
						||
| 
								 | 
							
								            this.delay = options.delay;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    CheckComplete() {
							 | 
						||
| 
								 | 
							
								        if (this.done) {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this.processClosed) {
							 | 
						||
| 
								 | 
							
								            this._setResult();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (this.processExited) {
							 | 
						||
| 
								 | 
							
								            this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _debug(message) {
							 | 
						||
| 
								 | 
							
								        this.emit('debug', message);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    _setResult() {
							 | 
						||
| 
								 | 
							
								        // determine whether there is an error
							 | 
						||
| 
								 | 
							
								        let error;
							 | 
						||
| 
								 | 
							
								        if (this.processExited) {
							 | 
						||
| 
								 | 
							
								            if (this.processError) {
							 | 
						||
| 
								 | 
							
								                error = new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) {
							 | 
						||
| 
								 | 
							
								                error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if (this.processStderr && this.options.failOnStdErr) {
							 | 
						||
| 
								 | 
							
								                error = new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // clear the timeout
							 | 
						||
| 
								 | 
							
								        if (this.timeout) {
							 | 
						||
| 
								 | 
							
								            clearTimeout(this.timeout);
							 | 
						||
| 
								 | 
							
								            this.timeout = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.done = true;
							 | 
						||
| 
								 | 
							
								        this.emit('done', error, this.processExitCode);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    static HandleTimeout(state) {
							 | 
						||
| 
								 | 
							
								        if (state.done) {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!state.processClosed && state.processExited) {
							 | 
						||
| 
								 | 
							
								            const message = `The STDIO streams did not close within ${state.delay /
							 | 
						||
| 
								 | 
							
								                1000} seconds of the exit event from process '${state.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`;
							 | 
						||
| 
								 | 
							
								            state._debug(message);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        state._setResult();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								//# sourceMappingURL=toolrunner.js.map
							 |