'use strict';

const { exec, execSync } = require('child_process');
const path = require("path");
const { callbackify, promisify } = require("util");
const glob = promisify(require("glob"));
const fs = require("fs");
const expect = require("truffle-expect");
const debug = require("debug")("external-compile");

const runCommand = promisify(function (command, options, callback) {
  const { cwd, logger, input } = options;
  const child = exec(command, { cwd, input });

  child.stdout.on('data', function(data) {
    data = data.toString().replace(/\n$/, '');
    logger.log(data);
  });

  child.stderr.on('data', function(data) {
    data = data.toString().replace(/\n$/, '');
    logger.log(data);
  });

  child.on('close', function(code) {
    // If the command didn't exit properly, show the output and throw.
    if (code !== 0) {
      var err = new Error("Unknown exit code: " + code);
      return callback(err);
    }

    callback();
  });
});

const compile = callbackify(async function(options) {
  if (options.logger == null) {
    options.logger = console;
  }

  expect.options(options, [
    "compilers",
    "working_directory"
  ]);
  expect.options(options.compilers, ["external"]);
  expect.options(options.compilers.external, [
    "command",
    "targets"
  ]);

  const { command, targets } = options.compilers.external;
  const cwd = options.working_directory;
  const logger = options.logger;

  debug("running compile command: %s", command);
  await runCommand(command, { cwd, logger });

  const contracts = {};
  for (let target of targets) {
    const pattern = path.join(cwd, target.path);

    for (let preprocessed of await glob( pattern, { follow: true })) {
      debug("processing target: %s", preprocessed);
      const input = fs.readFileSync(preprocessed).toString();

      const output = (target.command)
        ? execSync(target.command, { cwd, input })
        : input;
      const contract = JSON.parse(output);
      contracts[contract.contractName] = contract;
    }
  }

  return contracts;
});

compile.all = compile;
compile.necessary = compile;

module.exports = compile;

@@ -1563,24 +1563,131 @@
  targets) %7B%0A
+    expect.one(target, %5B %22path%22, %22command%22 %5D);  // also allows both%0A%0A    if (target.path != undefined) %7B%0A  
     const pa
@@ -1720,24 +1720,26 @@
 get.path);%0A%0A
+  
     for (let
@@ -1793,24 +1793,26 @@
  true %7D)) %7B%0A
+  
       debug(
@@ -1853,24 +1853,26 @@
 sed);%0A      
+  
 const input 
@@ -1918,24 +1918,26 @@
 g();%0A%0A      
+  
 const output
@@ -1964,16 +1964,18 @@
         
+ 
+ 
 ? execSy
@@ -2017,16 +2017,18 @@
         
+  
 : input;
@@ -2028,16 +2028,191 @@
  input;%0A
+        const contract = JSON.parse(output);%0A        contracts%5Bcontract.contractName%5D = contract;%0A      %7D%0A    %7D else %7B%0A      const output = execSync(target.command, %7B cwd %7D);%0A
       co

Allow optional target.path in external compilation