<commit_before>// npm build command

// everything about the installation after the creation of
// the .npm/{name}/{version}/package folder.
// linking the modules into the npm.root,
// resolving dependencies, etc.

// This runs AFTER install or link are completed.

var npm = require('./npm.js')
var log = require('npmlog')
var chain = require('slide').chain
var fs = require('graceful-fs')
var path = require('path')
var lifecycle = require('./utils/lifecycle.js')
var readJson = require('read-package-json')
var link = require('./utils/link.js')
var linkIfExists = link.ifExists
var cmdShim = require('cmd-shim')
var cmdShimIfExists = cmdShim.ifExists
var asyncMap = require('slide').asyncMap
var ini = require('ini')
var writeFile = require('write-file-atomic')
var getPackageId = require('./install/get-package-id.js')

module.exports = build
build.usage = 'npm build [<folder>]'

build._didBuild = {}
build._noLC = {}
function build (args, global, didPre, didRB, cb) {
  if (typeof cb !== 'function') {
    cb = didRB
    didRB = false
  }
  if (typeof cb !== 'function') {
    cb = didPre
    didPre = false
  }
  if (typeof cb !== 'function') {
    cb = global
    global = npm.config.get('global')
  }

  // it'd be nice to asyncMap these, but actually, doing them
  // in parallel generally munges up the output from node-waf
  var builder = build_(global, didPre, didRB)
  chain(args.map(function (arg) {
    return function (cb) {
      builder(arg, cb)
    }
  }), cb)
}

function build_ (global, didPre, didRB) {
  return function (folder, cb) {
    folder = path.resolve(folder)
    if (build._didBuild[folder]) log.info('build', 'already built', folder)
    build._didBuild[folder] = true
    log.info('build', folder)
    readJson(path.resolve(folder, 'package.json'), function (er, pkg) {
      if (er) return cb(er)
      chain([
        !didPre && [lifecycle, pkg, 'preinstall', folder],
        [linkStuff, pkg, folder, global, didRB],
        [writeBuiltinConf, pkg, folder],
        didPre !== build._noLC && [lifecycle, pkg, 'install', folder],
        didPre !== build._noLC && [lifecycle, pkg, 'postinstall', folder],
        didPre !== build._noLC && npm.config.get('npat') && [lifecycle, pkg, 'test', folder]
      ],
      cb)
    })
  }
}

var writeBuiltinConf = build.writeBuiltinConf = function (pkg, folder, cb) {
  // the builtin config is "sticky". Any time npm installs
  // itself globally, it puts its builtin config file there
  var parent = path.dirname(folder)
  var dir = npm.globalDir

  if (pkg.name !== 'npm' ||
      !npm.config.get('global') ||
      !npm.config.usingBuiltin ||
      dir !== parent) {
    return cb()
  }

  var data = ini.stringify(npm.config.sources.builtin.data)
  writeFile(path.resolve(folder, 'npmrc'), data, cb)
}

var linkStuff = build.linkStuff = function (pkg, folder, global, didRB, cb) {
  // allow to opt out of linking binaries.
  if (npm.config.get('bin-links') === false) return cb()

  // if it's global, and folder is in {prefix}/node_modules,
  // then bins are in {prefix}/bin
  // otherwise, then bins are in folder/../.bin
  var parent = pkg.name && pkg.name[0] === '@' ? path.dirname(path.dirname(folder)) : path.dirname(folder)
  var gnm = global && npm.globalDir
  var gtop = parent === gnm

  log.info('linkStuff', getPackageId(pkg))
  log.silly('linkStuff', getPackageId(pkg), 'has', parent, 'as its parent node_modules')
  if (global) log.silly('linkStuff', getPackageId(pkg), 'is part of a global install')
  if (gnm) log.silly('linkStuff', getPackageId(pkg), 'is installed into a global node_modules')
  if (gtop) log.silly('linkStuff', getPackageId(pkg), 'is installed into the top-level global node_modules')

  shouldWarn(pkg, folder, global, function () {
    asyncMap(
      [linkBins, linkMans, !didRB && rebuildBundles],
      function (fn, cb) {
        if (!fn) return cb()
        log.verbose(fn.name, getPackageId(pkg))
        fn(pkg, folder, parent, gtop, cb)
      },
      cb
    )
  })
}

function shouldWarn (pkg, folder, global, cb) {
  var parent = path.dirname(folder)
  var top = parent === npm.dir
  var cwd = npm.localPrefix

  readJson(path.resolve(cwd, 'package.json'), function (er, topPkg) {
    if (er) return cb(er)

    var linkedPkg = path.basename(cwd)
    var currentPkg = path.basename(folder)

    // current searched package is the linked package on first call
    if (linkedPkg !== currentPkg) {

      // don't generate a warning if it's listed in dependencies
      if (Object.keys(topPkg.dependencies || {}).indexOf(currentPkg) === -1) {

        if (top && pkg.preferGlobal && !global) {
          log.warn('prefer global', getPackageId(pkg) + ' should be installed with -g')
        }
      }
    }

    cb()
  })
}

function rebuildBundles (pkg, folder, parent, gtop, cb) {
  if (!npm.config.get('rebuild-bundle')) return cb()

  var deps = Object.keys(pkg.dependencies || {})
             .concat(Object.keys(pkg.devDependencies || {}))
  var bundles = pkg.bundleDependencies || pkg.bundledDependencies || []

  fs.readdir(path.resolve(folder, 'node_modules'), function (er, files) {
    // error means no bundles
    if (er) return cb()

    log.verbose('rebuildBundles', files)
    // don't asyncMap these, because otherwise build script output
    // gets interleaved and is impossible to read
    chain(files.filter(function (file) {
      // rebuild if:
      // not a .folder, like .bin or .hooks
      return !file.match(/^[\._-]/) &&
          // not some old 0.x style bundle
          file.indexOf('@') === -1 &&
          // either not a dep, or explicitly bundled
          (deps.indexOf(file) === -1 || bundles.indexOf(file) !== -1)
    }).map(function (file) {
      file = path.resolve(folder, 'node_modules', file)
      return function (cb) {
        if (build._didBuild[file]) return cb()
        log.verbose('rebuild bundle', file)
        // if file is not a package dir, then don't do it.
        fs.lstat(path.resolve(file, 'package.json'), function (er) {
          if (er) return cb()
          build_(false)(file, cb)
        })
      }
    }), cb)
  })
}

function linkBins (pkg, folder, parent, gtop, cb) {
  if (!pkg.bin || !gtop && path.basename(parent) !== 'node_modules') {
    return cb()
  }
  var binRoot = gtop ? npm.globalBin
                     : path.resolve(parent, '.bin')
  log.verbose('link bins', [pkg.bin, binRoot, gtop])

  asyncMap(Object.keys(pkg.bin), function (b, cb) {
    linkBin(
      path.resolve(folder, pkg.bin[b]),
      path.resolve(binRoot, b),
      gtop && folder,
      function (er) {
        if (er) return cb(er)
        // bins should always be executable.
        // XXX skip chmod on windows?
        var src = path.resolve(folder, pkg.bin[b])
        fs.chmod(src, npm.modes.exec, function (er) {
          if (er && er.code === 'ENOENT' && npm.config.get('ignore-scripts')) {
            return cb()
          }
          if (er || !gtop) return cb(er)
          var dest = path.resolve(binRoot, b)
          var out = npm.config.get('parseable')
                  ? dest + '::' + src + ':BINFILE'
                  : dest + ' -> ' + src
          log.clearProgress()
          console.log(out)
          log.showProgress()
          cb()
        })
      }
    )
  }, cb)
}

function linkBin (from, to, gently, cb) {
  if (process.platform !== 'win32') {
    return linkIfExists(from, to, gently, cb)
  } else {
    return cmdShimIfExists(from, to, cb)
  }
}

function linkMans (pkg, folder, parent, gtop, cb) {
  if (!pkg.man || !gtop || process.platform === 'win32') return cb()

  var manRoot = path.resolve(npm.config.get('prefix'), 'share', 'man')
  log.verbose('linkMans', 'man files are', pkg.man, 'in', manRoot)

  // make sure that the mans are unique.
  // otherwise, if there are dupes, it'll fail with EEXIST
  var set = pkg.man.reduce(function (acc, man) {
    acc[path.basename(man)] = man
    return acc
  }, {})
  pkg.man = pkg.man.filter(function (man) {
    return set[path.basename(man)] === man
  })

  asyncMap(pkg.man, function (man, cb) {
    if (typeof man !== 'string') return cb()
    log.silly('linkMans', 'preparing to link', man)
    var parseMan = man.match(/(.*\.([0-9]+)(\.gz)?)$/)
    if (!parseMan) {
      return cb(new Error(
        man + ' is not a valid name for a man file.  ' +
        'Man files must end with a number, ' +
        'and optionally a .gz suffix if they are compressed.'
      ))
    }

    var stem = parseMan[1]
    var sxn = parseMan[2]
    var bn = path.basename(stem)
    var manSrc = path.resolve(folder, man)
    var manDest = path.join(manRoot, 'man' + sxn, bn)

    linkIfExists(manSrc, manDest, gtop && folder, cb)
  }, cb)
}
<commit_msg>install: don't warn on preferGlobal for devDeps<commit_after>// npm build command

// everything about the installation after the creation of
// the .npm/{name}/{version}/package folder.
// linking the modules into the npm.root,
// resolving dependencies, etc.

// This runs AFTER install or link are completed.

var npm = require('./npm.js')
var log = require('npmlog')
var chain = require('slide').chain
var fs = require('graceful-fs')
var path = require('path')
var lifecycle = require('./utils/lifecycle.js')
var readJson = require('read-package-json')
var link = require('./utils/link.js')
var linkIfExists = link.ifExists
var cmdShim = require('cmd-shim')
var cmdShimIfExists = cmdShim.ifExists
var asyncMap = require('slide').asyncMap
var ini = require('ini')
var writeFile = require('write-file-atomic')
var getPackageId = require('./install/get-package-id.js')

module.exports = build
build.usage = 'npm build [<folder>]'

build._didBuild = {}
build._noLC = {}
function build (args, global, didPre, didRB, cb) {
  if (typeof cb !== 'function') {
    cb = didRB
    didRB = false
  }
  if (typeof cb !== 'function') {
    cb = didPre
    didPre = false
  }
  if (typeof cb !== 'function') {
    cb = global
    global = npm.config.get('global')
  }

  // it'd be nice to asyncMap these, but actually, doing them
  // in parallel generally munges up the output from node-waf
  var builder = build_(global, didPre, didRB)
  chain(args.map(function (arg) {
    return function (cb) {
      builder(arg, cb)
    }
  }), cb)
}

