1 | // |
---|
2 | // The Dojo Build System |
---|
3 | // |
---|
4 | // This is application is implemented as an AMD package intended to be loaded and executed by dojo. It is known to work correctly |
---|
5 | // with node.js (fast!) and rhino (slow!). The program may be started from a command prompt as follows: |
---|
6 | // |
---|
7 | // node.js: |
---|
8 | // >node path/to/dojotoolkit/dojo/dojo.js load=build <arguments> |
---|
9 | // |
---|
10 | // rhino: |
---|
11 | // >java -jar path/to/js.jar path/to/dojotoolkit/dojo/dojo.js baseUrl=path/to/dojotoolkit/dojo load=build <arguments> |
---|
12 | // |
---|
13 | // * notice that, owing to the defective design of rhino, it is impossible for a script to know the location from |
---|
14 | // which it was executed; therefore, the baseUrl must be provided. |
---|
15 | // |
---|
16 | // util/buildscripts/bng: |
---|
17 | // TODOC |
---|
18 | // |
---|
19 | // The application proceeds as follows: |
---|
20 | // |
---|
21 | // 1. Process the command line and then process the build control script(s)/profile as specified by the command line. |
---|
22 | // 2. Discover all resources as instructed by the build control script |
---|
23 | // 3. Move the resources through an ordered set of gates. Zero to many synchronous and/or asynchronous transforms may be applied to various |
---|
24 | // resources as specified by the build control script. Different resources can be subject to different transforms. Resources are allowed |
---|
25 | // to move through gates without stopping until a "synchronized" gate is encountered. All transforms must complete for the previous gate before |
---|
26 | // any transform is allowed on the synchronized gate. |
---|
27 | // 4. After the last gate has been completed, print a done message and terminate. |
---|
28 | // |
---|
29 | // See also: |
---|
30 | // |
---|
31 | // project home: http://bdframework.org/bdBuild/index |
---|
32 | // fossil: http://bdframework.org/bdBuild/repo |
---|
33 | // github: https://github.com/altoviso/bdBuild |
---|
34 | // docs: http://bdframework.org/bdBuild/docs |
---|
35 | |
---|
36 | define(["require", "dojo/has"], function(require, has) { |
---|
37 | |
---|
38 | // host-dependent environment initialization |
---|
39 | if (has("host-node")) { |
---|
40 | define("commandLineArgs", function() { |
---|
41 | //arg[0] is node; argv[1] is dojo.js; therefore, start with argv[2] |
---|
42 | return process.argv.slice(2); |
---|
43 | }); |
---|
44 | |
---|
45 | // helps during dev or heavily async node... |
---|
46 | var util = require.nodeRequire("util"); |
---|
47 | debug= function(it, depth, inspect){ |
---|
48 | util.debug(inspect ? util.inspect(it, false, depth) : it); |
---|
49 | }; |
---|
50 | |
---|
51 | // TODO: make this real |
---|
52 | has.add("is-windows", 0); |
---|
53 | } else if (has("host-rhino")) { |
---|
54 | define("commandLineArgs", [], function() { |
---|
55 | var result= []; |
---|
56 | require.rawConfig.commandLineArgs.forEach(function(item) { |
---|
57 | var parts= item.split("="); |
---|
58 | if (parts[0]!="baseUrl") { |
---|
59 | result.push(item); |
---|
60 | } |
---|
61 | }); |
---|
62 | return result; |
---|
63 | }); |
---|
64 | // TODO: make this real |
---|
65 | has.add("is-windows", /indows/.test(environment["os.name"])); |
---|
66 | } else { |
---|
67 | console.log("unknown environment; terminating."); |
---|
68 | return 0; |
---|
69 | } |
---|
70 | |
---|
71 | this.require.scopeify= function(moduleList) { |
---|
72 | for (var p, mid, module, text= "", contextRequire= this, args= moduleList.split(","), i= 0; i<args.length;) { |
---|
73 | mid= args[i++].match(/\S+/)[0]; |
---|
74 | module= contextRequire(mid); |
---|
75 | mid= mid.match(/[^\/]+$/)[0]; |
---|
76 | for (p in module) { |
---|
77 | text+= "var " + p + "=" + mid + "." + p + ";\n"; |
---|
78 | } |
---|
79 | } |
---|
80 | return text; |
---|
81 | }; |
---|
82 | |
---|
83 | // run the build program |
---|
84 | require(["./buildControl", "./process"], function(bc, process) { |
---|
85 | var |
---|
86 | gateListeners= bc.gateListeners= [], |
---|
87 | |
---|
88 | transforms= bc.transforms, |
---|
89 | transformJobs= bc.transformJobs, |
---|
90 | transformJobsLength= transformJobs.length, |
---|
91 | |
---|
92 | // all discovered resources |
---|
93 | resources= [], |
---|
94 | |
---|
95 | reportError= function(resource, err) { |
---|
96 | bc.log("transformFailed", ["resource", resource.src, "transform", resource.jobPos, "error", err]); |
---|
97 | resource.error= true; |
---|
98 | }, |
---|
99 | |
---|
100 | returnFromAsyncProc= function(resource, err) { |
---|
101 | bc.waiting--; |
---|
102 | if (err) { |
---|
103 | // notice reportError can decide to continue or panic |
---|
104 | reportError(resource, err); |
---|
105 | } |
---|
106 | advance(resource, true); |
---|
107 | }, |
---|
108 | |
---|
109 | advance= function(resource, continuingSameGate) { |
---|
110 | if (resource.error) { |
---|
111 | return; |
---|
112 | } |
---|
113 | if (!continuingSameGate) { |
---|
114 | // first time trying to advance through the current gate |
---|
115 | bc.waiting++; |
---|
116 | } |
---|
117 | |
---|
118 | // apply all transforms with a gateId <= the current gate for resource that have not yet been applied |
---|
119 | var err, nextJobPos, candidate; |
---|
120 | while (1) { |
---|
121 | nextJobPos= resource.jobPos + 1, |
---|
122 | candidate= nextJobPos<resource.job.length && resource.job[nextJobPos]; |
---|
123 | // candidate (if any) is a [transformProc, gateId] pair |
---|
124 | if (candidate && candidate[1]<=bc.currentGate) { |
---|
125 | resource.jobPos++; |
---|
126 | bc.waiting++; |
---|
127 | err= candidate[0](resource, returnFromAsyncProc); |
---|
128 | if (err===returnFromAsyncProc) { |
---|
129 | // the transform proc must call returnFromAsyncProc when complete |
---|
130 | return; |
---|
131 | } |
---|
132 | bc.waiting--; |
---|
133 | if (err) { |
---|
134 | // notice we reportError can decide to continue or panic |
---|
135 | reportError(resource, err); |
---|
136 | // if reportError didn't panic, then this break will cause this resource to clear the next |
---|
137 | // gate; when all resources have cleared the next gate, passGate will notice error count and |
---|
138 | // quit |
---|
139 | break; |
---|
140 | } |
---|
141 | } else { |
---|
142 | break; |
---|
143 | } |
---|
144 | } |
---|
145 | |
---|
146 | // got through the gate; advise passGate which will decrement the lock we set at top of this function |
---|
147 | passGate(); |
---|
148 | }, |
---|
149 | |
---|
150 | advanceGate= function(currentGate) { |
---|
151 | while (1) { |
---|
152 | bc.currentGate= ++currentGate; |
---|
153 | bc.log("pacify", "starting " + bc.gates[bc.currentGate][2] + "..."); |
---|
154 | gateListeners.forEach(function(listener){ |
---|
155 | listener(bc.gates[bc.currentGate][1]); |
---|
156 | }); |
---|
157 | if (currentGate==bc.gates.length-1 || bc.gates[currentGate+1][0]) { |
---|
158 | // if we've either advanced to the last gate or the next gate is a synchronized gate, then hold at the current gate |
---|
159 | return; |
---|
160 | } |
---|
161 | } |
---|
162 | }, |
---|
163 | |
---|
164 | passGate= bc.passGate= function() { |
---|
165 | if (--bc.waiting) { |
---|
166 | return; |
---|
167 | } // else all processes have passed through bc.currentGate |
---|
168 | |
---|
169 | if(bc.checkDiscovery){ |
---|
170 | //passing the first gate which is dicovery and just echoing discovery; therefore |
---|
171 | process.exit(0); |
---|
172 | } |
---|
173 | |
---|
174 | if (bc.currentGate<bc.gates.length-1) { |
---|
175 | advanceGate(bc.currentGate); |
---|
176 | // hold the next gate until all resources have been advised |
---|
177 | bc.waiting++; |
---|
178 | resources.forEach(function(resource){ advance(resource, 0); }); |
---|
179 | // release the hold placed above |
---|
180 | passGate(); |
---|
181 | } else { |
---|
182 | if (!resources.length) { |
---|
183 | bc.log("discoveryFailed"); |
---|
184 | } |
---|
185 | bc.log("pacify", "Process finished normally.\n\terrors: " + bc.getErrorCount() + "\n\twarnings: " + bc.getWarnCount() + "\n\tbuild time: " + ((new Date()).getTime() - bc.startTimestamp.getTime()) / 1000 + " seconds"); |
---|
186 | process.exit(bc.exitCode); |
---|
187 | // that's all, folks... |
---|
188 | } |
---|
189 | }; |
---|
190 | |
---|
191 | bc.start= function(resource) { |
---|
192 | // check for collisions |
---|
193 | var |
---|
194 | src= resource.src, |
---|
195 | dest= resource.dest; |
---|
196 | if (bc.resourcesByDest[src]) { |
---|
197 | // a dest is scheduled to overwrite a source |
---|
198 | bc.log("overwrite", ["input", src, "resource destined for same location: ", bc.resourcesByDest[src].src]); |
---|
199 | return; |
---|
200 | } |
---|
201 | if (bc.resourcesByDest[dest]) { |
---|
202 | // multiple srcs scheduled to write into a single dest |
---|
203 | bc.log("outputCollide", ["source-1", src, "source-2", bc.resourcesByDest[dest].src]); |
---|
204 | return; |
---|
205 | } |
---|
206 | // remember the resources in the global maps |
---|
207 | bc.resources[resource.src]= resource; |
---|
208 | bc.resourcesByDest[resource.dest]= resource; |
---|
209 | |
---|
210 | if(bc.checkDiscovery){ |
---|
211 | bc.log("pacify", src + "-->" + dest); |
---|
212 | return; |
---|
213 | } |
---|
214 | |
---|
215 | // find the transformJob and start it... |
---|
216 | for (var i= 0; i<transformJobsLength; i++) { |
---|
217 | if (transformJobs[i][0](resource, bc)) { |
---|
218 | // job gives a vector of functions to apply to the resource |
---|
219 | // jobPos says the index in the job vector that has been applied |
---|
220 | resources.push(resource); |
---|
221 | resource.job= transformJobs[i][1]; |
---|
222 | resource.jobPos= -1; |
---|
223 | advance(resource); |
---|
224 | return; |
---|
225 | } |
---|
226 | } |
---|
227 | bc.log("noTransform", ["resoures", resource.src]); |
---|
228 | }; |
---|
229 | |
---|
230 | function doBuild(){ |
---|
231 | var |
---|
232 | transformNames= [], |
---|
233 | pluginNames= [], |
---|
234 | deps= []; |
---|
235 | bc.discoveryProcs.forEach(function(mid) { deps.push(mid); }); |
---|
236 | for (var p in bc.transforms) { |
---|
237 | // each item is a [AMD-MID, gateId] pair |
---|
238 | transformNames.push(p); |
---|
239 | deps.push(bc.transforms[p][0]); |
---|
240 | } |
---|
241 | for (p in bc.plugins) { |
---|
242 | pluginNames.push(p); |
---|
243 | deps.push(bc.plugins[p]); |
---|
244 | } |
---|
245 | bc.plugins= {}; |
---|
246 | require(deps, function() { |
---|
247 | // pull out the discovery procedures |
---|
248 | for (var discoveryProcs= [], argsPos= 0; argsPos<bc.discoveryProcs.length; discoveryProcs.push(arguments[argsPos++])); |
---|
249 | |
---|
250 | // replace the transformIds in the transformJobs with the actual transform procs; similarly for plugins |
---|
251 | for (var id, proc, i=0; i<transformNames.length;) { |
---|
252 | id= transformNames[i++]; |
---|
253 | proc= arguments[argsPos++]; |
---|
254 | // replace every occurence of id with proc |
---|
255 | transformJobs.forEach(function(item) { |
---|
256 | // item is a [predicate, vector of [transformId, gateId] pairs] pairs |
---|
257 | for (var transforms=item[1], i= 0; i<transforms.length; i++) { |
---|
258 | if (transforms[i][0]==id) { |
---|
259 | transforms[i][0]= proc; |
---|
260 | break; |
---|
261 | } |
---|
262 | } |
---|
263 | }); |
---|
264 | } |
---|
265 | for (i=0; i<pluginNames.length;) { |
---|
266 | bc.plugins[bc.getSrcModuleInfo(pluginNames[i++]).mid]= arguments[argsPos++]; |
---|
267 | } |
---|
268 | |
---|
269 | // start the transform engine: initialize bc.currentGate and bc.waiting, then discover and start each resource. |
---|
270 | // note: discovery procs will call bc.start with each discovered resource, which will call advance, which will |
---|
271 | // enter each resource in a race to the next gate, which will result in many bc.waiting incs/decs |
---|
272 | bc.waiting= 1; // matches *1* |
---|
273 | bc.log("pacify", "discovering resources..."); |
---|
274 | advanceGate(-1); |
---|
275 | discoveryProcs.forEach(function(proc) { proc(); }); |
---|
276 | passGate(); // matched *1* |
---|
277 | }); |
---|
278 | } |
---|
279 | |
---|
280 | if(!bc.errorCount && bc.release){ |
---|
281 | doBuild(); |
---|
282 | } |
---|
283 | }); |
---|
284 | }); |
---|