npm: Upgrade to 1.3.19
[platform/upstream/nodejs.git] / deps / npm / lib / build.js
1 // npm build command
2
3 // everything about the installation after the creation of
4 // the .npm/{name}/{version}/package folder.
5 // linking the modules into the npm.root,
6 // resolving dependencies, etc.
7
8 // This runs AFTER install or link are completed.
9
10 var npm = require("./npm.js")
11   , log = require("npmlog")
12   , chain = require("slide").chain
13   , fs = require("graceful-fs")
14   , path = require("path")
15   , lifecycle = require("./utils/lifecycle.js")
16   , readJson = require("read-package-json")
17   , link = require("./utils/link.js")
18   , linkIfExists = link.ifExists
19   , cmdShim = require("cmd-shim")
20   , cmdShimIfExists = cmdShim.ifExists
21   , asyncMap = require("slide").asyncMap
22
23 module.exports = build
24 build.usage = "npm build <folder>\n(this is plumbing)"
25
26 build._didBuild = {}
27 build._noLC = {}
28 function build (args, global, didPre, didRB, cb) {
29   if (typeof cb !== "function") cb = didRB, didRB = false
30   if (typeof cb !== "function") cb = didPre, didPre = false
31   if (typeof cb !== "function") {
32     cb = global, global = npm.config.get("global")
33   }
34   // it'd be nice to asyncMap these, but actually, doing them
35   // in parallel generally munges up the output from node-waf
36   var builder = build_(global, didPre, didRB)
37   chain(args.map(function (arg) { return function (cb) {
38     builder(arg, cb)
39   }}), cb)
40 }
41
42 function build_ (global, didPre, didRB) { return function (folder, cb) {
43   folder = path.resolve(folder)
44   build._didBuild[folder] = true
45   log.info("build", folder)
46   readJson(path.resolve(folder, "package.json"), function (er, pkg) {
47     if (er) return cb(er)
48     chain
49       ( [ !didPre && [lifecycle, pkg, "preinstall", folder]
50         , [linkStuff, pkg, folder, global, didRB]
51         , pkg.name === "npm" && [writeBuiltinConf, folder]
52         , didPre !== build._noLC && [lifecycle, pkg, "install", folder]
53         , didPre !== build._noLC && [lifecycle, pkg, "postinstall", folder]
54         , didPre !== build._noLC
55           && npm.config.get("npat")
56           && [lifecycle, pkg, "test", folder] ]
57       , cb )
58   })
59 }}
60
61 function writeBuiltinConf (folder, cb) {
62   // the builtin config is "sticky". Any time npm installs itself,
63   // it puts its builtin config file there, as well.
64   if (!npm.config.usingBuiltin
65       || folder !== path.dirname(__dirname)) {
66     return cb()
67   }
68   npm.config.save("builtin", cb)
69 }
70
71 function linkStuff (pkg, folder, global, didRB, cb) {
72   // allow to opt out of linking binaries.
73   if (npm.config.get("bin-links") === false) return cb()
74
75   // if it's global, and folder is in {prefix}/node_modules,
76   // then bins are in {prefix}/bin
77   // otherwise, then bins are in folder/../.bin
78   var parent = path.dirname(folder)
79     , gnm = global && npm.globalDir
80     , top = parent === npm.dir
81     , gtop = parent === gnm
82
83   log.verbose("linkStuff", [global, gnm, gtop, parent])
84   log.info("linkStuff", pkg._id)
85
86   shouldWarn(pkg, folder, global, function() {
87     asyncMap( [linkBins, linkMans, !didRB && rebuildBundles]
88             , function (fn, cb) {
89       if (!fn) return cb()
90       log.verbose(fn.name, pkg._id)
91       fn(pkg, folder, parent, gtop, cb)
92     }, cb)
93   })
94 }
95
96 function shouldWarn(pkg, folder, global, cb) {
97   var parent = path.dirname(folder)
98     , top = parent === npm.dir
99     , cwd = process.cwd()
100
101   readJson(path.resolve(cwd, "package.json"), function(er, topPkg) {
102     if (er) return cb(er)
103
104     var linkedPkg = path.basename(cwd)
105       , currentPkg = path.basename(folder)
106
107     // current searched package is the linked package on first call
108     if (linkedPkg !== currentPkg) {
109
110       if (!topPkg.dependencies) return cb()
111
112       // don't generate a warning if it's listed in dependencies
113       if (Object.keys(topPkg.dependencies).indexOf(currentPkg) === -1) {
114
115         if (top && pkg.preferGlobal && !global) {
116           log.warn("prefer global", pkg._id + " should be installed with -g")
117         }
118       }
119     }
120
121     cb()
122   })
123 }
124
125 function rebuildBundles (pkg, folder, parent, gtop, cb) {
126   if (!npm.config.get("rebuild-bundle")) return cb()
127
128   var deps = Object.keys(pkg.dependencies || {})
129              .concat(Object.keys(pkg.devDependencies || {}))
130     , bundles = pkg.bundleDependencies || pkg.bundledDependencies || []
131
132   fs.readdir(path.resolve(folder, "node_modules"), function (er, files) {
133     // error means no bundles
134     if (er) return cb()
135
136     log.verbose("rebuildBundles", files)
137     // don't asyncMap these, because otherwise build script output
138     // gets interleaved and is impossible to read
139     chain(files.filter(function (file) {
140       // rebuild if:
141       // not a .folder, like .bin or .hooks
142       return !file.match(/^[\._-]/)
143           // not some old 0.x style bundle
144           && file.indexOf("@") === -1
145           // either not a dep, or explicitly bundled
146           && (deps.indexOf(file) === -1 || bundles.indexOf(file) !== -1)
147     }).map(function (file) {
148       file = path.resolve(folder, "node_modules", file)
149       return function (cb) {
150         if (build._didBuild[file]) return cb()
151         log.verbose("rebuild bundle", file)
152         // if file is not a package dir, then don't do it.
153         fs.lstat(path.resolve(file, "package.json"), function (er, st) {
154           if (er) return cb()
155           build_(false)(file, cb)
156         })
157     }}), cb)
158   })
159 }
160
161 function linkBins (pkg, folder, parent, gtop, cb) {
162   if (!pkg.bin || !gtop && path.basename(parent) !== "node_modules") {
163     return cb()
164   }
165   var binRoot = gtop ? npm.globalBin
166                      : path.resolve(parent, ".bin")
167   log.verbose("link bins", [pkg.bin, binRoot, gtop])
168
169   asyncMap(Object.keys(pkg.bin), function (b, cb) {
170     linkBin( path.resolve(folder, pkg.bin[b])
171            , path.resolve(binRoot, b)
172            , gtop && folder
173            , function (er) {
174       if (er) return cb(er)
175       // bins should always be executable.
176       // XXX skip chmod on windows?
177       var src = path.resolve(folder, pkg.bin[b])
178       fs.chmod(src, npm.modes.exec, function (er) {
179         if (er && er.code === "ENOENT" && npm.config.get("ignore-scripts")) {
180           return cb()
181         }
182         if (er || !gtop) return cb(er)
183         var dest = path.resolve(binRoot, b)
184           , out = npm.config.get("parseable")
185                 ? dest + "::" + src + ":BINFILE"
186                 : dest + " -> " + src
187         console.log(out)
188         cb()
189       })
190     })
191   }, cb)
192 }
193
194 function linkBin (from, to, gently, cb) {
195   if (process.platform !== "win32") {
196     return linkIfExists(from, to, gently, cb)
197   } else {
198     return cmdShimIfExists(from, to, cb)
199   }
200 }
201
202 function linkMans (pkg, folder, parent, gtop, cb) {
203   if (!pkg.man || !gtop || process.platform === "win32") return cb()
204
205   var manRoot = path.resolve(npm.config.get("prefix"), "share", "man")
206
207   // make sure that the mans are unique.
208   // otherwise, if there are dupes, it'll fail with EEXIST
209   var set = pkg.man.reduce(function (acc, man) {
210     acc[path.basename(man)] = man
211     return acc
212   }, {})
213   pkg.man = pkg.man.filter(function (man) {
214     return set[path.basename(man)] === man
215   })
216
217   asyncMap(pkg.man, function (man, cb) {
218     if (typeof man !== "string") return cb()
219     var parseMan = man.match(/(.*\.([0-9]+)(\.gz)?)$/)
220       , stem = parseMan[1]
221       , sxn = parseMan[2]
222       , gz = parseMan[3] || ""
223       , bn = path.basename(stem)
224       , manDest = path.join(manRoot, "man" + sxn, bn)
225
226     linkIfExists(man, manDest, gtop && folder, cb)
227   }, cb)
228 }