function build_ (global, didPre, didRB) {
  return function (folder, cb) {
    folder = path.resolve(folder)
    if (build._didBuild[folder]) log.info('build', 'already built', folder)
    build._didBuild[folder] = true
    log.info('build', folder)
    readJson(path.resolve(folder, 'package.json'), function (er, pkg) {
      if (er) return cb(er)
      chain([
        !didPre && [lifecycle, pkg, 'preinstall', folder],
        [linkStuff, pkg, folder, global, didRB],
        [writeBuiltinConf, pkg, folder],
        didPre !== build._noLC && [lifecycle, pkg, 'install', folder],
        didPre !== build._noLC && [lifecycle, pkg, 'postinstall', folder],
        didPre !== build._noLC && npm.config.get('npat') && [lifecycle, pkg, 'test', folder]
      ],
      cb)
    })
  }
}

var writeBuiltinConf = build.writeBuiltinConf = function (pkg, folder, cb) {
  // the builtin config is "sticky". Any time npm installs
  // itself globally, it puts its builtin config file there
  var parent = path.dirname(folder)
  var dir = npm.globalDir

  if (pkg.name !== 'npm' ||
      !npm.config.get('global') ||
      !npm.config.usingBuiltin ||
      dir !== parent) {
    return cb()
  }

  var data = ini.stringify(npm.config.sources.builtin.data)
  writeFile(path.resolve(folder, 'npmrc'), data, cb)
}

var linkStuff = build.linkStuff = function (pkg, folder, global, didRB, cb) {
  // allow to opt out of linking binaries.
  if (npm.config.get('bin-links') === false) return cb()

  // if it's global, and folder is in {prefix}/node_modules,
  // then bins are in {prefix}/bin
  // otherwise, then bins are in folder/../.bin
  var parent = pkg.name && pkg.name[0] === '@' ? path.dirname(path.dirname(folder)) : path.dirname(folder)
  var gnm = global && npm.globalDir
  var gtop = parent === gnm

  log.info('linkStuff', getPackageId(pkg))
  log.silly('linkStuff', getPackageId(pkg), 'has', parent, 'as its parent node_modules')
  if (global) log.silly('linkStuff', getPackageId(pkg), 'is part of a global install')
  if (gnm) log.silly('linkStuff', getPackageId(pkg), 'is installed into a global node_modules')
  if (gtop) log.silly('linkStuff', getPackageId(pkg), 'is installed into the top-level global node_modules')

  shouldWarn(pkg, folder, global, function () {
    asyncMap(
      [linkBins, linkMans, !didRB && rebuildBundles],
      function (fn, cb) {
        if (!fn) return cb()
        log.verbose(fn.name, getPackageId(pkg))
        fn(pkg, folder, parent, gtop, cb)
      },
      cb
    )
  })
}

function shouldWarn (pkg, folder, global, cb) {
  var parent = path.dirname(folder)
  var top = parent === npm.dir
  var cwd = npm.localPrefix

  readJson(path.resolve(cwd, 'package.json'), function (er, topPkg) {
    if (er) return cb(er)

    var linkedPkg = path.basename(cwd)
    var currentPkg = path.basename(folder)

    // current searched package is the linked package on first call
    if (linkedPkg !== currentPkg) {

      // don't generate a warning if it's listed in dependencies
      if (Object.keys(topPkg.dependencies || {})
          .concat(Object.keys(topPkg.devDependencies || {}))
          .indexOf(currentPkg) === -1) {

        if (top && pkg.preferGlobal && !global) {
          log.warn('prefer global', getPackageId(pkg) + ' should be installed with -g')
        }
      }
    }

    cb()
  })
}

function rebuildBundles (pkg, folder, parent, gtop, cb) {
  if (!npm.config.get('rebuild-bundle')) return cb()

  var deps = Object.keys(pkg.dependencies || {})
             .concat(Object.keys(pkg.devDependencies || {}))
  var bundles = pkg.bundleDependencies || pkg.bundledDependencies || []

  fs.readdir(path.resolve(folder, 'node_modules'), function (er, files) {
    // error means no bundles
    if (er) return cb()

    log.verbose('rebuildBundles', files)
    // don't asyncMap these, because otherwise build script output
    // gets interleaved and is impossible to read
    chain(files.filter(function (file) {
      // rebuild if:
      // not a .folder, like .bin or .hooks
      return !file.match(/^[\._-]/) &&
          // not some old 0.x style bundle
          file.indexOf('@') === -1 &&
          // either not a dep, or explicitly bundled
          (deps.indexOf(file) === -1 || bundles.indexOf(file) !== -1)
    }).map(function (file) {
      file = path.resolve(folder, 'node_modules', file)
      return function (cb) {
        if (build._didBuild[file]) return cb()
        log.verbose('rebuild bundle', file)
        // if file is not a package dir, then don't do it.
        fs.lstat(path.resolve(file, 'package.json'), function (er) {
          if (er) return cb()
          build_(false)(file, cb)
        })
      }
    }), cb)
  })
}

function linkBins (pkg, folder, parent, gtop, cb) {
  if (!pkg.bin || !gtop && path.basename(parent) !== 'node_modules') {
    return cb()
  }
  var binRoot = gtop ? npm.globalBin
                     : path.resolve(parent, '.bin')
  log.verbose('link bins', [pkg.bin, binRoot, gtop])

  asyncMap(Object.keys(pkg.bin), function (b, cb) {
    linkBin(
      path.resolve(folder, pkg.bin[b]),
      path.resolve(binRoot, b),
      gtop && folder,
      function (er) {
        if (er) return cb(er)
        // bins should always be executable.
        // XXX skip chmod on windows?
        var src = path.resolve(folder, pkg.bin[b])
        fs.chmod(src, npm.modes.exec, function (er) {
          if (er && er.code === 'ENOENT' && npm.config.get('ignore-scripts')) {
            return cb()
          }
          if (er || !gtop) return cb(er)
          var dest = path.resolve(binRoot, b)
          var out = npm.config.get('parseable')
                  ? dest + '::' + src + ':BINFILE'
                  : dest + ' -> ' + src
          log.clearProgress()
          console.log(out)
          log.showProgress()
          cb()
        })
      }
    )
  }, cb)
}

function linkBin (from, to, gently, cb) {
  if (process.platform !== 'win32') {
    return linkIfExists(from, to, gently, cb)
  } else {
    return cmdShimIfExists(from, to, cb)
  }
}

function linkMans (pkg, folder, parent, gtop, cb) {
  if (!pkg.man || !gtop || process.platform === 'win32') return cb()

  var manRoot = path.resolve(npm.config.get('prefix'), 'share', 'man')
  log.verbose('linkMans', 'man files are', pkg.man, 'in', manRoot)

  // make sure that the mans are unique.
  // otherwise, if there are dupes, it'll fail with EEXIST
  var set = pkg.man.reduce(function (acc, man) {
    acc[path.basename(man)] = man
    return acc
  }, {})
  pkg.man = pkg.man.filter(function (man) {
    return set[path.basename(man)] === man
  })

  asyncMap(pkg.man, function (man, cb) {
    if (typeof man !== 'string') return cb()
    log.silly('linkMans', 'preparing to link', man)
    var parseMan = man.match(/(.*\.([0-9]+)(\.gz)?)$/)
    if (!parseMan) {
      return cb(new Error(
        man + ' is not a valid name for a man file.  ' +
        'Man files must end with a number, ' +
        'and optionally a .gz suffix if they are compressed.'
      ))
    }

    var stem = parseMan[1]
    var sxn = parseMan[2]
    var bn = path.basename(stem)
    var manSrc = path.resolve(folder, man)
    var manDest = path.join(manRoot, 'man' + sxn, bn)

    linkIfExists(manSrc, manDest, gtop && folder, cb)
  }, cb)
}
<|endoftext|>


<commit_before>'use strict';

const parser = require('../parser');
const sort   = require('./sort');
const Router = require('./router');

let show = function (trace) {
  let r = '';

  if (trace.segment)
    r += trace.segment.join('/');

  if (trace.query)
    r += '?' + trace.query.map(xs => xs[0] + (xs[1] ? '=' + xs[1] : '')).join('&');

  if (trace.fragment)
    r += '#' + trace.fragment[0];

  if (trace.method)
    r = (trace.method[0] === '[.*]' ? 'ALL' : trace.method[0]) + ' ' + r;

  if (trace.header)
    r += ' {' + trace.header.map(xs => xs[0] + (xs[1] ? ': ' + xs[1] : '')).join(', ') + '}';

  return r;
};
let trace = function (node, _state) {
  _state = _state || {};
  _state[node.type] = _state[node.type] || [];

  if (node.pattern === 'pair')
    _state[node.type].unshift([node.key.org, node.value && node.value.org]);
  else if (node.pattern !== 'null')
    _state[node.type].unshift(node.org);

  if (node.parent)
    return trace(node.parent, _state);
  return show(_state);
};
let walk = function (tree, f) {
  return tree
    .map(x => x.type === 'handler' ? [f(x)] : [])
    .concat(tree.map(x => walk(x.children || [], f)))
    .reduce((x,y) => x.concat(y), []);
};

