diff options
author | isaacs <i@izs.me> | 2010-05-10 06:35:30 +0400 |
---|---|---|
committer | isaacs <i@izs.me> | 2010-05-10 06:35:30 +0400 |
commit | 44d1b65f993d7dcdfc6259ae1b59a2684ea5779d (patch) | |
tree | b063c7c0a606facd619971a5fe0130f7558f841a /lib | |
parent | a5c897ac8a19cc73389fd8a83403bf68775d2fd4 (diff) |
Leverage the cache for installs.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/install.js | 207 |
1 files changed, 64 insertions, 143 deletions
diff --git a/lib/install.js b/lib/install.js index 728d113c9..53b0b1f11 100644 --- a/lib/install.js +++ b/lib/install.js @@ -28,6 +28,7 @@ var registry = require("./utils/registry") , path = require("path") , chain = require("./utils/chain") , fs = require("fs") + , cache = require("./cache") function registryInstall (pkglist, cb) { // it's helpful to know what we have already @@ -57,17 +58,7 @@ function registryInstall (pkglist, cb) { , installReg = {} ;(function F (pkg) { if (!pkg) { - installList = installList.map(function (i) { - return path.join(npm.dir, i, "package") - }) - log(installList, "install") - if (installList.length === 0) { - return log("nothing to install", "install", cb) - } - return npm.commands.build - ( installList - , cb - ) + return buildAll(installList, cb) } log(pkg, "install pkg") if (seen[pkg]) { @@ -76,29 +67,30 @@ function registryInstall (pkglist, cb) { } seen[pkg] = true - function tarballHandler (er, data) { - if (er) return cb(er) - npm.set(data) - installedPackages[data.name] = installedPackages[data.name] || {} - installedPackages[data.name][data.version] = data - installReg[data.name] = installReg[data.name] || {} - installReg[data.name][data.version] = data - installList.push(path.join(data.name, data.version)) - // also make sure to get any dependencies. + // Call this with the parsed package data when we know + // that it's sitting in the cache + function Continue (er, data) { + if (er) return log.er(cb, "Error installing "+pkg)(er) + seen[data.name+"@"+data.version] = true + var p = path.join(data.name, data.version) + , ir = installReg + if (!installReg[data.name] || !installReg[data.name][data.version]) { + installList.push(p) + ;(ir[data.name] = ir[data.name] || {})[data.version] = true + } + // also add the dependencies. if (data.dependencies) for (var dep in data.dependencies) { dep = dep.trim()+"@"+data.dependencies[dep] if (!seen[dep]) pkglist.push(dep) } F(pkglist.shift()) } - if (pkg.match(/^(file:|\.|\/)/)) { - pkg = pkg.replace(/^file:(\/\/)?/, '') - if (pkg.charAt(0) !== "/") pkg = path.join(process.cwd(), pkg) - return installLocalTarball(pkg, tarballHandler) - } - if (pkg.match(/^https?:\/\//)) return fetchTarball(pkg, tarballHandler) - // now we know it's not a URL, so handle it like a tag, version, or range. + // it's a local thing or a url if it has a / in it. + if (pkg.indexOf("/") !== -1) return cache.add(pkg, Continue) + + // now we know it's not a URL or file, + // so handle it like a tag, version, or range. pkg = pkg.split("@") var name = pkg[0] , ver = pkg.slice(1).join("@").trim() @@ -106,59 +98,41 @@ function registryInstall (pkglist, cb) { , exact = semver.valid(ver) , tag = (!exact && !range) pkg = pkg.join("@") - // must fetch data to know how to solve this. + + // if it's an exact name/version, and not already installed, then pull + // the data out of the cache to ensure it's there, and then add deps. + if (exact) { + if ( (name in installedPackages) && (ver in installedPackages[name]) + || (name in installReg) && (ver in installReg[name]) + ) { + // already installed. Continue. + return F(pkglist.shift()) + } + return cache.read(name, ver, Continue) + } + + // if not exact, then fetch the root data so that we have the + // tags and all the versions, and can know what's available. var data = npm.get(name) if (!data) { log(name, "fetch data") return registry.get(name, function (er, data) { if (!er && !data) er = new Error("not found in registry: "+name) if (er) return cb(er) - npm.set(name, data) + npm.set(data) seen[pkg] = false return F(pkg) }) } - // now we know that we have the data. + // now we know that we have the data. if (tag) { tag = ver || defTag if (!data["dist-tags"] || !(tag in data["dist-tags"])) { - return cb(new Error( - "Tag "+tag+" not found for package "+name)) + return cb(new Error("Tag "+tag+" not found for package "+name)) } data = data.versions[data["dist-tags"][tag]] - npm.set(data) - // add that version to the list, and its dependencies if (!seen[name+"@"+data.version]) pkglist.push(name+"@"+data.version) - if (data.dependencies) for (var dep in data.dependencies) { - dep = dep.trim()+"@"+data.dependencies[dep] - if (!seen[dep]) pkglist.push(dep) - } - return F(pkglist.shift()) - } - // now we know it's not a tag. Either a real version, or a range, - // and possibly already installed. - if (exact) { - if ((name in installedPackages) && (ver in installedPackages[name])) { - // already installed. Continue. - return F(pkglist.shift()) - } - var data = npm.get(name) - // make sure this version exists. - if (!(ver in data.versions)) { - return cb(new Error( - "Required version "+name+"@"+ver+" not found in registry")) - } - data = data.versions[ver] - // get the tarball, and add the deps. - var tarball = data.dist.tarball - if (!tarball) return cb(new Error( - "No tarball URL found for "+name+"@"+ver)) - if (!seen[tarball]) pkglist.push(tarball) - if (data.dependencies) for (var dep in data.dependencies) { - dep = dep.trim()+"@"+data.dependencies[dep] - if (!seen[dep]) pkglist.push(dep) - } return F(pkglist.shift()) } // now we know it's a range. get the data for this package, and then @@ -170,12 +144,11 @@ function registryInstall (pkglist, cb) { var satis = semver.maxSatisfying ( Object.keys(installedPackages[name] || {}) .concat(Object.keys(installReg[name] || {})) - , range) - if (satis) { - satis = name+"@"+satis - if (!seen[satis]) pkglist.push(satis) - return F(pkglist.shift()) - } + , range + ) + // if there is a satisfying version already, then simply move on. + if (satis) return F(pkglist.shift()) + // new thing. fetch from registry. favor defTag version. var data = npm.get(name) , stable = data["dist-tags"] && data["dist-tags"][defTag] @@ -187,90 +160,38 @@ function registryInstall (pkglist, cb) { var satis = semver.maxSatisfying(Object.keys(data.versions), range) if (!satis) return cb(new Error( "No satisfying version found for "+name+"@"+range)) - data = data[satis] + // now just install as an explicit version. satis = name+"@"+satis if (!seen[satis]) pkglist.push(satis) - if (data.dependencies) for (var dep in data.dependencies) { - dep = dep.trim()+"@"+data.dependencies[dep] - if (!seen[dep]) pkglist.push(dep) - } return F(pkglist.shift()) })(pkglist.shift()) } -// download the tarball, and move the contents into -// the appropriate name/version folder. -function fetchTarball (tarball, cb) { - var target = path.join(npm.tmp, tarball.replace(/[^a-zA-Z0-9]/g, "-")+"-"+ - Date.now()+"-"+Math.random()) + ".tgz" - chain - ( [mkdir, npm.tmp] - , [fetch, tarball, target] - , function (er, ok) { - if (er) return cb(er) - installLocalTarball(target, function (er, data) { - if (er) return cb(er) - rm(target, function (er) { - if (er) return cb(er) - cb(null, data) - }) - }) - } - ) -} -function installLocalTarball (tarball, cb) { - var folder = path.join(npm.tmp, tarball.replace(/[^a-zA-Z0-9]/g, "-")+"-"+ - Date.now()+"-"+Math.random()) +function buildAll (list, cb) { + if (list.length === 0) { + return log("Nothing to install", "install", cb) + } + log("About to unpack", "buildAll") chain - ( [mkdir, folder] - , [unpackTar, tarball, folder] - , function (er) { - if (er) return cb(er) - readJson(path.join(folder, "package.json"), function (er, data) { - if (er) return cb(er) - chain - ( [moveIntoPlace, folder, data] - , [log, data._id, "moved into place"] - , [rm, folder] - , [log, folder, "removed"] - , function (er) { return cb(er, data) } - ) - }) - } + ( [unpackAll, list] + , [build, list] + , cb ) - } - -function unpackTar (tarball, unpackTarget, cb) { - exec("tar", ["xzvf", tarball, "--strip", "1", "-C", unpackTarget], cb) +function unpackAll (list, cb) { + list = list.map(function (i) { return i.split('/') }) + ;(function U (i) { + if (!i) return cb() + var target = path.join(npm.dir, i[0], i[1], "package") + cache.unpack(i[0], i[1], target, function (er) { + if (er) return cb(er) + return U(list.shift()) + }) + })(list.shift()) } - -// move to ROOT/.npm/{name}/{version}/package -function moveIntoPlace (dir, data, cb) { - if (!data.name || !data.version) { - return cb(new Error("Name or version not found in package info.")) - } - var target = path.join(npm.dir, data.name, data.version) - - chain - ( function (cb) { - fs.lstat(target, function (e) { - log((e?"remove":"creating") + " " +target, "moveIntoPlace") - if (e) rm(target, function (er, ok) { - if (er) { - log("could not remove " + target, "moveIntoPlace") - cb(new Error(target+" exists, and can't be removed")) - } else { - log("unlinked "+target,"moveIntoPlace") - cb() - } - }) - else cb() - }) - } - , [mkdir, target] - , [fs, "rename", dir, path.join(target, "package")] - , [log, "done", "moveIntoPlace"] +function build (list, cb) { + npm.commands.build + ( list.map(function (i) { return path.join(npm.dir, i, "package") }) , cb ) } |