<commit_before>
// npm build command

// everything about the installation after the creation of
// the .npm/{name}/{version}/package folder.
// linking the main.js and libs folder, dropping these into
// the npm.root, resolving dependencies, etc.

// This runs AFTER install or link are completed.

var npm = require("../npm")
  , log = require("./utils/log")
  , rm = require("./utils/rm-rf")
  , chain = require("./utils/chain")
  , fetch = require("./utils/fetch")
  , fs = require("fs")
  , path = require("path")
  , semver = require("./utils/semver")
  , mkdir = require("./utils/mkdir-p")
  , lifecycle = require("./utils/lifecycle")
  , readJson = require("./utils/read-json")
  , ini = require("./utils/ini")
  , sys = require("sys")
  , writeShim = require("./utils/write-shim")
  , link = require("./utils/link")
  , linkIfExists = link.ifExists
  , shimIfExists = writeShim.ifExists
  , readInstalled = require("./utils/read-installed")

module.exports = build

// pkg is either a "package" folder, or a package.json data obj, or an
// object that has the package.json data on the _data member.
function build (args, cb) {
  readAll(args, function (er, args) {
    if (er) return cb(er)
    chain(args.map(function (a) {
      return [buildStep, a]
    }).concat(
      [ [ linkDependencies, args]
      , [ finishBuild, args ]
      ]).concat(cb))
  })
}

function buildStep (pkg, cb) {
  pkg = pkg && pkg._data || pkg
  if (!pkg) return cb(new Error("Invalid package data "+sys.inspect(pkg)))
  chain
    ( [lifecycle, pkg, "preinstall"]
      // make sure that all dependencies are already installed.
    , [resolveDependencies, pkg]

      // generate ROOT/.npm/{name}/{version}/main.js
    , [createMain, pkg]

      // symlink ROOT/{name}-{version}.js to ROOT/.npm/{name}/{version}/main.js
    , [linkMain, pkg]

      // shim ROOT/.npm/{name}-{version}/{module-name}
      // to ROOT/.npm/{name}/{version}/package/{module}
    , [linkModules, pkg, path.join(npm.root, pkg.name+"-"+pkg.version)]

      // symlink any bins into the node install prefix
    , [linkBins, pkg]

    , cb
    )
}
function autoUpdate (pkg, cb) {
  var auto = npm.config.get("update-dependents")
  if (!auto) return log(
    "update-dependents disabled by config", "update-dependents", cb)
  pkg = pkg && pkg._data || pkg
  if (auto === "always") {
    return npm.commands["update-dependents"]([pkg], cb)
  }
  readInstalled(pkg.name, function (er, inst) {
    if (er) return cb(er)
    var versions = Object.keys(inst[pkg.name])
                   .filter(function (v) { return v !== pkg.version })
                   .sort(semver.sort)
      , maxHave = versions.pop()
    if (!maxHave) return log(
      "only version, no dependencies to update", "update-dependents", cb)
    if (!semver.gt(pkg.version, maxHave)) return log(
      "downgrade, not updating dependencencies", "update-dependents", cb)
    npm.commands["update-dependents"]([pkg], cb)
  })
}
function autoActivate (pkg, cb) {
  var auto = npm.config.get("auto-activate")
  if (!auto) return log(
    "auto-activate disabled by config", "auto-activate", cb)
  pkg = pkg && pkg._data || pkg
  // get the list of versions of this package.
  readInstalled(pkg.name, function (er, inst) {
    if (er) return cb(er)
    var versions = Object.keys(inst)
    if (auto !== "always" && versions.indexOf("active") !== -1) {
      log("another version already active", "auto-activate")
      log("to activate, do:   npm activate "+
        pkg.name+" "+pkg.version, "auto-activate")
      return cb()
    }
    log(pkg.name+"@"+pkg.version, "activate")
    npm.commands.activate([pkg.name+"@"+pkg.version], cb)
  })
}

function readAll (folders, cb) {
  folders = folders.slice(0)
  var data = []
  ;(function R (folder) {
    if (!folder) return cb(null, data)
    if (typeof folder === "object") {
      data.push(folder)
      return R(folders.pop())
    }
    readJson(path.join(folder, "package.json"), function (er, d) {
      if (er) return cb(er)
      data.push(d)
      return R(folders.pop())
    })
  })(folders.pop())
}

// make sure that all the dependencies have been installed.
function resolveDependencies (pkg, topCb) {
  if (!pkg) return topCb(new Error("Package not found to resolve dependencies"))
  // link foo-1.0.3 to ROOT/.npm/{pkg}/{version}/dependencies/foo

  var found = []
  chain
    ( [mkdir, path.join(npm.dir, pkg.name, pkg.version, "dependencies")]
    , function (cb) {
        if (!pkg.dependencies) return topCb()
        // don't create anything until they're all verified.
        var reqs = []
        for (var i in pkg.dependencies) reqs.push({name:i, version:pkg.dependencies[i]})
        if (!reqs.length) return topCb()
        ;(function R (req) {
          if (!req) {
            found.forEach(function (req) {
              log(req.name + "@" + req.version, "found")
            })
            return cb()
          }
          log(req.name+"-"+req.version, "required")
          // see if we have this thing installed.
          fs.readdir(path.join(npm.dir, req.name), function (er, versions) {
            if (er) return cb(new Error(
              "Required package: "+req.name+"("+req.version+") not found."))
            // TODO: Get the "stable" version if there is one.
            // Look that up from the registry.
            var satis = semver.maxSatisfying(versions, req.version)
            if (satis) {
              found.push({name:req.name, version:satis})
              return R(reqs.pop())
            }
            return cb(new Error(
              "Required package: "+req.name+"("+req.version+") not found. "+
              "(Found: "+JSON.stringify(versions)+")"))
          })
        })(reqs.pop())
      }

      // save the resolved dependencies on the pkg data for later
    , function (cb) { pkg._resolvedDeps = found; cb() }
    , topCb
    )
}

function linkDependencies (args, cb) {
  // at this point, we know that all the packages in the "args" list have
  // been installed, and so have all the dependencies in each of their
  // _resolvedDeps array.
  // link each package's _resolvedDeps properly.
  var c = []
  args.filter(function (pkg) {
    return pkg._resolvedDeps && pkg._resolvedDeps.length
  }).forEach(function (pkg) {
    c.push( [ dependentLink, pkg ]
          , [ dependencyLink, pkg ]
          )
  })
  c.push(cb)
  chain(c)
}
// for each dependency, link this pkg into the proper "dependent" folder
function dependentLink (pkg, cb) {
  if (!pkg._resolvedDeps || !pkg._resolvedDeps.length) return cb()
  chain(pkg._resolvedDeps.map(function (dep) {
    var dependents = path.join(npm.dir, dep.name, dep.version, "dependents")
      , to = path.join(dependents, pkg.name + "-" + pkg.version)
      , from = path.join(npm.dir, pkg.name, pkg.version)
    return [link, from, to]
  }).concat(cb))
}
// link each dep into this pkg's "dependencies" folder
function dependencyLink (pkg, cb) {
  if (!pkg._resolvedDeps || !pkg._resolvedDeps.length) return cb()
  chain(pkg._resolvedDeps.map(function (dep) { return function (cb) {
    pkg.link = pkg.link || {}
    var dependencies = path.join(npm.dir, pkg.name, pkg.version, "dependencies")
      , dependsOn = path.join( npm.dir, pkg.name, pkg.version
                             , "dependson", dep.name + "-" + dep.version
                             )
      , fromRoot = path.join(npm.dir, dep.name, dep.version)
      , fromLib = path.join(npm.root, dep.name + "-" + dep.version)
      , fromMain = fromLib + ".js"
      , toLib = path.join(dependencies, dep.name)
      , toMain = toLib+".js"
      , linkToLib = (dep.name in pkg.link)
        ? path.join(npm.dir, pkg.name, pkg.version, "package", pkg.link[dep.name])
        : null
      , linkToMain = linkToLib ? linkToLib + ".js" : null
    chain([ [ mkdir, dependsOn ]
          , [ link, fromRoot, dependsOn ]
          , [ shimIfExists, fromMain, toMain ]
          , [ linkModules, dep, toLib ]
          , linkToLib && [ linkModules, dep, linkToLib ]
          , linkToMain && [ shimIfExists, fromMain, linkToMain ]
          , cb
          ].filter(function (_) { return _ }))
  }}).concat(cb))
}

function createMain (pkg,cb) {
  if (!pkg.main) return cb()
  log(pkg.main, "main")
  writeShim
    ( path.join(npm.dir, pkg.name, pkg.version, "package", pkg.main)
    , path.join(npm.dir, pkg.name, pkg.version, "main.js")
    , path.join(npm.dir, pkg.name, pkg.version, "dependencies")
    , cb
    )
}

// shim {target}/{module-name} to ROOT/.npm/{name}/{version}/package/{module-path}
function linkModules (pkg, target, cb) {
  var mod = pkg.modules
  if (!mod) {
    var lib = pkg.directories && pkg.directories.lib || pkg.lib
    if (!lib) return cb()
    log("Auto-linking the 'directories.lib' is deprecated and will be removed.", "! WARNING !")
    log("Use the package 'modules' feature instead.", "! WARNING !")
    return linkDefaultModules(pkg, lib, target, cb)
  }

  var versionDir = path.join(npm.dir, pkg.name, pkg.version)
    , pkgDir = path.join(versionDir, "package")

  // todo: don't use chain here. do them concurrently.
  chain(Object.keys(mod).map(function (key) {
    var value = mod[key] + ".js"
    key = key + ".js"
    var from = path.join(pkgDir, value)
      , dep = path.join(versionDir, "dependencies")
      , to = path.join(target, key)
    return function (done) { shim(from, to, dep, done) }
  }).concat(cb))
}

/////
///// DEPRECATED
///// FIXME: remove by 0.2.0
/////
// shim ROOT/{name}-{version}/**/*.js to ROOT/.npm/{name}/{version}/{lib}/**/*.js
function linkDefaultModules(pkg, lib, target, cb) {
  var pkgDir = path.join(npm.dir, pkg.name, pkg.version, "package")
    , libDir = path.join(pkgDir, lib)

  // create a modules hash from the lib folder.
  // this sucks, and is going away eventually.
  pkg.modules = {}
  walk(libDir).map(function (filename) {
    filename = filename.replace(/\.(js|node)$/, '')
    pkg.modules[filename.substr(libDir.length + 1)] = filename.substr(pkgDir.length + 1)
  })
  return linkModules(pkg, target, cb)
}
function walk (filename) {
  var filenames = []
    , stats = fs.statSync(filename)
  if(stats.isFile() && filename.match(/\.(js|node)$/)) {
    // Filename
    filenames.push(filename)
  } else if (stats.isDirectory()) {
    // Directory - walk recursive
    var files = fs.readdirSync(filename)
    for (var i = 0; i < files.length; i++) {
      walk(filename + '/' + files[i]).forEach(function (fn) {
        filenames.push(fn)
      })
    }
  }
  return filenames
}
///// end
///// DEPRECATED
///// FIXME: remove by 0.1.30
/////

function shim (from, to, dep, cb) {
  fs.lstat(to, function (er) {
    if (!er) rm(to, function (er) {
      if (er) cb(er)
      else shim(from, to, dep, cb)
    })
    else {
      writeShim(from, to, dep, cb)
    }
  })
}

function linkMain (pkg, cb) {
  if (!pkg.main) return cb()
  var from = path.join(npm.dir, pkg.name, pkg.version, "main.js")
    , dep = path.join(npm.dir, pkg.name, pkg.version, "dependencies")
    , to = path.join(npm.root, pkg.name+"-"+pkg.version+".js")
  shim(from, to, dep, cb)
}