let parse = function (pattern, startRule) {
  try {

    return parser.parse(pattern, {startRule});

  } catch (err) {

    let msg = 'in URL pattern `' + pattern + '`';
    if (err.location)
      msg += ' at column ' + err.location.start.column;
    msg += ', ' + err.message;

    throw new SyntaxError(msg);
  }
};
let merge = (routes, opts) => sort(routes).reduce((merged, route) => {
  let node = merged.find(x => !sort.type(route, [x]));

  if (!node && opts.case) {
    let lower = x => {
      let r = {
        type   : x.type,
        pattern: x.pattern,
        org    : x.org && x.org.toLowerCase(),
        order  : x.order
      };
      if (x.key) r.key = {
        type   : x.key.type,
        pattern: x.key.pattern,
        org    : x.key.org && x.key.org.toLowerCase()
      };
      if (x.value) r.value = {
        type   : x.value.type,
        pattern: x.value.pattern,
        org    : x.value.org && x.value.org.toLowerCase()
      };
      return [r];
    };

    let fail = merged.find(x => !sort.type(lower(route[0]), lower(x)));
    if (fail)
      throw new Error('case conflict `' + trace(route[0]) + '` vs `' + trace(fail) + '`');
  }

  if (route.length === 1 && node)
    throw new Error('duplicate route `' + trace(node) + '`');

  if (node) {
    node.children = (node.children || []).concat([route.slice(1)]);
  } else {
    merged.push(route[0]);
    if (route.length > 1)
      route[0].children = [route.slice(1)];
  }

  return merged;
}, []).map(node => {
  if (node.children) {
    node.children.forEach(x => x[0].parent = node);
    node.children = merge(node.children, opts);
  }
  return node;
});
let toTree = (routes, opts) => merge(routes.map((route, i) => {
  let xs = parse(route.url, 'route');

  xs.push(parse(route.method || '[.*]', 'method'));

  let headers = route.header || route.headers || [];
  if (!(headers instanceof Array))
    headers = [headers];

  headers.forEach(x => {
    if (typeof x === 'string') {
      xs.push({
        type   : 'header',
        pattern: 'pair',
        key    : parse(x, 'key'),
        value  : { type: 'value', pattern: 'variable', match: /^(.*)$/ }
      });
    } else if (typeof x === 'object') {
      Object.keys(x).forEach(i => xs.push({
        type   : 'header',
        pattern: 'pair',
        key    : parse(i, 'key'),
        value  : parse(x[i], 'value')
      }));
    } else {
      throw new TypeError('header must be either a String, Object or Array');
    }
  });

  xs.push({type: 'handler', handler: route.handler, middleware: route.middleware});

  xs.forEach(x => x.order = i);

  if (opts.trim) {
    let i = xs.findIndex(x => x.type === 'segment' && x.pattern === 'null');
    let b = i > 1 &&
            xs[i-1].type === 'segment' &&
            xs[i-1].pattern === 'absolute' &&
            xs[i-1].org === '';
    if (b)
      xs.splice(i-1, 1);
  }

  return xs;
}), opts);

let stringEquals = function (str, caseInsensitive) {
  if (!caseInsensitive)
    return x => x === str;

  str = str.toLowerCase();
  return x => (x || '').toLowerCase() === str;
};
let regexpMatch = function (regexp, caseInsensitive) {
  if (!caseInsensitive)
    return regexp;
  return new RegExp(regexp.source, 'i');
};
let toPredicate = function (node, caseInsensitive) {
  switch (node.pattern) {
    case 'null'       : return Router.satisfy(x => x === undefined);
    case 'absolute'   : return Router.satisfy(stringEquals(node.match, caseInsensitive));
    case 'conditional': return x => Router.match(regexpMatch(node.match, caseInsensitive))(x).save();
    case 'variable'   : return x => Router.match(regexpMatch(node.match, caseInsensitive))(x).save();
    case 'glob'       : return x => Router.match(regexpMatch(node.match, caseInsensitive))(x).save();
  }
};
let toRouter = function (node, opts) {
  let router;
  let children;

  if (node.children && node.children.length === 1)
    children = node.children.map(x => toRouter(x, opts))[0];
  else if (node.children && node.children.length > 1)
    children = Router.choice(node.children.map(x => toRouter(x, opts)));

  if (node.type === 'handler')
    return Router.of(node);

  let makeRouter = Router[node.type];

  switch (node.pattern) {
    case 'null':
      router = makeRouter(toPredicate(node, opts.case));
      if (opts.trim)
        router = makeRouter(Router.satisfy(x => !x)).then(router);
      break;

    case 'absolute':
      router = makeRouter(toPredicate(node, opts.case));
      break;

    case 'conditional':
      router = makeRouter(toPredicate(node, opts.case));
      break;

    case 'variable':
      router = makeRouter(toPredicate(node, opts.case));
      break;

    case 'glob':
      router = Router.segments(toPredicate(node, opts.case), children);
      return router;

    case 'pair':
      router = makeRouter(toPredicate(node.key, opts.case), toPredicate(node.value, opts.case));
      break;
  }

  if (children)
    router = router.then(children);

  return router;
};

let build = function (routes, opts) {
  let tree = toTree(routes, opts);

  if (opts.debug)
    walk(tree, trace).forEach(x => console.log(x));

  let router = Router.choice(tree.map(x => toRouter(x, opts)));

  walk(tree, x => {
    delete x.parent;
    delete x.order;
  });

  return router;
};

module.exports = build;
<commit_msg>add another debug option<commit_after>'use strict';

const parser = require('../parser');
const sort   = require('./sort');
const Router = require('./router');

let show = function (trace) {
  let r = '';

  if (trace.segment)
    r += trace.segment.join('/');

  if (trace.query)
    r += '?' + trace.query.map(xs => xs[0] + (xs[1] ? '=' + xs[1] : '')).join('&');

  if (trace.fragment)
    r += '#' + trace.fragment[0];

  if (trace.method)
    r = (trace.method[0] === '[.*]' ? 'ALL' : trace.method[0]) + ' ' + r;

  if (trace.header)
    r += ' {' + trace.header.map(xs => xs[0] + (xs[1] ? ': ' + xs[1] : '')).join(', ') + '}';

  return r;
};
let trace = function (node, _state, opts) {
  _state = _state || {};
  _state[node.type] = _state[node.type] || [];

  if (node.pattern === 'pair')
    _state[node.type].unshift([node.key.org, node.value && node.value.org]);
  else if (node.pattern !== 'null')
    _state[node.type].unshift(node.org);

  if (node.parent)
    return trace(node.parent, _state, opts);
  if (opts && opts.debug)
    return _state;
  return show(_state);
};
let walk = function (tree, f) {
  return tree
    .map(x => x.type === 'handler' ? [f(x)] : [])
    .concat(tree.map(x => walk(x.children || [], f)))
    .reduce((x,y) => x.concat(y), []);
};

let parse = function (pattern, startRule) {
  try {

    return parser.parse(pattern, {startRule});

  } catch (err) {

    let msg = 'in URL pattern `' + pattern + '`';
    if (err.location)
      msg += ' at column ' + err.location.start.column;
    msg += ', ' + err.message;

    throw new SyntaxError(msg);
  }
};
let merge = (routes, opts) => sort(routes).reduce((merged, route) => {
  let node = merged.find(x => !sort.type(route, [x]));

  if (!node && opts.case) {
    let lower = x => {
      let r = {
        type   : x.type,
        pattern: x.pattern,
        org    : x.org && x.org.toLowerCase(),
        order  : x.order
      };
      if (x.key) r.key = {
        type   : x.key.type,
        pattern: x.key.pattern,
        org    : x.key.org && x.key.org.toLowerCase()
      };
      if (x.value) r.value = {
        type   : x.value.type,
        pattern: x.value.pattern,
        org    : x.value.org && x.value.org.toLowerCase()
      };
      return [r];
    };

    let fail = merged.find(x => !sort.type(lower(route[0]), lower(x)));
    if (fail)
      throw new Error('case conflict `' + trace(route[0]) + '` vs `' + trace(fail) + '`');
  }

  if (route.length === 1 && node)
    throw new Error('duplicate route `' + trace(node) + '`');

  if (node) {
    node.children = (node.children || []).concat([route.slice(1)]);
  } else {
    merged.push(route[0]);
    if (route.length > 1)
      route[0].children = [route.slice(1)];
  }

  return merged;
}, []).map(node => {
  if (node.children) {
    node.children.forEach(x => x[0].parent = node);
    node.children = merge(node.children, opts);
  }
  return node;
});
let toTree = (routes, opts) => merge(routes.map((route, i) => {
  let xs = parse(route.url, 'route');

  xs.push(parse(route.method || '[.*]', 'method'));

  let headers = route.header || route.headers || [];
  if (!(headers instanceof Array))
    headers = [headers];

  headers.forEach(x => {
    if (typeof x === 'string') {
      xs.push({
        type   : 'header',
        pattern: 'pair',
        key    : parse(x, 'key'),
        value  : { type: 'value', pattern: 'variable', match: /^(.*)$/ }
      });
    } else if (typeof x === 'object') {
      Object.keys(x).forEach(i => xs.push({
        type   : 'header',
        pattern: 'pair',
        key    : parse(i, 'key'),
        value  : parse(x[i], 'value')
      }));
    } else {
      throw new TypeError('header must be either a String, Object or Array');
    }
  });

  xs.push({type: 'handler', handler: route.handler, middleware: route.middleware});

  xs.forEach(x => x.order = i);

  if (opts.trim) {
    let i = xs.findIndex(x => x.type === 'segment' && x.pattern === 'null');
    let b = i > 1 &&
            xs[i-1].type === 'segment' &&
            xs[i-1].pattern === 'absolute' &&
            xs[i-1].org === '';
    if (b)
      xs.splice(i-1, 1);
  }

  return xs;
}), opts);

let stringEquals = function (str, caseInsensitive) {
  if (!caseInsensitive)
    return x => x === str;

  str = str.toLowerCase();
  return x => (x || '').toLowerCase() === str;
};
let regexpMatch = function (regexp, caseInsensitive) {
  if (!caseInsensitive)
    return regexp;
  return new RegExp(regexp.source, 'i');
};
let toPredicate = function (node, caseInsensitive) {
  switch (node.pattern) {
    case 'null'       : return Router.satisfy(x => x === undefined);
    case 'absolute'   : return Router.satisfy(stringEquals(node.match, caseInsensitive));
    case 'conditional': return x => Router.match(regexpMatch(node.match, caseInsensitive))(x).save();
    case 'variable'   : return x => Router.match(regexpMatch(node.match, caseInsensitive))(x).save();
    case 'glob'       : return x => Router.match(regexpMatch(node.match, caseInsensitive))(x).save();
  }
};
let toRouter = function (node, opts) {
  let router;
  let children;

  if (node.children && node.children.length === 1)
    children = node.children.map(x => toRouter(x, opts))[0];
  else if (node.children && node.children.length > 1)
    children = Router.choice(node.children.map(x => toRouter(x, opts)));

  if (node.type === 'handler')
    return Router.of(node);

  let makeRouter = Router[node.type];

  switch (node.pattern) {
    case 'null':
      router = makeRouter(toPredicate(node, opts.case));
      if (opts.trim)
        router = makeRouter(Router.satisfy(x => !x)).then(router);
      break;

    case 'absolute':
      router = makeRouter(toPredicate(node, opts.case));
      break;

    case 'conditional':
      router = makeRouter(toPredicate(node, opts.case));
      break;

    case 'variable':
      router = makeRouter(toPredicate(node, opts.case));
      break;

    case 'glob':
      router = Router.segments(toPredicate(node, opts.case), children);
      return router;

    case 'pair':
      router = makeRouter(toPredicate(node.key, opts.case), toPredicate(node.value, opts.case));
      break;
  }

  if (children)
    router = router.then(children);

  return router;
};

