source: Dev/branches/rest-dojo-ui/client/util/build/main.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 9.5 KB
Line 
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
36define(["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});
Note: See TracBrowser for help on using the repository browser.