function linkBins (pkg, cb) {
  if (!pkg.bin) return cb()
  log(pkg._id, "linkBins")
  var binroot = npm.config.get("binroot")
    , dep = path.join(npm.dir, pkg.name, pkg.version, "dependencies")
  if (!binroot) return cb()
  if (!process.env.PATH || -1 === process.env.PATH.indexOf(binroot)) {
    log("bins installing to "+binroot+", outside PATH", "warning")
  }
  chain(Object.keys(pkg.bin).filter(function (i) {
    return i.charAt(0) !== "_"
  }).map(function (i) {
    log(i+" "+pkg.bin[i], "linkBin")
    var to = path.join(binroot, i+"-"+pkg.version)
      , from = path.join(npm.dir, pkg.name, pkg.version, "package", pkg.bin[i])
    return [shimTest, from, to, dep]
  }).concat(log.er(cb, "failed to link bins")))
}

function shimTest (from, to, dep, cb) {
  // if it needs a shim, then call writeShim
  // otherwise, just link it in.
  if (from.match(/\.(node|js)$/)) return writeShim(from, to, dep, cb)
  fs.readFile(from, function (er, data) {
    if (er) return cb(er)
    data = data.toString("ascii")
    var envNode = data.match(/#!(\/usr\/bin\/)?env node/)
      , node = data.match(/#!(\/usr(\/local)?\/bin\/)?node/)
    if (envNode && envNode.index === 0 || node && node.index === 0) {
      return writeShim(from, to, dep, cb)
    }
    return link(from, to, cb)
  })
}

function finishBuild (args, cb) {
  chain(args.map(function (pkg) { return function (cb) {
    chain
      ( [lifecycle, pkg, "install"]
      , [lifecycle, pkg, "postinstall"]
      , [autoActivate, pkg]
      , [autoUpdate, pkg]
      , cb
      )
  }}).concat(args.map(function (pkg) {
    pkg = pkg && pkg._data || pkg
    return [ log, "Success: "+(pkg.name + "@"+pkg.version), "build" ]
  })).concat(cb))
}
<commit_msg>Link all the modules at once, don't chain them sequentially<commit_after>
// npm build command

// everything about the installation after the creation of
// the .npm/{name}/{version}/package folder.
// linking the main.js and libs folder, dropping these into
// the npm.root, resolving dependencies, etc.

// This runs AFTER install or link are completed.

var npm = require("../npm")
  , log = require("./utils/log")
  , rm = require("./utils/rm-rf")
  , chain = require("./utils/chain")
  , fetch = require("./utils/fetch")
  , fs = require("fs")
  , path = require("path")
  , semver = require("./utils/semver")
  , mkdir = require("./utils/mkdir-p")
  , lifecycle = require("./utils/lifecycle")
  , readJson = require("./utils/read-json")
  , ini = require("./utils/ini")
  , sys = require("sys")
  , writeShim = require("./utils/write-shim")
  , link = require("./utils/link")
  , linkIfExists = link.ifExists
  , shimIfExists = writeShim.ifExists
  , readInstalled = require("./utils/read-installed")

module.exports = build

// pkg is either a "package" folder, or a package.json data obj, or an
// object that has the package.json data on the _data member.
function build (args, cb) {
  readAll(args, function (er, args) {
    if (er) return cb(er)
    chain(args.map(function (a) {
      return [buildStep, a]
    }).concat(
      [ [ linkDependencies, args]
      , [ finishBuild, args ]
      ]).concat(cb))
  })
}

function buildStep (pkg, cb) {
  pkg = pkg && pkg._data || pkg
  if (!pkg) return cb(new Error("Invalid package data "+sys.inspect(pkg)))
  chain
    ( [lifecycle, pkg, "preinstall"]
      // make sure that all dependencies are already installed.
    , [resolveDependencies, pkg]

      // generate ROOT/.npm/{name}/{version}/main.js
    , [createMain, pkg]

      // symlink ROOT/{name}-{version}.js to ROOT/.npm/{name}/{version}/main.js
    , [linkMain, pkg]

      // shim ROOT/.npm/{name}-{version}/{module-name}
      // to ROOT/.npm/{name}/{version}/package/{module}
    , [linkModules, pkg, path.join(npm.root, pkg.name+"-"+pkg.version)]

      // symlink any bins into the node install prefix
    , [linkBins, pkg]

    , cb
    )
}
function autoUpdate (pkg, cb) {
  var auto = npm.config.get("update-dependents")
  if (!auto) return log(
    "update-dependents disabled by config", "update-dependents", cb)
  pkg = pkg && pkg._data || pkg
  if (auto === "always") {
    return npm.commands["update-dependents"]([pkg], cb)
  }
  readInstalled(pkg.name, function (er, inst) {
    if (er) return cb(er)
    var versions = Object.keys(inst[pkg.name])
                   .filter(function (v) { return v !== pkg.version })
                   .sort(semver.sort)
      , maxHave = versions.pop()
    if (!maxHave) return log(
      "only version, no dependencies to update", "update-dependents", cb)
    if (!semver.gt(pkg.version, maxHave)) return log(
      "downgrade, not updating dependencencies", "update-dependents", cb)
    npm.commands["update-dependents"]([pkg], cb)
  })
}
function autoActivate (pkg, cb) {
  var auto = npm.config.get("auto-activate")
  if (!auto) return log(
    "auto-activate disabled by config", "auto-activate", cb)
  pkg = pkg && pkg._data || pkg
  // get the list of versions of this package.
  readInstalled(pkg.name, function (er, inst) {
    if (er) return cb(er)
    var versions = Object.keys(inst)
    if (auto !== "always" && versions.indexOf("active") !== -1) {
      log("another version already active", "auto-activate")
      log("to activate, do:   npm activate "+
        pkg.name+" "+pkg.version, "auto-activate")
      return cb()
    }
    log(pkg.name+"@"+pkg.version, "activate")
    npm.commands.activate([pkg.name+"@"+pkg.version], cb)
  })
}

function readAll (folders, cb) {
  folders = folders.slice(0)
  var data = []
  ;(function R (folder) {
    if (!folder) return cb(null, data)
    if (typeof folder === "object") {
      data.push(folder)
      return R(folders.pop())
    }
    readJson(path.join(folder, "package.json"), function (er, d) {
      if (er) return cb(er)
      data.push(d)
      return R(folders.pop())
    })
  })(folders.pop())
}

// make sure that all the dependencies have been installed.
function resolveDependencies (pkg, topCb) {
  if (!pkg) return topCb(new Error("Package not found to resolve dependencies"))
  // link foo-1.0.3 to ROOT/.npm/{pkg}/{version}/dependencies/foo

  var found = []
  chain
    ( [mkdir, path.join(npm.dir, pkg.name, pkg.version, "dependencies")]
    , function (cb) {
        if (!pkg.dependencies) return topCb()
        // don't create anything until they're all verified.
        var reqs = []
        for (var i in pkg.dependencies) reqs.push({name:i, version:pkg.dependencies[i]})
        if (!reqs.length) return topCb()
        ;(function R (req) {
          if (!req) {
            found.forEach(function (req) {
              log(req.name + "@" + req.version, "found")
            })
            return cb()
          }
          log(req.name+"-"+req.version, "required")
          // see if we have this thing installed.
          fs.readdir(path.join(npm.dir, req.name), function (er, versions) {
            if (er) return cb(new Error(
              "Required package: "+req.name+"("+req.version+") not found."))
            // TODO: Get the "stable" version if there is one.
            // Look that up from the registry.
            var satis = semver.maxSatisfying(versions, req.version)
            if (satis) {
              found.push({name:req.name, version:satis})
              return R(reqs.pop())
            }
            return cb(new Error(
              "Required package: "+req.name+"("+req.version+") not found. "+
              "(Found: "+JSON.stringify(versions)+")"))
          })
        })(reqs.pop())
      }

      // save the resolved dependencies on the pkg data for later
    , function (cb) { pkg._resolvedDeps = found; cb() }
    , topCb
    )
}

function linkDependencies (args, cb) {
  // at this point, we know that all the packages in the "args" list have
  // been installed, and so have all the dependencies in each of their
  // _resolvedDeps array.
  // link each package's _resolvedDeps properly.
  var c = []
  args.filter(function (pkg) {
    return pkg._resolvedDeps && pkg._resolvedDeps.length
  }).forEach(function (pkg) {
    c.push( [ dependentLink, pkg ]
          , [ dependencyLink, pkg ]
          )
  })
  c.push(cb)
  chain(c)
}
// for each dependency, link this pkg into the proper "dependent" folder
function dependentLink (pkg, cb) {
  if (!pkg._resolvedDeps || !pkg._resolvedDeps.length) return cb()
  chain(pkg._resolvedDeps.map(function (dep) {
    var dependents = path.join(npm.dir, dep.name, dep.version, "dependents")
      , to = path.join(dependents, pkg.name + "-" + pkg.version)
      , from = path.join(npm.dir, pkg.name, pkg.version)
    return [link, from, to]
  }).concat(cb))
}
// link each dep into this pkg's "dependencies" folder
function dependencyLink (pkg, cb) {
  if (!pkg._resolvedDeps || !pkg._resolvedDeps.length) return cb()
  chain(pkg._resolvedDeps.map(function (dep) { return function (cb) {
    pkg.link = pkg.link || {}
    var dependencies = path.join(npm.dir, pkg.name, pkg.version, "dependencies")
      , dependsOn = path.join( npm.dir, pkg.name, pkg.version
                             , "dependson", dep.name + "-" + dep.version
                             )
      , fromRoot = path.join(npm.dir, dep.name, dep.version)
      , fromLib = path.join(npm.root, dep.name + "-" + dep.version)
      , fromMain = fromLib + ".js"
      , toLib = path.join(dependencies, dep.name)
      , toMain = toLib+".js"
      , linkToLib = (dep.name in pkg.link)
        ? path.join(npm.dir, pkg.name, pkg.version, "package", pkg.link[dep.name])
        : null
      , linkToMain = linkToLib ? linkToLib + ".js" : null
    chain([ [ mkdir, dependsOn ]
          , [ link, fromRoot, dependsOn ]
          , [ shimIfExists, fromMain, toMain ]
          , [ linkModules, dep, toLib ]
          , linkToLib && [ linkModules, dep, linkToLib ]
          , linkToMain && [ shimIfExists, fromMain, linkToMain ]
          , cb
          ].filter(function (_) { return _ }))
  }}).concat(cb))
}

function createMain (pkg,cb) {
  if (!pkg.main) return cb()
  log(pkg.main, "main")
  writeShim
    ( path.join(npm.dir, pkg.name, pkg.version, "package", pkg.main)
    , path.join(npm.dir, pkg.name, pkg.version, "main.js")
    , path.join(npm.dir, pkg.name, pkg.version, "dependencies")
    , cb
    )
}

// shim {target}/{module-name} to ROOT/.npm/{name}/{version}/package/{module-path}
function linkModules (pkg, target, cb) {
  var mod = pkg.modules
  if (!mod) {
    var lib = pkg.directories && pkg.directories.lib || pkg.lib
    if (!lib) return cb()
    log("Auto-linking the 'directories.lib' is deprecated and will be removed.", "! WARNING !")
    log("Use the package 'modules' feature instead.", "! WARNING !")
    return linkDefaultModules(pkg, lib, target, cb)
  }

  var versionDir = path.join(npm.dir, pkg.name, pkg.version)
    , pkgDir = path.join(versionDir, "package")

  var modKeys = Object.keys(mod)
    , m = modKeys.length
    , errState = null
  function cb_ (er) {
    if (errState) return
    if (er) return cb(errState = er)
    if (-- m === 0) return cb()
  }
  // chain(Object.keys(mod).map(function (key) {
  modKeys.forEach(function (key) {
    var value = mod[key] + ".js"
    key = key + ".js"
    var from = path.join(pkgDir, value)
      , dep = path.join(versionDir, "dependencies")
      , to = path.join(target, key)
    shim(from, to, dep, cb_)
  })
}

/////
///// DEPRECATED
///// FIXME: remove by 0.2.0
/////
// shim ROOT/{name}-{version}/**/*.js to ROOT/.npm/{name}/{version}/{lib}/**/*.js
function linkDefaultModules(pkg, lib, target, cb) {
  var pkgDir = path.join(npm.dir, pkg.name, pkg.version, "package")
    , libDir = path.join(pkgDir, lib)

  // create a modules hash from the lib folder.
  // this sucks, and is going away eventually.
  pkg.modules = {}
  walk(libDir).map(function (filename) {
    filename = filename.replace(/\.(js|node)$/, '')
    pkg.modules[filename.substr(libDir.length + 1)] = filename.substr(pkgDir.length + 1)
  })
  return linkModules(pkg, target, cb)
}
function walk (filename) {
  var filenames = []
    , stats = fs.statSync(filename)
  if(stats.isFile() && filename.match(/\.(js|node)$/)) {
    // Filename
    filenames.push(filename)
  } else if (stats.isDirectory()) {
    // Directory - walk recursive
    var files = fs.readdirSync(filename)
    for (var i = 0; i < files.length; i++) {
      walk(filename + '/' + files[i]).forEach(function (fn) {
        filenames.push(fn)
      })
    }
  }
  return filenames
}
///// end
///// DEPRECATED
///// FIXME: remove by 0.1.30
/////

function shim (from, to, dep, cb) {
  fs.lstat(to, function (er) {
    if (!er) rm(to, function (er) {
      if (er) cb(er)
      else shim(from, to, dep, cb)
    })
    else {
      writeShim(from, to, dep, cb)
    }
  })
}

function linkMain (pkg, cb) {
  if (!pkg.main) return cb()
  var from = path.join(npm.dir, pkg.name, pkg.version, "main.js")
    , dep = path.join(npm.dir, pkg.name, pkg.version, "dependencies")
    , to = path.join(npm.root, pkg.name+"-"+pkg.version+".js")
  shim(from, to, dep, cb)
}

function linkBins (pkg, cb) {
  if (!pkg.bin) return cb()
  log(pkg._id, "linkBins")
  var binroot = npm.config.get("binroot")
    , dep = path.join(npm.dir, pkg.name, pkg.version, "dependencies")
  if (!binroot) return cb()
  if (!process.env.PATH || -1 === process.env.PATH.indexOf(binroot)) {
    log("bins installing to "+binroot+", outside PATH", "warning")
  }
  chain(Object.keys(pkg.bin).filter(function (i) {
    return i.charAt(0) !== "_"
  }).map(function (i) {
    log(i+" "+pkg.bin[i], "linkBin")
    var to = path.join(binroot, i+"-"+pkg.version)
      , from = path.join(npm.dir, pkg.name, pkg.version, "package", pkg.bin[i])
    return [shimTest, from, to, dep]
  }).concat(log.er(cb, "failed to link bins")))
}

function shimTest (from, to, dep, cb) {
  // if it needs a shim, then call writeShim
  // otherwise, just link it in.
  if (from.match(/\.(node|js)$/)) return writeShim(from, to, dep, cb)
  fs.readFile(from, function (er, data) {
    if (er) return cb(er)
    data = data.toString("ascii")
    var envNode = data.match(/#!(\/usr\/bin\/)?env node/)
      , node = data.match(/#!(\/usr(\/local)?\/bin\/)?node/)
    if (envNode && envNode.index === 0 || node && node.index === 0) {
      return writeShim(from, to, dep, cb)
    }
    return link(from, to, cb)
  })
}

function finishBuild (args, cb) {
  chain(args.map(function (pkg) { return function (cb) {
    chain
      ( [lifecycle, pkg, "install"]
      , [lifecycle, pkg, "postinstall"]
      , [autoActivate, pkg]
      , [autoUpdate, pkg]
      , cb
      )
  }}).concat(args.map(function (pkg) {
    pkg = pkg && pkg._data || pkg
    return [ log, "Success: "+(pkg.name + "@"+pkg.version), "build" ]
  })).concat(cb))
}
<|endoftext|>


<commit_before>var tools         = require('koapp');
var shell         = require('shelljs');
var fs            = require('fs');
var async         = require('async');
var jsonfile      = require('jsonfile');
var chalk         = require('chalk');
var data          = {};
var platform      = "";
var specificDeps  = {
    'ios': {'dep':'https://github.com/apache/cordova-plugins.git#wkwebview-engine-localhost', 'version':'@4.0.0'},
    'android': {'dep':'cordova-plugin-crosswalk-webview@1.7','version':'@5.0.0'}
};
var commandData = {};
var debugMode;

module.exports = {
    build : build,
    help  : help
};

function build(debug, projectName, platform, options) {
    if (!projectName) return tools.debugLog('Project name is mandatory', debug);
    if (!platform) return tools.debugLog('Platform is mandatory', debug);
    if (platform !== 'ios' && platform !== 'android' && platform !== 'none') return tools.debugLog('Invalid platform. Please choose between "ios" or "android".', debug);

    debugMode   = debug;
    commandData = buildData(options);

    jsonfile.readFile(`${commandData.repo}/www/core/structure.json`, function(err, obj) {
        if (err) return tools.debugLog(chalk.red(`\nError reading file structure.json. Please make sure the folder "${commandData.repo}" exists in your project folder.\n`), debugMode);

        var cordova = `cordova create ${projectName} ${obj.packageName || "com.kingofapp.sampleApp"} "${(obj.name || "sampleApp")}" --link-to=${commandData.repo}/www`;
        data = obj;

        var cordovaBuildExec = shell.exec(cordova, { async: false });
        if (cordovaBuildExec.stderr) return tools.debugLog(cordovaBuildExec.stderr, debug);

        shell.cd(projectName);
        var functions = buildSteps(platform, projectName, options);
        async.series(functions, function(err, result) {
            if (err) return tools.debugLog(err, debugMode);

            if (options.indexOf('--dist') !== -1) {
                shell.rm('-rf', `${commandData.repo}/www`);
                shell.mv('-f', `${commandData.repo}/dist`, `${commandData.repo}/www`);
            }

            tools.debugLog(chalk.green("Done!"), debugMode);
        });
    });
}

// This function is prepared to handle more options in the future
function buildData(options) {
    var commandData = {
        repo: "com.kingofapp.visualizer"
    };

    if (options.indexOf('--dev') !== -1) commandData.repo += ".dev";
    if (options.indexOf('--dist') !== -1) {
        shell.mkdir('-p', `${commandData.repo}/dist`);
    }

    return commandData;
}

function buildSteps(platform, projectName, options) {
    var functions = [
        addCordovaPreferences,
        addCordovaPlugins.bind(addCordovaPlugins, platform),
        addPlatform.bind(addPlatform, platform)
    ];

    if (options.indexOf('--no-image') === -1) functions.push(addSplashAndIcon.bind(addSplashAndIcon, projectName));

    return functions;
}

function addSplashAndIcon(projectName, callback) {
    tools.debugLog(chalk.green("Generating icon and splash..."), debugMode);

    shell.cp('-f', `../${commandData.repo}/www/images/icon.png`, 'icon.png');
    shell.cp('-f', `../${commandData.repo}/www/images/splash.png`, 'splash.png');
    async.parallel([
        shell.exec.bind(shell.exec, 'cordova-icon'),
        shell.exec.bind(shell.exec, 'cordova-splash'),
        ], function (err, data) {
            if (err && err.indexOf('ENOENT') > -1) {
                return callback('Error executing ImageMagick. Check the Koapp-cli documentation to learn how to fix this problem (https://github.com/KingofApp/koapp-cli).');
            }

            shell.rm(['splash.png', 'icon.png']);
            shell.cd('..');
            callback(err, data);
   });
}

//Add cordova preferences
function addCordovaPreferences(callback) {
    tools.debugLog(chalk.green("Adding cordova Dependencies..."), debugMode);

    fs.readFile('config.xml', function(err, fileData) {
        if (fileData) {
            var output = cleanBody(fileData.toString());

            fs.writeFile('config.xml', output, function(err) {
                if (err) {
                    tools.debugLog(chalk.red('\nError writing into config.xml file. Please make sure you initialized a project and you are in the right folder.\n'), debugMode);
                    callback(err);
                }

                callback(null);
            });
        } else {
            tools.debugLog(chalk.red('\nError reading config.xml. Please make sure you initialized a project and you are in the right folder.\n'), debugMode);
            callback(err);
        }
    });
}

function cleanBody(body){
    var line = 3;
    var preferences     = '  <access origin="*" />\n' +
        '  <allow-navigation href="*" /> \n' +
        '  <preference name="AutoHideSplashScreen" value="false" />\n' +
        '  <preference name="SplashScreenDelay" value="10000" />\n' +
        '  <preference name="ShowSplashScreenSpinner" value="false" />\n' +
        '  <preference name="FadeSplashScreen" value="true" />\n' +
        '  <preference name="FadeSplashScreenDuration" value="1" />\n' +
        '  <preference name="target-device" value="handset" />';
    var localServer    = '  <content src="http://localhost:56969" />\n';
    var result = body;
    result = result.replace('<platform name="ios">', '<platform name="ios">\n      ' + localServer);

    result = result.split('\n');
    result.splice(line,0,preferences);
    result = result.filter(function(str){ return str; });
    return result.join('\n');
}

//Add particular cordova plugins
function addCordovaPlugins(platform, callback) {
    tools.debugLog(chalk.green('Adding cordova plugins...'));

    var plugins = ['cordova-plugin-device', 'cordova-plugin-geolocation', 'cordova-plugin-console', 'cordova-plugin-dialogs', 'cordova-plugin-globalization', 'cordova-plugin-statusbar', 'cordova-plugin-splashscreen', 'https://github.com/apache/cordova-plugin-inappbrowser.git'];

    if (['android', 'ios'].indexOf(platform) !== -1) plugins.push(specificDeps[platform]['dep']);

    /*plugins = _.union(plugins, getCordovaDeps(data.modules), getCordovaDeps(data.services));
    var commands = _.map(plugins, function(plugin) {
        return `cordova plugin add ${plugin}`;
    });*/

    plugins = plugins.concat(getCordovaDeps(data.modules), getCordovaDeps(data.services));
    var commands = plugins.map(function(plugin) {
        return `cordova plugin add ${plugin}`;
    });

    function executeCommand(command, callback) {
        var result = shell.exec(command, { async: false });

        if (result.indexOf('Saved plugin info for') > -1) callback(null, result.stdout);
        else callback(result.stderr);
    }

    async.each(commands, executeCommand, callback);
}

function addPlatform(platform, callback) {
    tools.debugLog(chalk.green('Adding platform...'), debugMode);

    var platformVersion = platform + specificDeps[platform]['version'];
    var addPlatformCommand = `cordova platform add ${platformVersion}`;
    shell.exec(addPlatformCommand, function(code, output) {
        callback(code, output);
    });
}

function getCordovaDeps(items) {
    return tools.getUniqueItems(items, 'deps');
}

function help() {
    var command = chalk.yellow('koapp build <projectName> <platform> [--dev || --no-image]');
    tools.debugLog(command, true);
    tools.debugLog('' , true);
    tools.debugLog('Build your Cordova project with all its dependencies for the selected platform', true);
    tools.debugLog('', true);
}
<commit_msg>Add plugin name instead of url<commit_after>var tools         = require('koapp');
var shell         = require('shelljs');
var fs            = require('fs');
var async         = require('async');
var jsonfile      = require('jsonfile');
var chalk         = require('chalk');
var data          = {};
var platform      = "";
var specificDeps  = {
    'ios': {'dep':'https://github.com/apache/cordova-plugins.git#wkwebview-engine-localhost', 'version':'@4.0.0'},
    'android': {'dep':'cordova-plugin-crosswalk-webview@1.7','version':'@5.0.0'}
};
var commandData = {};
var debugMode;

module.exports = {
    build : build,
    help  : help
};

function build(debug, projectName, platform, options) {
    if (!projectName) return tools.debugLog('Project name is mandatory', debug);
    if (!platform) return tools.debugLog('Platform is mandatory', debug);
    if (platform !== 'ios' && platform !== 'android' && platform !== 'none') return tools.debugLog('Invalid platform. Please choose between "ios" or "android".', debug);

    debugMode   = debug;
    commandData = buildData(options);

    jsonfile.readFile(`${commandData.repo}/www/core/structure.json`, function(err, obj) {
        if (err) return tools.debugLog(chalk.red(`\nError reading file structure.json. Please make sure the folder "${commandData.repo}" exists in your project folder.\n`), debugMode);

        var cordova = `cordova create ${projectName} ${obj.packageName || "com.kingofapp.sampleApp"} "${(obj.name || "sampleApp")}" --link-to=${commandData.repo}/www`;
        data = obj;

        var cordovaBuildExec = shell.exec(cordova, { async: false });
        if (cordovaBuildExec.stderr) return tools.debugLog(cordovaBuildExec.stderr, debug);

        shell.cd(projectName);
        var functions = buildSteps(platform, projectName, options);
        async.series(functions, function(err, result) {
            if (err) return tools.debugLog(err, debugMode);

            if (options.indexOf('--dist') !== -1) {
                shell.rm('-rf', `${commandData.repo}/www`);
                shell.mv('-f', `${commandData.repo}/dist`, `${commandData.repo}/www`);
            }

            tools.debugLog(chalk.green("Done!"), debugMode);
        });
    });
}

// This function is prepared to handle more options in the future
function buildData(options) {
    var commandData = {
        repo: "com.kingofapp.visualizer"
    };

    if (options.indexOf('--dev') !== -1) commandData.repo += ".dev";
    if (options.indexOf('--dist') !== -1) {
        shell.mkdir('-p', `${commandData.repo}/dist`);
    }

    return commandData;
}

function buildSteps(platform, projectName, options) {
    var functions = [
        addCordovaPreferences,
        addCordovaPlugins.bind(addCordovaPlugins, platform),
        addPlatform.bind(addPlatform, platform)
    ];

    if (options.indexOf('--no-image') === -1) functions.push(addSplashAndIcon.bind(addSplashAndIcon, projectName));

    return functions;
}

function addSplashAndIcon(projectName, callback) {
    tools.debugLog(chalk.green("Generating icon and splash..."), debugMode);

    shell.cp('-f', `../${commandData.repo}/www/images/icon.png`, 'icon.png');
    shell.cp('-f', `../${commandData.repo}/www/images/splash.png`, 'splash.png');
    async.parallel([
        shell.exec.bind(shell.exec, 'cordova-icon'),
        shell.exec.bind(shell.exec, 'cordova-splash'),
        ], function (err, data) {
            if (err && err.indexOf('ENOENT') > -1) {
                return callback('Error executing ImageMagick. Check the Koapp-cli documentation to learn how to fix this problem (https://github.com/KingofApp/koapp-cli).');
            }

            shell.rm(['splash.png', 'icon.png']);
            shell.cd('..');
            callback(err, data);
   });
}

//Add cordova preferences
function addCordovaPreferences(callback) {
    tools.debugLog(chalk.green("Adding cordova Dependencies..."), debugMode);

    fs.readFile('config.xml', function(err, fileData) {
        if (fileData) {
            var output = cleanBody(fileData.toString());

            fs.writeFile('config.xml', output, function(err) {
                if (err) {
                    tools.debugLog(chalk.red('\nError writing into config.xml file. Please make sure you initialized a project and you are in the right folder.\n'), debugMode);
                    callback(err);
                }

                callback(null);
            });
        } else {
            tools.debugLog(chalk.red('\nError reading config.xml. Please make sure you initialized a project and you are in the right folder.\n'), debugMode);
            callback(err);
        }
    });
}

function cleanBody(body){
    var line = 3;
    var preferences     = '  <access origin="*" />\n' +
        '  <allow-navigation href="*" /> \n' +
        '  <preference name="AutoHideSplashScreen" value="false" />\n' +
        '  <preference name="SplashScreenDelay" value="10000" />\n' +
        '  <preference name="ShowSplashScreenSpinner" value="false" />\n' +
        '  <preference name="FadeSplashScreen" value="true" />\n' +
        '  <preference name="FadeSplashScreenDuration" value="1" />\n' +
        '  <preference name="target-device" value="handset" />';
    var localServer    = '  <content src="http://localhost:56969" />\n';
    var result = body;
    result = result.replace('<platform name="ios">', '<platform name="ios">\n      ' + localServer);

    result = result.split('\n');
    result.splice(line,0,preferences);
    result = result.filter(function(str){ return str; });
    return result.join('\n');
}

//Add particular cordova plugins
function addCordovaPlugins(platform, callback) {
    tools.debugLog(chalk.green('Adding cordova plugins...'));

    var plugins = ['cordova-plugin-device', 'cordova-plugin-geolocation', 'cordova-plugin-console', 'cordova-plugin-dialogs', 'cordova-plugin-globalization', 'cordova-plugin-statusbar', 'cordova-plugin-splashscreen', 'cordova-plugin-inappbrowser'];

    if (['android', 'ios'].indexOf(platform) !== -1) plugins.push(specificDeps[platform]['dep']);

    /*plugins = _.union(plugins, getCordovaDeps(data.modules), getCordovaDeps(data.services));
    var commands = _.map(plugins, function(plugin) {
        return `cordova plugin add ${plugin}`;
    });*/

    plugins = plugins.concat(getCordovaDeps(data.modules), getCordovaDeps(data.services));
    var commands = plugins.map(function(plugin) {
        return `cordova plugin add ${plugin}`;
    });

    function executeCommand(command, callback) {
        var result = shell.exec(command, { async: false });

        if (result.indexOf('Saved plugin info for') > -1) callback(null, result.stdout);
        else callback(result.stderr);
    }

    async.each(commands, executeCommand, callback);
}

function addPlatform(platform, callback) {
    tools.debugLog(chalk.green('Adding platform...'), debugMode);

    var platformVersion = platform + specificDeps[platform]['version'];
    var addPlatformCommand = `cordova platform add ${platformVersion}`;
    shell.exec(addPlatformCommand, function(code, output) {
        callback(code, output);
    });
}

function getCordovaDeps(items) {
    return tools.getUniqueItems(items, 'deps');
}

function help() {
    var command = chalk.yellow('koapp build <projectName> <platform> [--dev || --no-image]');
    tools.debugLog(command, true);
    tools.debugLog('' , true);
    tools.debugLog('Build your Cordova project with all its dependencies for the selected platform', true);
    tools.debugLog('', true);
}
<|endoftext|>


<commit_before>'use strict';

var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var XRegExp = require('xregexp').XRegExp;

var SaveConfirmView = require('./save-confirm-view');
var BuildView = require('./build-view');
var GoogleAnalytics = require('./GoogleAnalytics');
var tools = require('./tools');

module.exports = {
  config: {
    panelVisibility: {
      title: 'Panel Visibility',
      description: 'Set when the build panel should be visible.',
      type: 'string',
      default: 'Toggle',
      enum: [ 'Toggle', 'Keep Visible', 'Show on Error' ],
      order: 1
    },
    buildOnSave: {
      title: 'Automatically build on save',
      description: 'Autmatically build your project each time an editor is saved.',
      type: 'boolean',
      default: false,
      order: 2
    },
    saveOnBuild: {
      title: 'Automatically save on build',
      description: 'Automatically save all edited files when triggering a build.',
      type: 'boolean',
      default: false,
      order: 3
    },
    monocleHeight: {
      title: 'Monocle Height',
      description: 'How much of the workspace to use for build panel when it is "maximized".',
      type: 'number',
      default: 0.75,
      minimum: 0.1,
      maximum: 0.9,
      order: 4
    },
    minimizedHeight: {
      title: 'Minimized Height',
      description: 'How much of the workspace to use for build panel when it is "minimized".',
      type: 'number',
      default: 0.15,
      minimum: 0.1,
      maximum: 0.9,
      order: 5
    }
  },

  activate: function(state) {
    // Manually append /usr/local/bin as it may not be set on some systems,
    // and it's common to have node installed here. Keep it at end so it won't
    // accidentially override any other node installation
    process.env.PATH += ':/usr/local/bin';

    /* Migration from 0.29.0. Remove after next version */
    if (atom.config.get('build.keepVisible')) {
      atom.config.set('build.panelVisibility', 'Keep Visible');
      atom.config.unset('build.keepVisible');
    }

    this.buildView = new BuildView();
    this.cmd = {};
    this.match = [];
    this.stdout = new Buffer(0);
    this.stderr = new Buffer(0);
    atom.commands.add('atom-workspace', 'build:trigger', this.build.bind(this));
    atom.commands.add('atom-workspace', 'build:error-match', this.errorMatch.bind(this));
    atom.commands.add('atom-workspace', 'build:error-match-first', this.errorMatchFirst.bind(this));
    atom.commands.add('atom-workspace', 'build:stop', this.stop.bind(this));
    atom.commands.add('atom-workspace', 'build:confirm', function() {
      GoogleAnalytics.sendEvent('build', 'confirmed');
      document.activeElement.click();
    });
    atom.commands.add('atom-workspace', 'build:no-confirm', function() {
      GoogleAnalytics.sendEvent('build', 'not confirmed');
      this.saveConfirmView.cancel();
    }.bind(this));

    atom.workspace.observeTextEditors(function(editor) {
      editor.onDidSave(function(event) {
        if (atom.config.get('build.buildOnSave')) {
          var workspaceElement = atom.views.getView(atom.workspace);
          atom.commands.dispatch(workspaceElement, 'build:trigger');
        }
      });
    });

    GoogleAnalytics.sendEvent('core', 'activated');
  },

  deactivate: function() {
    GoogleAnalytics.sendEvent('core', 'deactivated');
    if (this.child) {
      this.child.kill('SIGKILL');
    }
    clearTimeout(this.finishedTimer);
  },

  buildCommand: function() {
    var path;
    var textEditor = atom.workspace.getActiveTextEditor();
    if (!textEditor || !textEditor.getPath()) {
      /* default to building the first one if no editor is active */
      path = atom.project.getPaths()[0];
    } else {
      /* otherwise, build the one in the root of the active editor */
      path = _.find(atom.project.getPaths(), function (path) {
        var realpath = fs.realpathSync(path);
        return textEditor.getPath().substr(0, realpath.length) === realpath;
      });
    }

    var cmd = {
      env: {},
      args: [],
      cwd: path,
      sh: true,
      errorMatch: ''
    };
    _.find(tools, function (tool) {
      if (!tool.isEligable(path)) {
        return;
      }
      GoogleAnalytics.sendEvent('build', 'tool selected', tool.niceName);
      cmd = _.defaults(tool.settings(path), cmd);
      return true;
    });

    return cmd;
  },

  replace: function(value) {
    var editor = atom.workspace.getActiveTextEditor();
    if (editor && 'untitled' !== editor.getTitle()) {
      var activeFile = fs.realpathSync(editor.getPath());
      value = value.replace('{FILE_ACTIVE}', activeFile);
      value = value.replace('{FILE_ACTIVE_PATH}', path.dirname(activeFile));
      value = value.replace('{FILE_ACTIVE_NAME}', path.basename(activeFile));
      value = value.replace('{FILE_ACTIVE_NAME_BASE}', path.basename(activeFile, path.extname(activeFile)));
    }

    value = value.replace('{PROJECT_PATH}', fs.realpathSync(atom.project.getPaths()[0]));
    if (atom.project.getRepositories[0]) {
      value = value.replace('{REPO_BRANCH_SHORT}', atom.project.getRepositories()[0].getShortHead());
    }

    return value;
  },

  errorParse: function() {
    this.match = [];
    var regex = XRegExp(this.cmd.errorMatch);

    var matchErr, matchOut;

    matchErr = XRegExp.forEach(this.stderr.toString('utf8'), regex, function (match) {
      return this.push(match);
    }, []);

    matchOut = XRegExp.forEach(this.stdout.toString('utf8'), regex, function (match) {
      return this.push(match);
    }, []);

    this.match = matchErr.concat(matchOut);
  },

  errorMatchGoTo: function () {
    atom.workspace.open(this.replace(this.cmd.cwd) + path.sep + this.match[0].file, {
      initialLine: this.match[0].line ? this.match[0].line - 1 : 0, /* Because atom is zero-based */
      initialColumn: this.match[0].col ? this.match[0].col - 1 : 0 /* Because atom is zero-based */
    });

  },
  errorMatch: function() {
    GoogleAnalytics.sendEvent('errorMatch', 'match');
    if (!this.cmd.errorMatch) {
      return;
    }

    if (this.match.length === 0) {
      this.errorParse();
      if (this.match.length === 0) {
        return;
      }
    }

    this.errorMatchGoTo();
    this.match.push(this.match.shift());
  },

  errorMatchFirst: function() {
    GoogleAnalytics.sendEvent('errorMatch', 'first');
    if (!this.cmd.errorMatch) {
      return;
    }
    this.errorParse();
    if (this.match.length === 0) {
      return;
    }

    this.errorMatchGoTo();
    this.match.push(this.match.shift());
  },

  startNewBuild: function() {
    this.cmd = {};
    this.match = [];
    try {
      this.cmd = this.buildCommand();
    } catch (error) {
      this.buildView.reset();
      this.buildView.errorMessage('You have a syntax error in your build file.', true);
      this.buildView.append(error.message);
      return;
    }

    GoogleAnalytics.sendEvent('build', 'triggered');

    if (!this.cmd.exec) {
      return;
    }

    var env = _.extend(process.env, this.cmd.env);
    _.each(env, function(value, key, list) {
      list[key] = this.replace(value);
    }.bind(this));

    var args = _.map(this.cmd.args, this.replace);

    this.cmd.exec = this.replace(this.cmd.exec);

    this.child = child_process.spawn(
      this.cmd.sh ? '/bin/sh' : this.cmd.exec,
      this.cmd.sh ? [ '-c', [ this.cmd.exec ].concat(args).join(' ') ] : args,
      { cwd: this.replace(this.cmd.cwd), env: env }
    );

    this.stdout = new Buffer(0);
    this.child.stdout.on('data', function (buffer) {
      this.stdout = Buffer.concat([ this.stdout, buffer ]);
      this.buildView.append(buffer);
    }.bind(this));

    this.stderr = new Buffer(0);
    this.child.stderr.on('data', function (buffer) {
      this.stderr = Buffer.concat([ this.stderr, buffer ]);
      this.buildView.append(buffer);
    }.bind(this));

    this.child.on('error', function(err) {
      this.buildView.append((this.cmd.sh ? 'Unable to execute with sh: ' : 'Unable to execute: ') + this.cmd.exec);
      this.buildView.append(/\s/.test(this.cmd.exec) ? '`cmd` cannot contain space. Use `args` for arguments.' : '');
    }.bind(this));

    this.child.on('close', function(exitCode) {
      this.buildView.buildFinished(0 === exitCode);
      if (0 === exitCode) {
        GoogleAnalytics.sendEvent('build', 'succeeded');
        this.finishedTimer = setTimeout(function() {
          this.buildView.detach();
        }.bind(this), 1000);
      } else {
        GoogleAnalytics.sendEvent('build', 'failed');
      }
      this.child = null;
    }.bind(this));

    this.buildView.buildStarted();
    this.buildView.append((this.cmd.sh ? 'Executing with sh: ' : 'Executing: ') + this.cmd.exec + [ ' ' ].concat(args).join(' '));
  },

  abort: function(cb) {
    this.child.removeAllListeners('close');
    this.child.on('close', function() {
      this.child = null;
      if (cb) {
        cb();
      }
    }.bind(this));
    this.child.kill();
    this.child.killed = true;
  },

  build: function() {
    clearTimeout(this.finishedTimer);

    this.doSaveConfirm(this.unsavedTextEditors(), function() {
      if (this.child) {
        this.abort(this.startNewBuild.bind(this));
      } else {
        this.startNewBuild();
      }
    }.bind(this));
  },

  doSaveConfirm: function(modifiedTextEditors, continuecb, cancelcb) {
    var saveAndContinue = function(save) {
      _.each(modifiedTextEditors, function(textEditor) {
        save && textEditor.save();
      });
      continuecb();
    };

    if (0 === _.size(modifiedTextEditors) || atom.config.get('build.saveOnBuild')) {
      return saveAndContinue(true);
    }

    this.saveConfirmView = new SaveConfirmView();
    this.saveConfirmView.show(saveAndContinue, cancelcb);
  },

  unsavedTextEditors: function() {
    return _.filter(atom.workspace.getTextEditors(), function(textEditor) {
      return textEditor.isModified() && ('untitled' !== textEditor.getTitle());
    });
  },

  stop: function() {

    clearTimeout(this.finishedTimer);
    if (this.child) {
      if (this.child.killed){
        // This child has been killed, but hasn't terminated. Hide it from user.
        this.child.removeAllListeners();
        this.child = null;
        this.buildView.buildAborted();
        return;
      }

      this.abort(this.buildView.buildAborted.bind(this.buildView));

      this.buildView.buildAbortInitiated();
    } else {
      this.buildView.reset();
    }
  }
};
<commit_msg>Removed migration code<commit_after>'use strict';

var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var XRegExp = require('xregexp').XRegExp;

var SaveConfirmView = require('./save-confirm-view');
var BuildView = require('./build-view');
var GoogleAnalytics = require('./GoogleAnalytics');
var tools = require('./tools');

module.exports = {
  config: {
    panelVisibility: {
      title: 'Panel Visibility',
      description: 'Set when the build panel should be visible.',
      type: 'string',
      default: 'Toggle',
      enum: [ 'Toggle', 'Keep Visible', 'Show on Error' ],
      order: 1
    },
    buildOnSave: {
      title: 'Automatically build on save',
      description: 'Autmatically build your project each time an editor is saved.',
      type: 'boolean',
      default: false,
      order: 2
    },
    saveOnBuild: {
      title: 'Automatically save on build',
      description: 'Automatically save all edited files when triggering a build.',
      type: 'boolean',
      default: false,
      order: 3
    },
    monocleHeight: {
      title: 'Monocle Height',
      description: 'How much of the workspace to use for build panel when it is "maximized".',
      type: 'number',
      default: 0.75,
      minimum: 0.1,
      maximum: 0.9,
      order: 4
    },
    minimizedHeight: {
      title: 'Minimized Height',
      description: 'How much of the workspace to use for build panel when it is "minimized".',
      type: 'number',
      default: 0.15,
      minimum: 0.1,
      maximum: 0.9,
      order: 5
    }
  },

  activate: function(state) {
    // Manually append /usr/local/bin as it may not be set on some systems,
    // and it's common to have node installed here. Keep it at end so it won't
    // accidentially override any other node installation
    process.env.PATH += ':/usr/local/bin';

    this.buildView = new BuildView();
    this.cmd = {};
    this.match = [];
    this.stdout = new Buffer(0);
    this.stderr = new Buffer(0);
    atom.commands.add('atom-workspace', 'build:trigger', this.build.bind(this));
    atom.commands.add('atom-workspace', 'build:error-match', this.errorMatch.bind(this));
    atom.commands.add('atom-workspace', 'build:error-match-first', this.errorMatchFirst.bind(this));
    atom.commands.add('atom-workspace', 'build:stop', this.stop.bind(this));
    atom.commands.add('atom-workspace', 'build:confirm', function() {
      GoogleAnalytics.sendEvent('build', 'confirmed');
      document.activeElement.click();
    });
    atom.commands.add('atom-workspace', 'build:no-confirm', function() {
      GoogleAnalytics.sendEvent('build', 'not confirmed');
      this.saveConfirmView.cancel();
    }.bind(this));

    atom.workspace.observeTextEditors(function(editor) {
      editor.onDidSave(function(event) {
        if (atom.config.get('build.buildOnSave')) {
          var workspaceElement = atom.views.getView(atom.workspace);
          atom.commands.dispatch(workspaceElement, 'build:trigger');
        }
      });
    });

    GoogleAnalytics.sendEvent('core', 'activated');
  },

  deactivate: function() {
    GoogleAnalytics.sendEvent('core', 'deactivated');
    if (this.child) {
      this.child.kill('SIGKILL');
    }
    clearTimeout(this.finishedTimer);
  },

  buildCommand: function() {
    var path;
    var textEditor = atom.workspace.getActiveTextEditor();
    if (!textEditor || !textEditor.getPath()) {
      /* default to building the first one if no editor is active */
      path = atom.project.getPaths()[0];
    } else {
      /* otherwise, build the one in the root of the active editor */
      path = _.find(atom.project.getPaths(), function (path) {
        var realpath = fs.realpathSync(path);
        return textEditor.getPath().substr(0, realpath.length) === realpath;
      });
    }

    var cmd = {
      env: {},
      args: [],
      cwd: path,
      sh: true,
      errorMatch: ''
    };
    _.find(tools, function (tool) {
      if (!tool.isEligable(path)) {
        return;
      }
      GoogleAnalytics.sendEvent('build', 'tool selected', tool.niceName);
      cmd = _.defaults(tool.settings(path), cmd);
      return true;
    });

    return cmd;
  },

  replace: function(value) {
    var editor = atom.workspace.getActiveTextEditor();
    if (editor && 'untitled' !== editor.getTitle()) {
      var activeFile = fs.realpathSync(editor.getPath());
      value = value.replace('{FILE_ACTIVE}', activeFile);
      value = value.replace('{FILE_ACTIVE_PATH}', path.dirname(activeFile));
      value = value.replace('{FILE_ACTIVE_NAME}', path.basename(activeFile));
      value = value.replace('{FILE_ACTIVE_NAME_BASE}', path.basename(activeFile, path.extname(activeFile)));
    }

    value = value.replace('{PROJECT_PATH}', fs.realpathSync(atom.project.getPaths()[0]));
    if (atom.project.getRepositories[0]) {
      value = value.replace('{REPO_BRANCH_SHORT}', atom.project.getRepositories()[0].getShortHead());
    }

    return value;
  },

  errorParse: function() {
    this.match = [];
    var regex = XRegExp(this.cmd.errorMatch);

    var matchErr, matchOut;

    matchErr = XRegExp.forEach(this.stderr.toString('utf8'), regex, function (match) {
      return this.push(match);
    }, []);

    matchOut = XRegExp.forEach(this.stdout.toString('utf8'), regex, function (match) {
      return this.push(match);
    }, []);

    this.match = matchErr.concat(matchOut);
  },

  errorMatchGoTo: function () {
    atom.workspace.open(this.replace(this.cmd.cwd) + path.sep + this.match[0].file, {
      initialLine: this.match[0].line ? this.match[0].line - 1 : 0, /* Because atom is zero-based */
      initialColumn: this.match[0].col ? this.match[0].col - 1 : 0 /* Because atom is zero-based */
    });

  },
  errorMatch: function() {
    GoogleAnalytics.sendEvent('errorMatch', 'match');
    if (!this.cmd.errorMatch) {
      return;
    }

    if (this.match.length === 0) {
      this.errorParse();
      if (this.match.length === 0) {
        return;
      }
    }

    this.errorMatchGoTo();
    this.match.push(this.match.shift());
  },

  errorMatchFirst: function() {
    GoogleAnalytics.sendEvent('errorMatch', 'first');
    if (!this.cmd.errorMatch) {
      return;
    }
    this.errorParse();
    if (this.match.length === 0) {
      return;
    }

    this.errorMatchGoTo();
    this.match.push(this.match.shift());
  },

  startNewBuild: function() {
    this.cmd = {};
    this.match = [];
    try {
      this.cmd = this.buildCommand();
    } catch (error) {
      this.buildView.reset();
      this.buildView.errorMessage('You have a syntax error in your build file.', true);
      this.buildView.append(error.message);
      return;
    }

    GoogleAnalytics.sendEvent('build', 'triggered');

    if (!this.cmd.exec) {
      return;
    }

    var env = _.extend(process.env, this.cmd.env);
    _.each(env, function(value, key, list) {
      list[key] = this.replace(value);
    }.bind(this));

    var args = _.map(this.cmd.args, this.replace);

    this.cmd.exec = this.replace(this.cmd.exec);

    this.child = child_process.spawn(
      this.cmd.sh ? '/bin/sh' : this.cmd.exec,
      this.cmd.sh ? [ '-c', [ this.cmd.exec ].concat(args).join(' ') ] : args,
      { cwd: this.replace(this.cmd.cwd), env: env }
    );

    this.stdout = new Buffer(0);
    this.child.stdout.on('data', function (buffer) {
      this.stdout = Buffer.concat([ this.stdout, buffer ]);
      this.buildView.append(buffer);
    }.bind(this));

    this.stderr = new Buffer(0);
    this.child.stderr.on('data', function (buffer) {
      this.stderr = Buffer.concat([ this.stderr, buffer ]);
      this.buildView.append(buffer);
    }.bind(this));

    this.child.on('error', function(err) {
      this.buildView.append((this.cmd.sh ? 'Unable to execute with sh: ' : 'Unable to execute: ') + this.cmd.exec);
      this.buildView.append(/\s/.test(this.cmd.exec) ? '`cmd` cannot contain space. Use `args` for arguments.' : '');
    }.bind(this));

    this.child.on('close', function(exitCode) {
      this.buildView.buildFinished(0 === exitCode);
      if (0 === exitCode) {
        GoogleAnalytics.sendEvent('build', 'succeeded');
        this.finishedTimer = setTimeout(function() {
          this.buildView.detach();
        }.bind(this), 1000);
      } else {
        GoogleAnalytics.sendEvent('build', 'failed');
      }
      this.child = null;
    }.bind(this));

    this.buildView.buildStarted();
    this.buildView.append((this.cmd.sh ? 'Executing with sh: ' : 'Executing: ') + this.cmd.exec + [ ' ' ].concat(args).join(' '));
  },

  abort: function(cb) {
    this.child.removeAllListeners('close');
    this.child.on('close', function() {
      this.child = null;
      if (cb) {
        cb();
      }
    }.bind(this));
    this.child.kill();
    this.child.killed = true;
  },

  build: function() {
    clearTimeout(this.finishedTimer);

    this.doSaveConfirm(this.unsavedTextEditors(), function() {
      if (this.child) {
        this.abort(this.startNewBuild.bind(this));
      } else {
        this.startNewBuild();
      }
    }.bind(this));
  },

  doSaveConfirm: function(modifiedTextEditors, continuecb, cancelcb) {
    var saveAndContinue = function(save) {
      _.each(modifiedTextEditors, function(textEditor) {
        save && textEditor.save();
      });
      continuecb();
    };

    if (0 === _.size(modifiedTextEditors) || atom.config.get('build.saveOnBuild')) {
      return saveAndContinue(true);
    }

    this.saveConfirmView = new SaveConfirmView();
    this.saveConfirmView.show(saveAndContinue, cancelcb);
  },

  unsavedTextEditors: function() {
    return _.filter(atom.workspace.getTextEditors(), function(textEditor) {
      return textEditor.isModified() && ('untitled' !== textEditor.getTitle());
    });
  },

  stop: function() {

    clearTimeout(this.finishedTimer);
    if (this.child) {
      if (this.child.killed){
        // This child has been killed, but hasn't terminated. Hide it from user.
        this.child.removeAllListeners();
        this.child = null;
        this.buildView.buildAborted();
        return;
      }

      this.abort(this.buildView.buildAborted.bind(this.buildView));

      this.buildView.buildAbortInitiated();
    } else {
      this.buildView.reset();
    }
  }
};
<|endoftext|>


<commit_before>
module.exports = exports = build

/**
 * Module dependencies.
 */

var fs = require('graceful-fs')
  , rm = require('rimraf')
  , path = require('path')
  , glob = require('glob')
  , log = require('npmlog')
  , which = require('which')
  , exec = require('child_process').exec
  , processRelease = require('./process-release')
  , win = process.platform === 'win32'

exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'

function build (gyp, argv, callback) {
  var platformMake = 'make'
  if (process.platform === 'aix') {
    platformMake = 'gmake'
  } else if (process.platform.indexOf('bsd') !== -1) {
    platformMake = 'gmake'
  }

  var release = processRelease(argv, gyp, process.version, process.release)
    , makeCommand = gyp.opts.make || process.env.MAKE || platformMake
    , command = win ? 'msbuild' : makeCommand
    , buildDir = path.resolve('build')
    , configPath = path.resolve(buildDir, 'config.gypi')
    , jobs = gyp.opts.jobs || process.env.JOBS
    , buildType
    , config
    , arch
    , nodeDir

  loadConfigGypi()

  /**
   * Load the "config.gypi" file that was generated during "configure".
   */

  function loadConfigGypi () {
    fs.readFile(configPath, 'utf8', function (err, data) {
      if (err) {
        if (err.code == 'ENOENT') {
          callback(new Error('You must run `node-gyp configure` first!'))
        } else {
          callback(err)
        }
        return
      }
      config = JSON.parse(data.replace(/\#.+\n/, ''))

      // get the 'arch', 'buildType', and 'nodeDir' vars from the config
      buildType = config.target_defaults.default_configuration
      arch = config.variables.target_arch
      nodeDir = config.variables.nodedir

      if ('debug' in gyp.opts) {
        buildType = gyp.opts.debug ? 'Debug' : 'Release'
      }
      if (!buildType) {
        buildType = 'Release'
      }

      log.verbose('build type', buildType)
      log.verbose('architecture', arch)
      log.verbose('node dev dir', nodeDir)

      if (win) {
        findSolutionFile()
      } else {
        doWhich()
      }
    })
  }

  /**
   * On Windows, find the first build/*.sln file.
   */

  function findSolutionFile () {
    glob('build/*.sln', function (err, files) {
      if (err) return callback(err)
      if (files.length === 0) {
        return callback(new Error('Could not find *.sln file. Did you run "configure"?'))
      }
      guessedSolution = files[0]
      log.verbose('found first Solution file', guessedSolution)
      doWhich()
    })
  }

  /**
   * Uses node-which to locate the msbuild / make executable.
   */

  function doWhich () {
    // First make sure we have the build command in the PATH
    which(command, function (err, execPath) {
      if (err) {
        if (win && /not found/.test(err.message)) {
          // On windows and no 'msbuild' found. Let's guess where it is
          findMsbuild()
        } else {
          // Some other error or 'make' not found on Unix, report that to the user
          callback(err)
        }
        return
      }
      log.verbose('`which` succeeded for `' + command + '`', execPath)
      doBuild()
    })
  }

  /**
   * Search for the location of "msbuild.exe" file on Windows.
   */

  function findMsbuild () {
    if (config.variables.msbuild_path) {
      command = config.variables.msbuild_path
      log.verbose('using MSBuild:', command)
      doBuild()
      return
    }

    log.verbose('could not find "msbuild.exe" in PATH - finding location in registry')
    var notfoundErr = 'Can\'t find "msbuild.exe". Do you have Microsoft Visual Studio C++ 2008+ installed?'
    var cmd = 'reg query "HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions" /s'
    if (process.arch !== 'ia32')
      cmd += ' /reg:32'
    exec(cmd, function (err, stdout, stderr) {
      if (err) {
        return callback(new Error(err.message + '\n' + notfoundErr))
      }
      var reVers = /ToolsVersions\\([^\\]+)$/i
        , rePath = /\r\n[ \t]+MSBuildToolsPath[ \t]+REG_SZ[ \t]+([^\r]+)/i
        , msbuilds = []
        , r
        , msbuildPath
      stdout.split('\r\n\r\n').forEach(function(l) {
        if (!l) return
        l = l.trim()
        if (r = reVers.exec(l.substring(0, l.indexOf('\r\n')))) {
          var ver = parseFloat(r[1], 10)
          if (ver >= 3.5) {
            if (r = rePath.exec(l)) {
              msbuilds.push({
                version: ver,
                path: r[1]
              })
            }
          }
        }
      })
      msbuilds.sort(function (x, y) {
        return (x.version < y.version ? -1 : 1)
      })
      ;(function verifyMsbuild () {
        if (!msbuilds.length) return callback(new Error(notfoundErr))
        msbuildPath = path.resolve(msbuilds.pop().path, 'msbuild.exe')
        fs.stat(msbuildPath, function (err, stat) {
          if (err) {
            if (err.code == 'ENOENT') {
              if (msbuilds.length) {
                return verifyMsbuild()
              } else {
                callback(new Error(notfoundErr))
              }
            } else {
              callback(err)
            }
            return
          }
          command = msbuildPath
          doBuild()
        })
      })()
    })
  }

  /**
   * Actually spawn the process and compile the module.
   */

  function doBuild () {

    // Enable Verbose build
    var verbose = log.levels[log.level] <= log.levels.verbose
    if (!win && verbose) {
      argv.push('V=1')
    }
    if (win && !verbose) {
      argv.push('/clp:Verbosity=minimal')
    }

    if (win) {
      // Turn off the Microsoft logo on Windows
      argv.push('/nologo')
    }

    // Specify the build type, Release by default
    if (win) {
      var archLower = arch.toLowerCase()
      var p = archLower === 'x64' ? 'x64' :
              (archLower === 'arm' ? 'ARM' : 'Win32')
      argv.push('/p:Configuration=' + buildType + ';Platform=' + p)
      if (jobs) {
        var j = parseInt(jobs, 10)
        if (!isNaN(j) && j > 0) {
          argv.push('/m:' + j)
        } else if (jobs.toUpperCase() === 'MAX') {
          argv.push('/m:' + require('os').cpus().length)
        }
      }
    } else {
      argv.push('BUILDTYPE=' + buildType)
      // Invoke the Makefile in the 'build' dir.
      argv.push('-C')
      argv.push('build')
      if (jobs) {
        var j = parseInt(jobs, 10)
        if (!isNaN(j) && j > 0) {
          argv.push('--jobs')
          argv.push(j)
        } else if (jobs.toUpperCase() === 'MAX') {
          argv.push('--jobs')
          argv.push(require('os').cpus().length)
        }
      }
    }

    if (win) {
      // did the user specify their own .sln file?
      var hasSln = argv.some(function (arg) {
        return path.extname(arg) == '.sln'
      })
      if (!hasSln) {
        argv.unshift(gyp.opts.solution || guessedSolution)
      }
    }

    var proc = gyp.spawn(command, argv)
    proc.on('exit', onExit)
  }

  /**
   * Invoked after the make/msbuild command exits.
   */

  function onExit (code, signal) {
    if (code !== 0) {
      return callback(new Error('`' + command + '` failed with exit code: ' + code))
    }
    if (signal) {
      return callback(new Error('`' + command + '` got signal: ' + signal))
    }
    callback()
  }

}
<commit_msg>Prefix build targets with /t: on Windows<commit_after>
module.exports = exports = build

/**
 * Module dependencies.
 */

var fs = require('graceful-fs')
  , rm = require('rimraf')
  , path = require('path')
  , glob = require('glob')
  , log = require('npmlog')
  , which = require('which')
  , exec = require('child_process').exec
  , processRelease = require('./process-release')
  , win = process.platform === 'win32'

exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'

function build (gyp, argv, callback) {
  var platformMake = 'make'
  if (process.platform === 'aix') {
    platformMake = 'gmake'
  } else if (process.platform.indexOf('bsd') !== -1) {
    platformMake = 'gmake'
  } else if (win && argv.length > 0) {
    argv = argv.map(function(target) {
      return '/t:' + target
    })
  }

  var release = processRelease(argv, gyp, process.version, process.release)
    , makeCommand = gyp.opts.make || process.env.MAKE || platformMake
    , command = win ? 'msbuild' : makeCommand
    , buildDir = path.resolve('build')
    , configPath = path.resolve(buildDir, 'config.gypi')
    , jobs = gyp.opts.jobs || process.env.JOBS
    , buildType
    , config
    , arch
    , nodeDir

  loadConfigGypi()

  /**
   * Load the "config.gypi" file that was generated during "configure".
   */

  function loadConfigGypi () {
    fs.readFile(configPath, 'utf8', function (err, data) {
      if (err) {
        if (err.code == 'ENOENT') {
          callback(new Error('You must run `node-gyp configure` first!'))
        } else {
          callback(err)
        }
        return
      }
      config = JSON.parse(data.replace(/\#.+\n/, ''))

      // get the 'arch', 'buildType', and 'nodeDir' vars from the config
      buildType = config.target_defaults.default_configuration
      arch = config.variables.target_arch
      nodeDir = config.variables.nodedir

      if ('debug' in gyp.opts) {
        buildType = gyp.opts.debug ? 'Debug' : 'Release'
      }
      if (!buildType) {
        buildType = 'Release'
      }

      log.verbose('build type', buildType)
      log.verbose('architecture', arch)
      log.verbose('node dev dir', nodeDir)

      if (win) {
        findSolutionFile()
      } else {
        doWhich()
      }
    })
  }

  /**
   * On Windows, find the first build/*.sln file.
   */

  function findSolutionFile () {
    glob('build/*.sln', function (err, files) {
      if (err) return callback(err)
      if (files.length === 0) {
        return callback(new Error('Could not find *.sln file. Did you run "configure"?'))
      }
      guessedSolution = files[0]
      log.verbose('found first Solution file', guessedSolution)
      doWhich()
    })
  }

  /**
   * Uses node-which to locate the msbuild / make executable.
   */

  function doWhich () {
    // First make sure we have the build command in the PATH
    which(command, function (err, execPath) {
      if (err) {
        if (win && /not found/.test(err.message)) {
          // On windows and no 'msbuild' found. Let's guess where it is
          findMsbuild()
        } else {
          // Some other error or 'make' not found on Unix, report that to the user
          callback(err)
        }
        return
      }
      log.verbose('`which` succeeded for `' + command + '`', execPath)
      doBuild()
    })
  }

  /**
   * Search for the location of "msbuild.exe" file on Windows.
   */

  function findMsbuild () {
    if (config.variables.msbuild_path) {
      command = config.variables.msbuild_path
      log.verbose('using MSBuild:', command)
      doBuild()
      return
    }

    log.verbose('could not find "msbuild.exe" in PATH - finding location in registry')
    var notfoundErr = 'Can\'t find "msbuild.exe". Do you have Microsoft Visual Studio C++ 2008+ installed?'
    var cmd = 'reg query "HKLM\\Software\\Microsoft\\MSBuild\\ToolsVersions" /s'
    if (process.arch !== 'ia32')
      cmd += ' /reg:32'
    exec(cmd, function (err, stdout, stderr) {
      if (err) {
        return callback(new Error(err.message + '\n' + notfoundErr))
      }
      var reVers = /ToolsVersions\\([^\\]+)$/i
        , rePath = /\r\n[ \t]+MSBuildToolsPath[ \t]+REG_SZ[ \t]+([^\r]+)/i
        , msbuilds = []
        , r
        , msbuildPath
      stdout.split('\r\n\r\n').forEach(function(l) {
        if (!l) return
        l = l.trim()
        if (r = reVers.exec(l.substring(0, l.indexOf('\r\n')))) {
          var ver = parseFloat(r[1], 10)
          if (ver >= 3.5) {
            if (r = rePath.exec(l)) {
              msbuilds.push({
                version: ver,
                path: r[1]
              })
            }
          }
        }
      })
      msbuilds.sort(function (x, y) {
        return (x.version < y.version ? -1 : 1)
      })
      ;(function verifyMsbuild () {
        if (!msbuilds.length) return callback(new Error(notfoundErr))
        msbuildPath = path.resolve(msbuilds.pop().path, 'msbuild.exe')
        fs.stat(msbuildPath, function (err, stat) {
          if (err) {
            if (err.code == 'ENOENT') {
              if (msbuilds.length) {
                return verifyMsbuild()
              } else {
                callback(new Error(notfoundErr))
              }
            } else {
              callback(err)
            }
            return
          }
          command = msbuildPath
          doBuild()
        })
      })()
    })
  }

  /**
   * Actually spawn the process and compile the module.
   */

  function doBuild () {

    // Enable Verbose build
    var verbose = log.levels[log.level] <= log.levels.verbose
    if (!win && verbose) {
      argv.push('V=1')
    }
    if (win && !verbose) {
      argv.push('/clp:Verbosity=minimal')
    }

    if (win) {
      // Turn off the Microsoft logo on Windows
      argv.push('/nologo')
    }

    // Specify the build type, Release by default
    if (win) {
      var archLower = arch.toLowerCase()
      var p = archLower === 'x64' ? 'x64' :
              (archLower === 'arm' ? 'ARM' : 'Win32')
      argv.push('/p:Configuration=' + buildType + ';Platform=' + p)
      if (jobs) {
        var j = parseInt(jobs, 10)
        if (!isNaN(j) && j > 0) {
          argv.push('/m:' + j)
        } else if (jobs.toUpperCase() === 'MAX') {
          argv.push('/m:' + require('os').cpus().length)
        }
      }
    } else {
      argv.push('BUILDTYPE=' + buildType)
      // Invoke the Makefile in the 'build' dir.
      argv.push('-C')
      argv.push('build')
      if (jobs) {
        var j = parseInt(jobs, 10)
        if (!isNaN(j) && j > 0) {
          argv.push('--jobs')
          argv.push(j)
        } else if (jobs.toUpperCase() === 'MAX') {
          argv.push('--jobs')
          argv.push(require('os').cpus().length)
        }
      }
    }

    if (win) {
      // did the user specify their own .sln file?
      var hasSln = argv.some(function (arg) {
        return path.extname(arg) == '.sln'
      })
      if (!hasSln) {
        argv.unshift(gyp.opts.solution || guessedSolution)
      }
    }

    var proc = gyp.spawn(command, argv)
    proc.on('exit', onExit)
  }

  /**
   * Invoked after the make/msbuild command exits.
   */

  function onExit (code, signal) {
    if (code !== 0) {
      return callback(new Error('`' + command + '` failed with exit code: ' + code))
    }
    if (signal) {
      return callback(new Error('`' + command + '` got signal: ' + signal))
    }
    callback()
  }

}
<|endoftext|>


<commit_before>/* jshint expr:true */
import Mirage from 'ember-cli-mirage';
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
import destroyApp from '../../helpers/destroy-app';
import startApp from '../../helpers/start-app';
import testSelector from 'ember-test-selectors';
import {afterEach, beforeEach, describe, it} from 'mocha';
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
import {expect} from 'chai';

describe('Acceptance: Settings - Apps - Slack', function () {
    let application;

    beforeEach(function() {
        application = startApp();
    });

    afterEach(function() {
        destroyApp(application);
    });

    it('redirects to signin when not authenticated', async function () {
        invalidateSession(application);
        await visit('/settings/apps/slack');

        expect(currentURL(), 'currentURL').to.equal('/signin');
    });

    it('redirects to team page when authenticated as author', async function () {
        let role = server.create('role', {name: 'Author'});
        server.create('user', {roles: [role], slug: 'test-user'});

        authenticateSession(application);
        await visit('/settings/apps/slack');

        expect(currentURL(), 'currentURL').to.equal('/team/test-user');
    });

    it('redirects to team page when authenticated as editor', async function () {
        let role = server.create('role', {name: 'Editor'});
        server.create('user', {roles: [role], slug: 'test-user'});

        authenticateSession(application);
        await visit('/settings/apps/slack');

        expect(currentURL(), 'currentURL').to.equal('/team');
    });

    describe('when logged in', function () {
        beforeEach(function () {
            let role = server.create('role', {name: 'Administrator'});
            server.create('user', {roles: [role]});

            return authenticateSession(application);
        });

        it('it validates and saves a slack url properly', async function () {
            await visit('/settings/apps/slack');

            // has correct url
            expect(currentURL(), 'currentURL').to.equal('/settings/apps/slack');

            await fillIn('#slack-settings input[name="slack[url]"]', 'notacorrecturl');
            await click(testSelector('save-button'));

            expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
                .to.equal('The URL must be in a format like https://hooks.slack.com/services/<your personal key>');

            // CMD-S shortcut works
            await fillIn(testSelector('slack-url-input'), 'https://hooks.slack.com/services/1275958430');
            await triggerEvent('.gh-app', 'keydown', {
                keyCode: 83, // s
                metaKey: ctrlOrCmd === 'command',
                ctrlKey: ctrlOrCmd === 'ctrl'
            });

            let [newRequest] = server.pretender.handledRequests.slice(-1);
            let params = JSON.parse(newRequest.requestBody);
            let [result] = JSON.parse(params.settings.findBy('key', 'slack').value);

            expect(result.url).to.equal('https://hooks.slack.com/services/1275958430');
            expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
                .to.equal('');

            await fillIn('#slack-settings input[name="slack[url]"]', 'https://hooks.slack.com/services/1275958430');
            await click(testSelector('send-notification-button'));

            expect(find('.gh-alert-blue').length, 'modal element').to.equal(1);
            expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
                .to.equal('');

            server.put('/settings/', function () {
                return new Mirage.Response(422, {}, {
                    errors: [
                        {
                            errorType: 'ValidationError',
                            message: 'Test error'
                        }
                    ]
                });
            });

            await click('.gh-alert-blue .gh-alert-close');
            await click(testSelector('send-notification-button'));

            // we shouldn't try to send the test request if the save fails
            let [lastRequest] = server.pretender.handledRequests.slice(-1);
            expect(lastRequest.url).to.not.match(/\/slack\/test/);
            expect(find('.gh-alert-blue').length, 'check slack alert after api validation error').to.equal(0);
        });
    });
});
<commit_msg>✅ fix slack tests (#807)<commit_after>/* jshint expr:true */
import Mirage from 'ember-cli-mirage';
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
import destroyApp from '../../helpers/destroy-app';
import startApp from '../../helpers/start-app';
import testSelector from 'ember-test-selectors';
import {afterEach, beforeEach, describe, it} from 'mocha';
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
import {expect} from 'chai';

describe('Acceptance: Settings - Apps - Slack', function () {
    let application;

    beforeEach(function() {
        application = startApp();
    });

    afterEach(function() {
        destroyApp(application);
    });

    it('redirects to signin when not authenticated', async function () {
        invalidateSession(application);
        await visit('/settings/apps/slack');

        expect(currentURL(), 'currentURL').to.equal('/signin');
    });

    it('redirects to team page when authenticated as author', async function () {
        let role = server.create('role', {name: 'Author'});
        server.create('user', {roles: [role], slug: 'test-user'});

        authenticateSession(application);
        await visit('/settings/apps/slack');

        expect(currentURL(), 'currentURL').to.equal('/team/test-user');
    });

    it('redirects to team page when authenticated as editor', async function () {
        let role = server.create('role', {name: 'Editor'});
        server.create('user', {roles: [role], slug: 'test-user'});

        authenticateSession(application);
        await visit('/settings/apps/slack');

        expect(currentURL(), 'currentURL').to.equal('/team');
    });

    describe('when logged in', function () {
        beforeEach(function () {
            let role = server.create('role', {name: 'Administrator'});
            server.create('user', {roles: [role]});

            return authenticateSession(application);
        });

        it('it validates and saves a slack url properly', async function () {
            await visit('/settings/apps/slack');

            // has correct url
            expect(currentURL(), 'currentURL').to.equal('/settings/apps/slack');

            await fillIn('#slack-settings input[name="slack[url]"]', 'notacorrecturl');
            await click(testSelector('save-button'));

            expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
                .to.equal('The URL must be in a format like https://hooks.slack.com/services/<your personal key>');

            // CMD-S shortcut works
            await fillIn(testSelector('slack-url-input'), 'https://hooks.slack.com/services/1275958430');
            await triggerEvent('.gh-app', 'keydown', {
                keyCode: 83, // s
                metaKey: ctrlOrCmd === 'command',
                ctrlKey: ctrlOrCmd === 'ctrl'
            });

            let [newRequest] = server.pretender.handledRequests.slice(-1);
            let params = JSON.parse(newRequest.requestBody);
            let [result] = JSON.parse(params.settings.findBy('key', 'slack').value);

            expect(result.url).to.equal('https://hooks.slack.com/services/1275958430');
            expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
                .to.equal('');

            await fillIn('#slack-settings input[name="slack[url]"]', 'https://hooks.slack.com/services/1275958430');
            await click(testSelector('send-notification-button'));

            expect(find('.gh-notification').length, 'number of notifications').to.equal(1);
            expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
                .to.equal('');

            server.put('/settings/', function () {
                return new Mirage.Response(422, {}, {
                    errors: [
                        {
                            errorType: 'ValidationError',
                            message: 'Test error'
                        }
                    ]
                });
            });

            await click('.gh-notification .gh-notification-close');
            await click(testSelector('send-notification-button'));

            // we shouldn't try to send the test request if the save fails
            let [lastRequest] = server.pretender.handledRequests.slice(-1);
            expect(lastRequest.url).to.not.match(/\/slack\/test/);
            expect(find('.gh-notification').length, 'check slack notification after api validation error').to.equal(0);
        });
    });
});
<|endoftext|>


<commit_before>
const RG = require('../../../client/src/battles');
const expect = require('chai').expect;
const TemplLevel = require('../../../client/src/template.level');

const Castle = require('../../../client/data/tiles.castle');

describe('Template.Level', () => {
    it('creates a 2-d map of the level', () => {
        const level = new TemplLevel(10, 7);

        RG.RAND.setSeed(new Date().getTime());

        level.setGenParams([1, 2, 1, 1]);
        level.setRoomCount(30);
        level.create();
        expect(Array.isArray(level.map)).to.be.true;

        // console.log(JSON.stringify(level.map));
        // RG.printMap(level.map);

    });

    it('can create 2-d castles', () => {
        const level = new TemplLevel(10, 7);
        RG.RAND.setSeed(new Date().getTime());

        level.setFiller(Castle.filler);
        level.setTemplates(Castle.templates);
        level.setConstraintFunc(Castle.constraintFunc);
        level.setStartRoomFunc(Castle.getStartRoom);

        level.setGenParams([1, 1, 1, 1]);
        level.setRoomCount(20);
        level.create();

        RG.printMap(level.map);
    });
});
<commit_msg>Added a unit test for outer wall-only castle.<commit_after>
const RG = require('../../../client/src/battles');
const expect = require('chai').expect;
const TemplLevel = require('../../../client/src/template.level');

const Castle = require('../../../client/data/tiles.castle');

describe('Template.Level', () => {
    it('creates a 2-d map of the level', () => {
        const level = new TemplLevel(10, 7);

        RG.RAND.setSeed(new Date().getTime());

        level.setGenParams([1, 2, 1, 1]);
        level.setRoomCount(30);
        level.create();
        expect(Array.isArray(level.map)).to.be.true;

        // console.log(JSON.stringify(level.map));
        // RG.printMap(level.map);

    });

    it('can create 2-d castles', () => {
        const level = new TemplLevel(12, 6);
        RG.RAND.setSeed(new Date().getTime());

        level.setFiller(Castle.fillerWall);
        level.setTemplates(Castle.templates);
        level.setConstraintFunc(Castle.constraintFunc);
        level.setStartRoomFunc(Castle.getStartRoom);

        level.setGenParams([1, 1, 1, 1]);
        level.setRoomCount(-1); // Fill until no more exits
        level.create();

        expect(level.map).to.have.length(7 * 12);
        expect(level.map[0]).to.have.length(7 * 6);

        // RG.printMap(level.map);
    });

    it('can create 2-d castles with outer wall only', () => {
        const level = new TemplLevel(12, 6);
        RG.RAND.setSeed(new Date().getTime());

        level.setFiller(Castle.fillerFloor);
        level.setTemplates(Castle.templatesWall);
        level.setConstraintFunc(Castle.constraintFunc);
        level.setStartRoomFunc(Castle.getStartRoom);

        level.setGenParams([1, 1, 1, 1]);
        level.setRoomCount(-1); // Fill until no more exits
        level.create();

        expect(level.map).to.have.length(7 * 12);
        expect(level.map[0]).to.have.length(7 * 6);

        RG.printMap(level.map);
    });
});
<|endoftext|>


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

  // ImageDataに描画された文字の位置とサイズを取得する。
  // 返り値は`[x, y, w, h]`の形式
  function charRect(data) {
    var
    t = Infinity, b = 0,
    l = Infinity, r = 0,
    x, y;

    for (y = 0; y < data.height; y++) {
      for (x = 0; x < data.width; x++) {
        if (data.data[(y * data.width + x) * 4 + 3] !== 0) {
          t = Math.min(t, y);
          b = Math.max(b, y);
          l = Math.min(l, x);
          r = Math.max(r, x);
        }
      }
    }

    return [l, t, r - l, b - t];
  }

  // rectの領域をn×nに区切って、その中の文字の割合がpより大きいかどうかの入った配列（mesh）を返す
  // 配列の要素が1のときpより大きくて、0のとき小さい
  function charMesh(data, rect, n, p) {
    var
    sx = rect[0], sy = rect[1],
    w = rect[2], h = rect[3],
    x, y,
    i, j, k,
    mesh = [];

    for (y = 0; y < h; y++) {
      i = Math.floor(y / h * n);
      for (x = 0; x < w; x++) {
        j = Math.floor(x / w * n);
        k = i * n + j;
        mesh[k] = mesh[k] || 0;
        if (data.data[((y + sy) * data.width + (x + sx)) * 4 + 3] !== 0) {
          mesh[k] += 1;
        }
      }
    }

    for (y = 0; y < n; y++) {
      for (x = 0; x < n; x++) {
        k = y * n + x;
        mesh[k] /= w/n*h/n;
        mesh[k] = mesh[k] > p ? 1 : 0;
      }
    }

    return mesh;
  }

  // meshを整数値にする。
  // meshの長さが32より大きいと死ぬ。
  function meshToInt(mesh) {
    return parseInt(mesh.join(''), 2);
  }

  // 公開
  var
  chars = {
    charRect: charRect,
    charMesh: charMesh,
    meshToInt: meshToInt,
  };
  if (typeof module !== 'undefined') {
    module.exports = chars;
  }
  global.chars = chars;
})((this || 0).self || global);
<commit_msg>add charSquare method<commit_after>(function (global) {
  'use strict';

  // ImageDataに描画された文字の位置とサイズを取得する。
  // 返り値は`[x, y, w, h]`の形式
  function charRect(data) {
    var
    t = Infinity, b = 0,
    l = Infinity, r = 0,
    x, y;

    for (y = 0; y < data.height; y++) {
      for (x = 0; x < data.width; x++) {
        if (data.data[(y * data.width + x) * 4 + 3] !== 0) {
          t = Math.min(t, y);
          b = Math.max(b, y);
          l = Math.min(l, x);
          r = Math.max(r, x);
        }
      }
    }

    return [l, t, r - l, b - t];
  }

  // ImageDataに描画された文字の位置とサイズを正方形で取得する。
  // 返り値は`[x, y, w]`の形式
  function charSquare(data) {
    var
    rect = charRect(data),
    l = Math.max(rect[2], rect[3]);

    return [rect[0] - (rect[2] / 2) + (l / 2),
            rect[1] - (rect[3] / 2) + (l / 2),
            l];
  }

  // rectの領域をn×nに区切って、その中の文字の割合がpより大きいかどうかの入った配列（mesh）を返す
  // 配列の要素が1のときpより大きくて、0のとき小さい
  function charMesh(data, rect, n, p) {
    var
    sx = rect[0], sy = rect[1],
    w = rect[2], h = rect[3],
    x, y,
    i, j, k,
    mesh = [];

    for (y = 0; y < h; y++) {
      i = Math.floor(y / h * n);
      for (x = 0; x < w; x++) {
        j = Math.floor(x / w * n);
        k = i * n + j;
        mesh[k] = mesh[k] || 0;
        if (data.data[((y + sy) * data.width + (x + sx)) * 4 + 3] !== 0) {
          mesh[k] += 1;
        }
      }
    }

    for (y = 0; y < n; y++) {
      for (x = 0; x < n; x++) {
        k = y * n + x;
        mesh[k] /= w/n*h/n;
        mesh[k] = mesh[k] > p ? 1 : 0;
      }
    }

    return mesh;
  }

  // meshを整数値にする。
  // meshの長さが32より大きいと死ぬ。
  function meshToInt(mesh) {
    return parseInt(mesh.join(''), 2);
  }

  // 公開
  var
  chars = {
    charRect: charRect,
    charMesh: charMesh,
    meshToInt: meshToInt,
  };
  if (typeof module !== 'undefined') {
    module.exports = chars;
  }
  global.chars = chars;
})((this || 0).self || global);
<|endoftext|>