let build = function (routes, opts) {
  let tree = toTree(routes, opts);

  if (opts.debug)
    walk(tree, trace).forEach(x => console.log(x));

  if (typeof opts.tree === 'function')
    opts.tree(walk(tree, (n, s) => trace(n, s, {debug: true})));

  let router = Router.choice(tree.map(x => toRouter(x, opts)));

  walk(tree, x => {
    delete x.parent;
    delete x.order;
  });

  return router;
};

module.exports = build;
<|endoftext|>


<commit_before>var Cache = module.exports = function Cache() {
    this.storage = {};
};

Cache.prototype.has = function(key) {
    return this.storage.hasOwnProperty(key);
};

Cache.prototype.get = function(key) {
    return this.storage[key];
};

Cache.prototype.flush = function(key) {
    this.storage[key] = null;
};

Cache.prototype.store = function(key, value) {
    this.storage[key] = value;
};
<commit_msg>Added Cache#len()<commit_after>var Cache = module.exports = function Cache() {
    this.storage = {};
};

Cache.prototype.has = function(key) {
    return this.storage.hasOwnProperty(key);
};

Cache.prototype.len = function() {
    return Object.keys(this.storage).length;
};

Cache.prototype.get = function(key) {
    return this.storage[key];
};

Cache.prototype.flush = function(key) {
    this.storage[key] = null;
};

Cache.prototype.store = function(key, value) {
    this.storage[key] = value;
};
<|endoftext|>


<commit_before>function Cache() {
  this.everyone = [];
  this.users = [];
  this.admin = [];
  return this;
}

module.exports = Cache;

Cache.prototype.compile = function (scope) {
  var self = this;
  var pathPatt = /^\/.*$/;
  if (typeof self.compiled === 'undefined')
    self.compiled = {};
  var apps = [];
  for (var name in self[scope]) {
    var app = {
      name: name,
      index: self[scope][name].index
    }
    if (self[scope][name].position)
      app.position = self[scope][name].position;
    if (self[scope][name].href)
      app.href = self[scope][name].href;
    else {
      app.views = [];
      for (var path in self[scope][name]) {
        if (typeof self[scope][name][path].title === 'undefined')
          continue;
        if (pathPatt.test(path))
          app.views.push({
            path: path,
            title: self[scope][name][path].title
          });
        else app[path] = self[scope][name][path];
      }
    }
    apps.push(app);
  }
  self.compiled[scope] = apps;
};

Cache.prototype.update = function (apps) {
  this.everyone = {};
  this.users = {};
  this.admin = {};
  return this.init(apps);
};

Cache.prototype.get = function (scope) {
  var self = this;
  if (typeof scope === 'undefined' || scope === 'everyone') {
    return self.everyone;
  } else if (scope === 'users') {
    return self.users;
  } else if (scope === 'admin') {
    return self.admin;
  }
};

Cache.prototype.add = function (app) {
  var self = this;
  var routes = {};
  if (app.ui) {
    if (app.ui.views) {
      var tmp = {
        everyone: [],
        users: [],
        admin: []
      }
      for (var path in app.ui.views) {
        if (typeof app.ui.views[path].scope === 'undefined' || app.ui.views[path].title === 'undefined')
          continue;
        switch (app.ui.views[path].scope) {
          case 'everyone':
            tmp.everyone.push({
              path: app.path + path.replace(/:[a-z]+/g, ''),
              title: app.ui.views[path].title
            });
            if (app.ui.views[path].exclusive)
              break;
            /* falls through */
          case 'users':
            tmp.users.push({
              path: app.path + path.replace(/:[a-z]+/g, ''),
              title: app.ui.views[path].title
            });
            if (app.ui.views[path].exclusive)
              break;
            /* falls through */
          case 'admin':
            tmp.admin.push({
              path: app.path + path.replace(/:[a-z]+/g, ''),
              title: app.ui.views[path].title
            });
            if (app.ui.views[path].exclusive)
              break;
        }
      }
      if (tmp.everyone.length > 0)
        self.everyone.push({
          base: app.path,
          icon: app.ui.icon,
          name: app.ui.navName,
          index: app.ui.index,
          views: tmp.everyone,
          position: app.ui.position
        });
      if (tmp.users.length > 0)
        self.users.push({
          base: app.path,
          icon: app.ui.icon,
          name: app.ui.navName,
          index: app.ui.index,
          views: tmp.users,
          position: app.ui.position
        });
      if (tmp.admin.length > 0)
        self.admin.push({
          base: app.path,
          icon: app.ui.icon,
          name: app.ui.navName,
          index: app.ui.index,
          views: tmp.admin,
          position: app.ui.position
        });
    }
    if (app.ui.href) {
      var link = {
        icon: app.ui.icon,
        name: app.ui.navName,
        index: app.ui.index,
        href: app.ui.href,
        position: app.ui.position
      };
      self.everyone.push(link);
      self.users.push(link);
      self.admin.push(link);
    }
  }
  return self;
};

Cache.prototype.init = function (apps) {
  var self = this;
  for (var app in apps) {
    self.add(apps[app]);
  }
  return self;
};
<commit_msg>Fixed an issue where paths with no title show on nav<commit_after>function Cache() {
  this.everyone = [];
  this.users = [];
  this.admin = [];
  return this;
}

module.exports = Cache;

Cache.prototype.compile = function (scope) {
  var self = this;
  var pathPatt = /^\/.*$/;
  if (typeof self.compiled === 'undefined')
    self.compiled = {};
  var apps = [];
  for (var name in self[scope]) {
    var app = {
      name: name,
      index: self[scope][name].index
    }
    if (self[scope][name].position)
      app.position = self[scope][name].position;
    if (self[scope][name].href)
      app.href = self[scope][name].href;
    else {
      app.views = [];
      for (var path in self[scope][name]) {
        if (typeof self[scope][name][path].title === 'undefined')
          continue;
        if (pathPatt.test(path))
          app.views.push({
            path: path,
            title: self[scope][name][path].title
          });
        else app[path] = self[scope][name][path];
      }
    }
    apps.push(app);
  }
  self.compiled[scope] = apps;
};

Cache.prototype.update = function (apps) {
  this.everyone = {};
  this.users = {};
  this.admin = {};
  return this.init(apps);
};

Cache.prototype.get = function (scope) {
  var self = this;
  if (typeof scope === 'undefined' || scope === 'everyone') {
    return self.everyone;
  } else if (scope === 'users') {
    return self.users;
  } else if (scope === 'admin') {
    return self.admin;
  }
};

Cache.prototype.add = function (app) {
  var self = this;
  var routes = {};
  if (app.ui) {
    if (app.ui.views) {
      var tmp = {
        everyone: [],
        users: [],
        admin: []
      }
      for (var path in app.ui.views) {
        if (typeof app.ui.views[path].scope === 'undefined' || typeof app.ui.views[path].title === 'undefined')
          continue;
        switch (app.ui.views[path].scope) {
          case 'everyone':
            tmp.everyone.push({
              path: app.path + path.replace(/:[a-z]+/g, ''),
              title: app.ui.views[path].title
            });
            if (app.ui.views[path].exclusive)
              break;
            /* falls through */
          case 'users':
            tmp.users.push({
              path: app.path + path.replace(/:[a-z]+/g, ''),
              title: app.ui.views[path].title
            });
            if (app.ui.views[path].exclusive)
              break;
            /* falls through */
          case 'admin':
            tmp.admin.push({
              path: app.path + path.replace(/:[a-z]+/g, ''),
              title: app.ui.views[path].title
            });
            if (app.ui.views[path].exclusive)
              break;
        }
      }
      if (tmp.everyone.length > 0)
        self.everyone.push({
          base: app.path,
          icon: app.ui.icon,
          name: app.ui.navName,
          index: app.ui.index,
          views: tmp.everyone,
          position: app.ui.position
        });
      if (tmp.users.length > 0)
        self.users.push({
          base: app.path,
          icon: app.ui.icon,
          name: app.ui.navName,
          index: app.ui.index,
          views: tmp.users,
          position: app.ui.position
        });
      if (tmp.admin.length > 0)
        self.admin.push({
          base: app.path,
          icon: app.ui.icon,
          name: app.ui.navName,
          index: app.ui.index,
          views: tmp.admin,
          position: app.ui.position
        });
    }
    if (app.ui.href) {
      var link = {
        icon: app.ui.icon,
        name: app.ui.navName,
        index: app.ui.index,
        href: app.ui.href,
        position: app.ui.position
      };
      self.everyone.push(link);
      self.users.push(link);
      self.admin.push(link);
    }
  }
  return self;
};

Cache.prototype.init = function (apps) {
  var self = this;
  for (var app in apps) {
    self.add(apps[app]);
  }
  return self;
};
<|endoftext|>


<commit_before>(function() {
  'use strict';

  var MinMaxCharSelect = {
    controller: MinMaxCharSelectCtrl,
    controllerAs: 'vm',
    bindings: {
      activeCharacters: '<',
      selectedCharacter: '=',
      onSelectedChange: '&'
    },
    template: [
      '
',
      '

',
      '
{{ vm.activeCharacters[vm.selectedCharacter].className }}
',
      '
{{ vm.activeCharacters[vm.selectedCharacter].genderRace }}
',
      '
 {{ vm.activeCharacters[vm.selectedCharacter].level }}
',
      '
{{ vm.activeCharacters[vm.selectedCharacter].powerLevel }}
',
      '

',
      '
',
      '

'
    ].join('')
  };

  var MinMaxCharPopup = {
    controllerAs: 'vm',
    bindings: {
      activeCharacters: '<',
      selectedCharacter: '=',
      onSelected: '&'
    },
    template: [
      '
',
      '

',
      '
{{ char.className }}
',
      '
{{:: char.genderRace }}
',
      '
 {{ char.level }}
',
      '
{{ char.powerLevel }}
',
      '
',
    ].join('')
  };

  angular.module('dimApp')
    .component('dimMinMaxCharSelect', MinMaxCharSelect)
    .component('dimMinMaxCharPopup', MinMaxCharPopup);

  MinMaxCharSelectCtrl.$inject = ['$scope', 'ngDialog'];

  function MinMaxCharSelectCtrl($scope, ngDialog) {
    var vm = this;
    var dialogResult = null;

    vm.onSelected = function onSelected(idx) {
      if (vm.selectedCharacter !== idx) {
        var prev = vm.selectedCharacter;
        vm.selectedCharacter = idx;
        vm.onSelectedChange({ prev: prev, new: idx });
      }
      ngDialog.closeAll();
    };

    vm.openCharSelectPopup = function openCharSelectPopup(e) {
      e.stopPropagation();

      if (dialogResult === null) {
        ngDialog.closeAll();

        dialogResult = ngDialog.open({
          template: [
            '
',
            '  <dim-min-max-char-popup active-characters="vm.activeCharacters" selected-character="vm.selectedCharacter" on-selected="vm.onSelected(idx)"></dim-min-max-char-popup>',
            '
'].join(''),
          plain: true,
          appendTo: 'div[id="char-select"]',
          overlay: false,
          className: 'char-popup',
          showClose: false,
          scope: $scope
        });

        dialogResult.closePromise.then(function() {
          dialogResult = null;
        });
      } else {
        dialogResult.close();
      }
    };
  }
})();
<commit_msg>Don't show currently selected character in drop down<commit_after>(function() {
  'use strict';

  var MinMaxCharSelect = {
    controller: MinMaxCharSelectCtrl,
    controllerAs: 'vm',
    bindings: {
      activeCharacters: '<',
      selectedCharacter: '=',
      onSelectedChange: '&'
    },
    template: [
      '
',
      '

',
      '
{{ vm.activeCharacters[vm.selectedCharacter].className }}
',
      '
{{ vm.activeCharacters[vm.selectedCharacter].genderRace }}
',
      '
 {{ vm.activeCharacters[vm.selectedCharacter].level }}
',
      '
{{ vm.activeCharacters[vm.selectedCharacter].powerLevel }}
',
      '

',
      '
',
      '

'
    ].join('')
  };

  var MinMaxCharPopup = {
    controllerAs: 'vm',
    bindings: {
      activeCharacters: '<',
      selectedCharacter: '=',
      onSelected: '&'
    },
    template: [
      '
',
      '

',
      '
{{ char.className }}
',
      '
{{:: char.genderRace }}
',
      '
 {{ char.level }}
',
      '
{{ char.powerLevel }}
',
      '
',
    ].join('')
  };

  angular.module('dimApp')
    .component('dimMinMaxCharSelect', MinMaxCharSelect)
    .component('dimMinMaxCharPopup', MinMaxCharPopup);

  MinMaxCharSelectCtrl.$inject = ['$scope', 'ngDialog'];

  function MinMaxCharSelectCtrl($scope, ngDialog) {
    var vm = this;
    var dialogResult = null;

    vm.onSelected = function onSelected(idx) {
      if (vm.selectedCharacter !== idx) {
        var prev = vm.selectedCharacter;
        vm.selectedCharacter = idx;
        vm.onSelectedChange({ prev: prev, new: idx });
      }
      ngDialog.closeAll();
    };

    vm.openCharSelectPopup = function openCharSelectPopup(e) {
      e.stopPropagation();

      if (dialogResult === null) {
        ngDialog.closeAll();

        dialogResult = ngDialog.open({
          template: [
            '
',
            '  <dim-min-max-char-popup active-characters="vm.activeCharacters" selected-character="vm.selectedCharacter" on-selected="vm.onSelected(idx)"></dim-min-max-char-popup>',
            '
'].join(''),
          plain: true,
          appendTo: 'div[id="char-select"]',
          overlay: false,
          className: 'char-popup',
          showClose: false,
          scope: $scope
        });

        dialogResult.closePromise.then(function() {
          dialogResult = null;
        });
      } else {
        dialogResult.close();
      }
    };
  }
})();
<|endoftext|>


<commit_before>(function() {
  'use strict';
  var module = angular.module('fim.base');
  module.factory('PastGoodsProvider', function(nxt, $q, IndexedEntityProvider) {

    function PastGoodsProvider(api, $scope, pageSize, account) {
      this.init(api, $scope, pageSize, account);
      this.filter = null;
      this.account = account;
    }
    angular.extend(PastGoodsProvider.prototype, IndexedEntityProvider.prototype, {

      uniqueKey: function(good) {
        return good.goods;
      },
      sortFunction: function(a, b) {
        return a.index - b.index;
      },

      getData: function(firstIndex) {
        var deferred = $q.defer();
        var args = {
          firstIndex: firstIndex,
          lastIndex: firstIndex + this.pageSize,
          includeCounts: true,
          requestType: 'getDGSPurchases',
          seller: this.account
        }
        this.api.engine.socket().callAPIFunction(args).then(deferred.resolve, deferred.reject);
        return deferred.promise;
      },

      dataIterator: function(data) {
        console.log(data);
        var goods = data.purchases;
        var index = this.entities.length > 0 ? this.entities[this.entities.length - 1].index : 0;
        for (var i = 0; i < goods.length; i++) {
          var a = goods[i];
        }
        return new Iterator(goods);
      }
    });
    return PastGoodsProvider;
  });
})();<commit_msg>past goods provider cleaned<commit_after>(function() {
  'use strict';
  var module = angular.module('fim.base');
  module.factory('PastGoodsProvider', function(nxt, $q, IndexedEntityProvider) {

    function PastGoodsProvider(api, $scope, pageSize, account) {
      this.init(api, $scope, pageSize, account);
      this.account = account;
    }
    angular.extend(PastGoodsProvider.prototype, IndexedEntityProvider.prototype, {

      uniqueKey: function(good) {
        return good.goods;
      },
      sortFunction: function(a, b) {
        return a.index - b.index;
      },

      getData: function(firstIndex) {
        var deferred = $q.defer();
        var args = {
          firstIndex: firstIndex,
          lastIndex: firstIndex + this.pageSize,
          includeCounts: true,
          requestType: 'getDGSPurchases',
          seller: this.account
        }
        this.api.engine.socket().callAPIFunction(args).then(deferred.resolve, deferred.reject);
        return deferred.promise;
      },

      dataIterator: function(data) {
        var goods = data.purchases;
        var index = this.entities.length > 0 ? this.entities[this.entities.length - 1].index : 0;
        for (var i = 0; i < goods.length; i++) {
          var a = goods[i];
        }
        return new Iterator(goods);
      }
    });
    return PastGoodsProvider;
  });
})();<|endoftext|>


<commit_before>'use babel';

import EventEmitter from 'events';
import fs from 'fs';
import path from 'path';
import os from 'os';
import {execSync, exec} from 'child_process';
import voucher from 'voucher';
import glob from 'glob';

import defaultConfig from "./config";
export config from "./config";

// Store this because the default schema will overwrite it
var initialGeneratorValue = atom.config.get("build-cmake.generator");

export function providingFunction()
{
    const generateErrorMatch = [
        "(.+\n)?CMake Error at (?<file>[\\/0-9a-zA-Z\\._-]+):(?<line>\\d+)"
    ];
    const generateWarningMatch = [
        "(.+\n)?CMake Error at (?<file>[\\/0-9a-zA-Z\\._-]+):(?<line>\\d+)"
    ];
    const compileErrorMatch = [
        "(.+:\\d+:\\d+:\n)?(?<file>.+):(?<line>\\d+):(?<column>\\d+):\\s+(.*\\s+)?error:\\s+(?<message>.+)", // GCC/Clang Error,
        "(.*>)?(?<file>.+)\\((?<line>\\d+)\\):\\s+(.*\\s+)?error\\s+(C\\d+):(?<message>.*)" // Visual Studio Error
    ];
    const compileWarningMatch = [
        "(.+:\\d+:\\d+:\n)?(?<file>.+):(?<line>\\d+):(?<column>\\d+):\\s+(.*\\s+)?warning:\\s+(?<message>.+)", // GCC/Clang warning
        "(.*>)?(?<file>.+)\\((?<line>\\d+)\\):\\s+(.*\\s+)?warning\\s+(C\\d+):(?<message>.*)" // Visual Studio warning
    ];

    return class CMakeBuildProvider extends EventEmitter {
        constructor(source_dir)
        {
            super();
            // Set default value of generator. Wihtout this errors overwrite
            // the visual selection of the generator.
            this.generator = atom.config.get("build-cmake.generator");
            atom.config.observe('build-cmake.cmakelists', (cmakelists) => {
                this.source_dir = (!!cmakelists) ? source_dir + cmakelists.trim() : source_dir;
            });
            atom.config.observe('build-cmake.build_dir', (buildDir) => {

                const pathVars = {
                    TMPDIR : os.tmpdir(),
                    PROJECT_DIR : this.source_dir,
                    PROJECT_DIRNAME : path.basename(this.source_dir)
                };

                // @TODO: Add escape for '$'
                var pathVarNames = buildDir.match(/\$([a-zA-Z_]+)/g);

                if (pathVarNames) {
                    for (let i = 0; pathVarNames.length > i; i++) {
                        let pathVarName = pathVarNames[i];
                        let pathVarNameWithoutPrefix = pathVarName.substr(1);
                        let pathVarValue = pathVars[pathVarNameWithoutPrefix];

                        if (!pathVarValue) {
                            pathVarValue = "";
                            // @TODO: Maybe include environment variables?
                        }

                        let pathVarMatch = new RegExp(
                            "\\" + pathVarName);

                        buildDir = buildDir.replace(
                            pathVarMatch,
                            pathVarValue);
                    }
                }

                if (path.isAbsolute(buildDir)) {
                    this.build_dir = path.normalize(buildDir);
                } else {
                    this.build_dir = path.resolve(source_dir, buildDir);
                }

                this.cache_path = path.join(this.build_dir, 'CMakeCache.txt');
            });
            atom.config.observe('build-cmake.generator', (generator) => {
                this.generator = (!!generator) ? generator.trim() : '';
            });
            atom.config.observe('build-cmake.executable', executable => {
                this.executable = executable.trim();

                // Prevent any arguments since this could be exploited
                let firstWhiteSpace = this.executable.search(/\s/);
                if (firstWhiteSpace > -1) {
                    this.executable = this.executable.substr(0, firstWhiteSpace);
                }

                // @TODO: Either set their executable without arguments or
                //        give them an error message saying they cannot use
                //        arguments.
                // atom.config.set("build-cmake.executable", this.executable);
            });
            atom.config.observe('build-cmake.cmake_arguments', (args) => {
                this.cmake_arguments = args.split(' ').filter(v => v !== '');
            });
            atom.config.observe('build-cmake.build_arguments', (args) => {
                this.build_arguments = args.split(' ').filter(v => v !== '');
            });
            atom.config.observe('build-cmake.parallel_build', (parallel_build) => {
                this.parallel_build = parallel_build;
            });
            atom.config.onDidChange('build-cmake.build_dir', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.executable', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.generator', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.cmakelists', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.cmake_arguments', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.build_arguments', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.parallel_build', () => { this.emit('refresh'); });
        }

        destructor()
        {
        }

        getNiceName()
        {
            return 'cmake';
        }

        getDefaultGenerator()
        {
            // @TODO: Determine the platforms default generator instead of the
            //        first one in the list
            return this.availableGenerators[0];
        }

        getGeneratorSelectElement()
        {
            return document.getElementById("build-cmake.generator");
        }

        setAvailableGenerators(generators)
        {
            let selectEl = this.getGeneratorSelectElement();
            let currentGenerator = atom.config.get("build-cmake.generator");

            if (selectEl) {

                // Clear select element children.
                while (selectEl.lastChild) {
                    selectEl.lastChild.remove();
                }

                [""].concat(generators).forEach(generator => {
                    let optionEl = document.createElement("option");
                    optionEl.value = generator;
                    optionEl.textContent = generator;

                    selectEl.appendChild(optionEl);
                });

                // Clear above causes the selected value to disapear so we set
                // it here.
                selectEl.value = currentGenerator;
            }

            // Update schema with new enum values
            atom.config.setSchema("build-cmake.generator", Object.assign(
                                                               defaultConfig.generator,
                                                               { enum : [ '' ].concat(generators) }));

            if (!currentGenerator) {
                atom.config.set("build-cmake.generator", initialGeneratorValue);
                // Clear this once we've set it.
                initialGeneratorValue = "";
            }

            this.availableGenerators = generators;
        }

        setGeneratorSelectorError(msg)
        {
            let selectEl = this.getGeneratorSelectElement();
            if (selectEl) { // TODO
                let optionEl = document.createElement("option");
                optionEl.value = "";
                optionEl.textContent = msg;

                while (selectEl.lastChild) {
                    selectEl.lastChild.remove();
                }

                selectEl.appendChild(optionEl);
                selectEl.value = "";
            }
        }

        validateExecutable()
        {
            let executable = this.executable;

            function extractGenerators(stdout)
            {
                let generatorNames = [];
                let startIndex = stdout.search(/\s*Generators\s*(\r\n|\n)/);
                if (startIndex === -1) {
                    return false;
                }

                let generatorsStr = stdout.toString().substr(startIndex);
                let colonIndex = generatorsStr.indexOf(':');

                if (colonIndex === -1) {
                    return false;
                }

                generatorsStr = generatorsStr.substring(colonIndex + 1);

                let generatorsSplit = generatorsStr.split(/\n  (\w)/gi);

                if (generatorsSplit[0].trim() === "") {
                    generatorsSplit.shift();
                }

                for (let i = 0; generatorsSplit.length > i; i += 2) {
                    let generatorStr = generatorsSplit[i] + generatorsSplit[i + 1];
                    let generatorSplit = generatorStr.split("=");
                    let generatorName = generatorSplit[0].trim();
                    let generatorDesc = generatorSplit[1].trim();
                    let genNameNoArch = generatorName.replace("[arch]", "");
                    genNameNoArch = genNameNoArch.trim();

                    generatorNames.push(genNameNoArch);

                    if (generatorName.indexOf("[arch]") > -1) {
                        let archIndex = generatorDesc.indexOf("[arch]");
                        if (archIndex > -1) {
                            let archs = generatorDesc.match(/"\S*"/gi);

                            archs.forEach(arch => {
                                arch = arch.substring(1, arch.length - 1);
                                generatorNames.push(genNameNoArch + " " + arch);
                            });
                        }
                    }
                }

                return generatorNames;
            }

            return new Promise((resolve, reject) => {
                exec(executable + " --help", (err, stdout, stderr) => {

                    if (err) {
                        this.setGeneratorSelectorError(
                            `<ERROR: Failed to execute '${executable}'>`);
                        return reject();
                    }

                    if (stdout.length === 0) {
                        this.setGeneratorSelectorError(
                            `<ERROR: '${executable}' produced empty stdout>`);
                        return reject();
                    }

                    if (stderr.length > 0) {
                        this.setGeneratorSelectorError(
                            `<ERROR: Failed to execute '${executable}'>`);
                        return reject();
                    }

                    let generators = extractGenerators(stdout);

                    if (!generators) {
                        this.setGeneratorSelectorError(
                            `<ERROR: Invalid CMake executable '${executable}'>`);
                        return reject();
                    }

                    this.setAvailableGenerators(generators);
                    this.executable = executable;

                    return resolve();
                });
            });
        }

        isEligible()
        {
            return fs.existsSync(path.join(this.source_dir, 'CMakeLists.txt')) || fs.existsSync(this.cache_path);
        }

        createVisualStudioTarget(target_name)
        {
            args_list = [ '--build', this.build_dir, '--target', target_name, '--' ];
            if (this.parallel_build)
                args_list.push('/maxcpucount');
            return {
                atomCommandName : 'cmake:' + target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.build_dir,
                args : args_list.concat(this.build_arguments),
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                warningMatch : compileWarningMatch.concat(generateWarningMatch),
                sh : false
            };
        }

        visualStudioTargets()
        {
            return Array.from(new Set(glob.sync("**/*.vcxproj", { cwd : this.build_dir, nodir : true, ignore : "CMakeFiles/**" })
                                          .map(target => path.basename(target, '.vcxproj'))))
                .map(target => this.createVisualStudioTarget(target))
                .concat([ this.createVisualStudioTarget('clean') ]);
        }

        createMakeFileTarget(target_name)
        {
            args_list = [ '--build', this.build_dir, '--target', target_name, '--' ];
            if (this.parallel_build)
                args_list.push('-j' + os.cpus().length);
            return {
                atomCommandName : 'cmake:' + target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.build_dir,
                args : args_list.concat(this.build_arguments),
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                warningMatch : compileWarningMatch.concat(generateWarningMatch),
                sh : false
            };
        }

        makeFileTargets()
        {
            output = execSync(
                this.executable + ' --build "' + this.build_dir + '" --target help', { cwd : this.build_dir });
            return output.toString('utf8')
                .split(/[\r\n]{1,2}/)
                .filter(line => line.startsWith('...'))
                .map((line) => this.createMakeFileTarget(line.replace('... ', '').split(' ')[0]));
        }

        createNinjaTarget(target_name)
        {
            return {
                atomCommandName : 'cmake:' + target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.build_dir,
                args : [ '--build', this.build_dir, '--target', target_name ].concat(this.build_arguments),
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                warningMatch : compileWarningMatch.concat(generateWarningMatch),
                sh : false
            };
        }

        ninjaTargets()
        {
            output = execSync(
                this.executable + ' --build "' + this.build_dir + '" -- -t targets', { cwd : this.build_dir });
            return output.toString('utf8')
                .split(/[\r\n]{1,2}/)
                .map((line) => this.createNinjaTarget(line.split(':')[0].trim()));
        }

        settings()
        {
            fs.unwatchFile(this.cache_path);

            fs.watchFile(this.cache_path, (curr, prev) => {
                if (fs.existsSync(this.cache_path) && curr.mtime != prev.mtime)
                    this.emit('refresh');
            });

            var args = this.cmake_arguments.concat([ '-B' + this.build_dir, '-H' + this.source_dir, '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON' ]);
            // Add custom generator if specified.
            if (!!this.generator) {
                args.unshift('-G' + this.generator);
            }
            const generateTarget = {
                atomCommandName : 'cmake:generate',
                name : 'generate',
                exec : this.executable,
                cwd : this.source_dir,
                args : args,
                errorMatch : generateErrorMatch,
                warningMatch : generateWarningMatch,
                sh : false
            };

            return this.validateExecutable()
                .then(() => {
                    return voucher(fs.readFile, this.cache_path, {
                        encoding : 'utf8'
                    });
                })
                .then(cache => {
                    var generator = cache.match(/CMAKE_GENERATOR:INTERNAL=(.*)/)[1];
                    if (generator.match('Visual Studio'))
                        return [ generateTarget ].concat(this.visualStudioTargets());
                    else if (generator.match('Ninja'))
                        return [ generateTarget ].concat(this.ninjaTargets());
                    else
                        return [ generateTarget ].concat(this.makeFileTargets());
                })
                .catch(e => {
                    return [ generateTarget ];
                });
        }
    };
}
<commit_msg>Fix extraction of targets from ninja.<commit_after>'use babel';

import EventEmitter from 'events';
import fs from 'fs';
import path from 'path';
import os from 'os';
import {execSync, exec} from 'child_process';
import voucher from 'voucher';
import glob from 'glob';

import defaultConfig from "./config";
export config from "./config";

// Store this because the default schema will overwrite it
var initialGeneratorValue = atom.config.get("build-cmake.generator");

export function providingFunction()
{
    const generateErrorMatch = [
        "(.+\n)?CMake Error at (?<file>[\\/0-9a-zA-Z\\._-]+):(?<line>\\d+)"
    ];
    const generateWarningMatch = [
        "(.+\n)?CMake Error at (?<file>[\\/0-9a-zA-Z\\._-]+):(?<line>\\d+)"
    ];
    const compileErrorMatch = [
        "(.+:\\d+:\\d+:\n)?(?<file>.+):(?<line>\\d+):(?<column>\\d+):\\s+(.*\\s+)?error:\\s+(?<message>.+)", // GCC/Clang Error,
        "(.*>)?(?<file>.+)\\((?<line>\\d+)\\):\\s+(.*\\s+)?error\\s+(C\\d+):(?<message>.*)" // Visual Studio Error
    ];
    const compileWarningMatch = [
        "(.+:\\d+:\\d+:\n)?(?<file>.+):(?<line>\\d+):(?<column>\\d+):\\s+(.*\\s+)?warning:\\s+(?<message>.+)", // GCC/Clang warning
        "(.*>)?(?<file>.+)\\((?<line>\\d+)\\):\\s+(.*\\s+)?warning\\s+(C\\d+):(?<message>.*)" // Visual Studio warning
    ];

    return class CMakeBuildProvider extends EventEmitter {
        constructor(source_dir)
        {
            super();
            // Set default value of generator. Wihtout this errors overwrite
            // the visual selection of the generator.
            this.generator = atom.config.get("build-cmake.generator");
            atom.config.observe('build-cmake.cmakelists', (cmakelists) => {
                this.source_dir = (!!cmakelists) ? source_dir + cmakelists.trim() : source_dir;
            });
            atom.config.observe('build-cmake.build_dir', (buildDir) => {

                const pathVars = {
                    TMPDIR : os.tmpdir(),
                    PROJECT_DIR : this.source_dir,
                    PROJECT_DIRNAME : path.basename(this.source_dir)
                };

                // @TODO: Add escape for '$'
                var pathVarNames = buildDir.match(/\$([a-zA-Z_]+)/g);

                if (pathVarNames) {
                    for (let i = 0; pathVarNames.length > i; i++) {
                        let pathVarName = pathVarNames[i];
                        let pathVarNameWithoutPrefix = pathVarName.substr(1);
                        let pathVarValue = pathVars[pathVarNameWithoutPrefix];

                        if (!pathVarValue) {
                            pathVarValue = "";
                            // @TODO: Maybe include environment variables?
                        }

                        let pathVarMatch = new RegExp(
                            "\\" + pathVarName);

                        buildDir = buildDir.replace(
                            pathVarMatch,
                            pathVarValue);
                    }
                }

                if (path.isAbsolute(buildDir)) {
                    this.build_dir = path.normalize(buildDir);
                } else {
                    this.build_dir = path.resolve(source_dir, buildDir);
                }

                this.cache_path = path.join(this.build_dir, 'CMakeCache.txt');
            });
            atom.config.observe('build-cmake.generator', (generator) => {
                this.generator = (!!generator) ? generator.trim() : '';
            });
            atom.config.observe('build-cmake.executable', executable => {
                this.executable = executable.trim();

                // Prevent any arguments since this could be exploited
                let firstWhiteSpace = this.executable.search(/\s/);
                if (firstWhiteSpace > -1) {
                    this.executable = this.executable.substr(0, firstWhiteSpace);
                }

                // @TODO: Either set their executable without arguments or
                //        give them an error message saying they cannot use
                //        arguments.
                // atom.config.set("build-cmake.executable", this.executable);
            });
            atom.config.observe('build-cmake.cmake_arguments', (args) => {
                this.cmake_arguments = args.split(' ').filter(v => v !== '');
            });
            atom.config.observe('build-cmake.build_arguments', (args) => {
                this.build_arguments = args.split(' ').filter(v => v !== '');
            });
            atom.config.observe('build-cmake.parallel_build', (parallel_build) => {
                this.parallel_build = parallel_build;
            });
            atom.config.onDidChange('build-cmake.build_dir', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.executable', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.generator', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.cmakelists', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.cmake_arguments', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.build_arguments', () => { this.emit('refresh'); });
            atom.config.onDidChange('build-cmake.parallel_build', () => { this.emit('refresh'); });
        }

        destructor()
        {
        }

        getNiceName()
        {
            return 'cmake';
        }

        getDefaultGenerator()
        {
            // @TODO: Determine the platforms default generator instead of the
            //        first one in the list
            return this.availableGenerators[0];
        }

        getGeneratorSelectElement()
        {
            return document.getElementById("build-cmake.generator");
        }

        setAvailableGenerators(generators)
        {
            let selectEl = this.getGeneratorSelectElement();
            let currentGenerator = atom.config.get("build-cmake.generator");

            if (selectEl) {

                // Clear select element children.
                while (selectEl.lastChild) {
                    selectEl.lastChild.remove();
                }

                [""].concat(generators).forEach(generator => {
                    let optionEl = document.createElement("option");
                    optionEl.value = generator;
                    optionEl.textContent = generator;

                    selectEl.appendChild(optionEl);
                });

                // Clear above causes the selected value to disapear so we set
                // it here.
                selectEl.value = currentGenerator;
            }

            // Update schema with new enum values
            atom.config.setSchema("build-cmake.generator", Object.assign(
                                                               defaultConfig.generator,
                                                               { enum : [ '' ].concat(generators) }));

            if (!currentGenerator) {
                atom.config.set("build-cmake.generator", initialGeneratorValue);
                // Clear this once we've set it.
                initialGeneratorValue = "";
            }

            this.availableGenerators = generators;
        }

        setGeneratorSelectorError(msg)
        {
            let selectEl = this.getGeneratorSelectElement();
            if (selectEl) { // TODO
                let optionEl = document.createElement("option");
                optionEl.value = "";
                optionEl.textContent = msg;

                while (selectEl.lastChild) {
                    selectEl.lastChild.remove();
                }

                selectEl.appendChild(optionEl);
                selectEl.value = "";
            }
        }

        validateExecutable()
        {
            let executable = this.executable;

            function extractGenerators(stdout)
            {
                let generatorNames = [];
                let startIndex = stdout.search(/\s*Generators\s*(\r\n|\n)/);
                if (startIndex === -1) {
                    return false;
                }

                let generatorsStr = stdout.toString().substr(startIndex);
                let colonIndex = generatorsStr.indexOf(':');

                if (colonIndex === -1) {
                    return false;
                }

                generatorsStr = generatorsStr.substring(colonIndex + 1);

                let generatorsSplit = generatorsStr.split(/\n  (\w)/gi);

                if (generatorsSplit[0].trim() === "") {
                    generatorsSplit.shift();
                }

                for (let i = 0; generatorsSplit.length > i; i += 2) {
                    let generatorStr = generatorsSplit[i] + generatorsSplit[i + 1];
                    let generatorSplit = generatorStr.split("=");
                    let generatorName = generatorSplit[0].trim();
                    let generatorDesc = generatorSplit[1].trim();
                    let genNameNoArch = generatorName.replace("[arch]", "");
                    genNameNoArch = genNameNoArch.trim();

                    generatorNames.push(genNameNoArch);

                    if (generatorName.indexOf("[arch]") > -1) {
                        let archIndex = generatorDesc.indexOf("[arch]");
                        if (archIndex > -1) {
                            let archs = generatorDesc.match(/"\S*"/gi);

                            archs.forEach(arch => {
                                arch = arch.substring(1, arch.length - 1);
                                generatorNames.push(genNameNoArch + " " + arch);
                            });
                        }
                    }
                }

                return generatorNames;
            }

            return new Promise((resolve, reject) => {
                exec(executable + " --help", (err, stdout, stderr) => {

                    if (err) {
                        this.setGeneratorSelectorError(
                            `<ERROR: Failed to execute '${executable}'>`);
                        return reject();
                    }

                    if (stdout.length === 0) {
                        this.setGeneratorSelectorError(
                            `<ERROR: '${executable}' produced empty stdout>`);
                        return reject();
                    }

                    if (stderr.length > 0) {
                        this.setGeneratorSelectorError(
                            `<ERROR: Failed to execute '${executable}'>`);
                        return reject();
                    }

                    let generators = extractGenerators(stdout);

                    if (!generators) {
                        this.setGeneratorSelectorError(
                            `<ERROR: Invalid CMake executable '${executable}'>`);
                        return reject();
                    }

                    this.setAvailableGenerators(generators);
                    this.executable = executable;

                    return resolve();
                });
            });
        }

        isEligible()
        {
            return fs.existsSync(path.join(this.source_dir, 'CMakeLists.txt')) || fs.existsSync(this.cache_path);
        }

        createVisualStudioTarget(target_name)
        {
            args_list = [ '--build', this.build_dir, '--target', target_name, '--' ];
            if (this.parallel_build)
                args_list.push('/maxcpucount');
            return {
                atomCommandName : 'cmake:' + target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.build_dir,
                args : args_list.concat(this.build_arguments),
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                warningMatch : compileWarningMatch.concat(generateWarningMatch),
                sh : false
            };
        }

        visualStudioTargets()
        {
            return Array.from(new Set(glob.sync("**/*.vcxproj", { cwd : this.build_dir, nodir : true, ignore : "CMakeFiles/**" })
                                          .map(target => path.basename(target, '.vcxproj'))))
                .map(target => this.createVisualStudioTarget(target))
                .concat([ this.createVisualStudioTarget('clean') ]);
        }

        createMakeFileTarget(target_name)
        {
            args_list = [ '--build', this.build_dir, '--target', target_name, '--' ];
            if (this.parallel_build)
                args_list.push('-j' + os.cpus().length);
            return {
                atomCommandName : 'cmake:' + target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.build_dir,
                args : args_list.concat(this.build_arguments),
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                warningMatch : compileWarningMatch.concat(generateWarningMatch),
                sh : false
            };
        }

        makeFileTargets()
        {
            output = execSync(
                this.executable + ' --build "' + this.build_dir + '" --target help', { cwd : this.build_dir });
            return output.toString('utf8')
                .split(/[\r\n]{1,2}/)
                .filter(line => line.startsWith('...'))
                .map((line) => this.createMakeFileTarget(line.replace('... ', '').split(' ')[0]));
        }

        createNinjaTarget(target_name)
        {
            return {
                atomCommandName : 'cmake:' + target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.build_dir,
                args : [ '--build', this.build_dir, '--target', target_name ].concat(this.build_arguments),
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                warningMatch : compileWarningMatch.concat(generateWarningMatch),
                sh : false
            };
        }

        ninjaTargets()
        {
            output = execSync(
                this.executable + ' --build "' + this.build_dir + '" -- -t query all', { cwd : this.build_dir });
            return output.toString('utf8')
                .split(/[\r\n]{1,2}/)
                .map((line) => this.createNinjaTarget(line.split(':')[0].trim()));
        }

        settings()
        {
            fs.unwatchFile(this.cache_path);

            fs.watchFile(this.cache_path, (curr, prev) => {
                if (fs.existsSync(this.cache_path) && curr.mtime != prev.mtime)
                    this.emit('refresh');
            });

            var args = this.cmake_arguments.concat([ '-B' + this.build_dir, '-H' + this.source_dir, '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON' ]);
            // Add custom generator if specified.
            if (!!this.generator) {
                args.unshift('-G' + this.generator);
            }
            const generateTarget = {
                atomCommandName : 'cmake:generate',
                name : 'generate',
                exec : this.executable,
                cwd : this.source_dir,
                args : args,
                errorMatch : generateErrorMatch,
                warningMatch : generateWarningMatch,
                sh : false
            };

            return this.validateExecutable()
                .then(() => {
                    return voucher(fs.readFile, this.cache_path, {
                        encoding : 'utf8'
                    });
                })
                .then(cache => {
                    var generator = cache.match(/CMAKE_GENERATOR:INTERNAL=(.*)/)[1];
                    if (generator.match('Visual Studio'))
                        return [ generateTarget ].concat(this.visualStudioTargets());
                    else if (generator.match('Ninja'))
                        return [ generateTarget ].concat(this.ninjaTargets());
                    else
                        return [ generateTarget ].concat(this.makeFileTargets());
                })
                .catch(e => {
                    return [ generateTarget ];
                });
        }
    };
}
<|endoftext|>


<commit_before>'use babel';
'use strict';
import fs from 'fs';
import path from 'path';
import os from 'os';
import glob from 'glob';
import {spawn} from 'child_process';

/**
 */
export default class CMake {
  /**
   *@param {path} executable
   */
  constructor(executable) {
    this.executable = executable.trim();
    this.generators = null;
  }

  /**
   * @param {string} command
   * @param {Array} commandArguments
   * @param {regex} outputRegex
   * @param {function} callback
   * @param {string} outputMessage
   * @return {Promise}
   */
  grepStdout(command, commandArguments, outputRegex, callback, outputMessage) {
    return new Promise((resolve, reject) => {
      let child = spawn(command, commandArguments, {shell: false});
      let stdout = '';
      let stderr = '';
      child.stdout.on('data', (data) => {
        stdout = stdout + data.toString('utf8');
      });
      child.stderr.on('data', (data) => {
        stderr = stderr + data.toString('utf8');
      });
      child.on('close', (code) => {
        if (code !== 0) {
          reject(stderr.replace('/bin/sh: ', '').trim() + '!');
        } else {
          let matches = [];
          let match = outputRegex.exec(stdout);
          while (match != null) {
            matches = matches.concat(callback(match));
            match = outputRegex.exec(stdout);
          }
          if (outputMessage != null && matches.length <= 0)
            reject(outputMessage);
          else
            resolve(matches);
        }
      });
    });
  }

  /**
   * @return {Promise}
   */
  validateExecutable() {
    const that = this;
    if (that.generators == null) {
      return that
          .grepStdout(
              that.executable, ['--help'],
              /\s{3}(.*)\s*=\sGenerates?(.*\.|.*\s*.*\.)(\s*Optional\s\[arch\]\scan\sbe\s(\".*\").)?/g,
              (match) => {
                if (match[4]) {
                  const arches = match[4].split('or').map(
                      (arch) => arch.replace(/\"/g, ''));
                  return arches.map(
                      (arch) => match[1].replace(/\[arch\]/g, arch).trim());
                } else {
                  return [match[1].trim()];
                }
              },
              'The executable "' + that.executable +
                  '" failed to produce generators!')
          .then((generators) => {
            that.generators = generators;
          });
    }
  }

  /**
   * @param {path} cachePath
   * @return {Promise}
   */
  extractGeneratorFromCache(cachePath) {
    return new Promise(function(resolve, reject) {
      fs.readFile(cachePath, 'utf8', function(err, cache) {
        if (err) {
          reject(err);
        } else {
          const matches = cache.match(/CMAKE_GENERATOR:INTERNAL=(.*)/);
          if (matches.length != 2)
            reject('Could not find generator in cmake cache!');
          else
            resolve(matches[1]);
        }
      });
    });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  createMakeFileTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('-j' + os.cpus().length);
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  makeFileTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that.grepStdout(
        that.executable, ['--build', buildDirectory, '--target', 'help'],
        /\.{3}\s(.[^\s]+)/g, (match) => {
          return [that.createMakeFileTarget(
              match[1].trim(), sourceDirectory, buildDirectory, parallelBuild,
              extraBuildArguments)];
        });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  ninjaTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('-j' + os.cpus().length);
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  ninjaTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that
        .grepStdout(
            that.executable, ['--build', buildDirectory, '--', '-t', 'targets'],
            /(.+):/g,
            (match) => {
              return [that.ninjaTarget(
                  match[1].trim(), sourceDirectory, buildDirectory,
                  parallelBuild, extraBuildArguments)];
            })
        .then((targets) => {
          return that
              .grepStdout(
                  that.executable,
                  ['--build', buildDirectory, '--', '-t', 'query', 'all'],
                  /^\s{4}(.*)$/gm,
                  (match) => {
                    return [that.ninjaTarget(
                        match[1].trim(), sourceDirectory, buildDirectory,
                        parallelBuild, extraBuildArguments)];
                  })
              .then((allTargets) => {
                return targets.concat(allTargets);
              });
        });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  createVisualStudioTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('/maxcpucount');
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  visualStudioTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return new Promise(function(resolve, reject) {
      glob(
          '**/*.vcxproj', {
            cwd: sourceDirectory,
            nodir: true,
            ignore: 'CMakeFiles/**',
          },
          (error, files) => {
            if (error)
              reject(error);
            else
              resolve(files.map((file) => path.basename(file, '.vcxproj'))
                          .concat(['clean'])
                          .map(
                              (targetName) => that.createVisualStudioTarget(
                                  targetName, sourceDirectory, buildDirectory,
                                  parallelBuild, extraBuildArguments)));
          });
    });
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  extractTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that
        .extractGeneratorFromCache(path.join(buildDirectory, 'CMakeCache.txt'))
        .then((generator) => {
          if (generator.match('Visual Studio'))
            return that
                .visualStudioTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else if (generator.match('Ninja'))
            return that
                .ninjaTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else if (generator.match('Unix Makefiles'))
            return that
                .makeFileTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else
            throw Error('Cannot extract targets for generator \"' + generator + '\"');
        });
  }
};
<commit_msg>added support for MinGW Makefiles<commit_after>'use babel';
'use strict';
import fs from 'fs';
import path from 'path';
import os from 'os';
import glob from 'glob';
import {spawn} from 'child_process';

/**
 */
export default class CMake {
  /**
   *@param {path} executable
   */
  constructor(executable) {
    this.executable = executable.trim();
    this.generators = null;
  }

  /**
   * @param {string} command
   * @param {Array} commandArguments
   * @param {regex} outputRegex
   * @param {function} callback
   * @param {string} outputMessage
   * @return {Promise}
   */
  grepStdout(command, commandArguments, outputRegex, callback, outputMessage) {
    return new Promise((resolve, reject) => {
      let child = spawn(command, commandArguments, {shell: false});
      let stdout = '';
      let stderr = '';
      child.stdout.on('data', (data) => {
        stdout = stdout + data.toString('utf8');
      });
      child.stderr.on('data', (data) => {
        stderr = stderr + data.toString('utf8');
      });
      child.on('close', (code) => {
        if (code !== 0) {
          reject(stderr.replace('/bin/sh: ', '').trim() + '!');
        } else {
          let matches = [];
          let match = outputRegex.exec(stdout);
          while (match != null) {
            matches = matches.concat(callback(match));
            match = outputRegex.exec(stdout);
          }
          if (outputMessage != null && matches.length <= 0)
            reject(outputMessage);
          else
            resolve(matches);
        }
      });
    });
  }

  /**
   * @return {Promise}
   */
  validateExecutable() {
    const that = this;
    if (that.generators == null) {
      return that
          .grepStdout(
              that.executable, ['--help'],
              /\s{3}(.*)\s*=\sGenerates?(.*\.|.*\s*.*\.)(\s*Optional\s\[arch\]\scan\sbe\s(\".*\").)?/g,
              (match) => {
                if (match[4]) {
                  const arches = match[4].split('or').map(
                      (arch) => arch.replace(/\"/g, ''));
                  return arches.map(
                      (arch) => match[1].replace(/\[arch\]/g, arch).trim());
                } else {
                  return [match[1].trim()];
                }
              },
              'The executable "' + that.executable +
                  '" failed to produce generators!')
          .then((generators) => {
            that.generators = generators;
          });
    }
  }

  /**
   * @param {path} cachePath
   * @return {Promise}
   */
  extractGeneratorFromCache(cachePath) {
    return new Promise(function(resolve, reject) {
      fs.readFile(cachePath, 'utf8', function(err, cache) {
        if (err) {
          reject(err);
        } else {
          const matches = cache.match(/CMAKE_GENERATOR:INTERNAL=(.*)/);
          if (matches.length != 2)
            reject('Could not find generator in cmake cache!');
          else
            resolve(matches[1]);
        }
      });
    });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  createMakeFileTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('-j' + os.cpus().length);
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  makeFileTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that.grepStdout(
        that.executable, ['--build', buildDirectory, '--target', 'help'],
        /\.{3}\s(.[^\s]+)/g, (match) => {
          return [that.createMakeFileTarget(
              match[1].trim(), sourceDirectory, buildDirectory, parallelBuild,
              extraBuildArguments)];
        });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  ninjaTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('-j' + os.cpus().length);
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  ninjaTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that
        .grepStdout(
            that.executable, ['--build', buildDirectory, '--', '-t', 'targets'],
            /(.+):/g,
            (match) => {
              return [that.ninjaTarget(
                  match[1].trim(), sourceDirectory, buildDirectory,
                  parallelBuild, extraBuildArguments)];
            })
        .then((targets) => {
          return that
              .grepStdout(
                  that.executable,
                  ['--build', buildDirectory, '--', '-t', 'query', 'all'],
                  /^\s{4}(.*)$/gm,
                  (match) => {
                    return [that.ninjaTarget(
                        match[1].trim(), sourceDirectory, buildDirectory,
                        parallelBuild, extraBuildArguments)];
                  })
              .then((allTargets) => {
                return targets.concat(allTargets);
              });
        });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  createVisualStudioTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('/maxcpucount');
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  visualStudioTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return new Promise(function(resolve, reject) {
      glob(
          '**/*.vcxproj', {
            cwd: sourceDirectory,
            nodir: true,
            ignore: 'CMakeFiles/**',
          },
          (error, files) => {
            if (error)
              reject(error);
            else
              resolve(files.map((file) => path.basename(file, '.vcxproj'))
                          .concat(['clean'])
                          .map(
                              (targetName) => that.createVisualStudioTarget(
                                  targetName, sourceDirectory, buildDirectory,
                                  parallelBuild, extraBuildArguments)));
          });
    });
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  extractTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that
        .extractGeneratorFromCache(path.join(buildDirectory, 'CMakeCache.txt'))
        .then((generator) => {
          if (generator.match('Visual Studio'))
            return that
                .visualStudioTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else if (generator.match('Ninja'))
            return that
                .ninjaTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else if (generator.match('Unix Makefiles') || generator.match('MinGW Makefiles'))
            return that
                .makeFileTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else
            throw Error('Cannot extract targets for generator \"' + generator + '\"');
        });
  }
};
<|endoftext|>