source: Dev/trunk/src/client/util/build/main.js @ 483

Last change on this file since 483 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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                has.add("is-windows", process.platform == "win32");
52        }else if(has("host-rhino")){
53                define("commandLineArgs", [], function(){
54                        var result = [];
55                        require.rawConfig.commandLineArgs.forEach(function(item){
56                                var parts = item.split("=");
57                                if(parts[0]!="baseUrl"){
58                                        result.push(item);
59                                }
60                        });
61                        return result;
62                });
63                // TODO: make this real
64                has.add("is-windows", /indows/.test(environment["os.name"]));
65        }else{
66                console.log("unknown environment; terminating.");
67                return 0;
68        }
69
70        this.require.scopeify = function(moduleList){
71                for(var p, mid, module, text = "", contextRequire = this, args = moduleList.split(","), i = 0; i<args.length;){
72                        mid = args[i++].match(/\S+/)[0];
73                        module = contextRequire(mid);
74                        mid = mid.match(/[^\/]+$/)[0];
75                        for(p in module){
76                                text+= "var " + p + "=" + mid + "." + p + ";\n";
77                        }
78                }
79                return text;
80        };
81
82        // run the build program
83        require(["./buildControl", "./process"], function(bc, process){
84                var
85                        gateListeners = bc.gateListeners = [],
86
87                        transforms = bc.transforms,
88                        transformJobs = bc.transformJobs,
89                        transformJobsLength = transformJobs.length,
90
91                        // all discovered resources
92                        resources = [],
93
94                        reportError = function(resource, err){
95                                bc.log("transformFailed", ["resource", resource.src, "transform", resource.jobPos, "error", err]);
96                                resource.error = true;
97                        },
98
99                        returnFromAsyncProc = function(resource, err){
100                                bc.waiting--;
101                                if(err){
102                                        // notice reportError can decide to continue or panic
103                                        reportError(resource, err);
104                                }
105                                advance(resource, true);
106                        },
107
108                        advance = function(resource, continuingSameGate){
109                                if(resource.error){
110                                        return;
111                                }
112                                if(!continuingSameGate){
113                                        // first time trying to advance through the current gate
114                                        bc.waiting++;
115                                }
116
117                                // apply all transforms with a gateId <= the current gate for resource that have not yet been applied
118                                var err, nextJobPos, candidate;
119                                while(1){
120                                        nextJobPos = resource.jobPos + 1;
121                                        candidate = nextJobPos<resource.job.length && resource.job[nextJobPos];
122                                        // candidate (if any) is a [transformProc, gateId] pair
123                                        if(candidate && candidate[1]<=bc.currentGate){
124                                                resource.jobPos++;
125                                                bc.waiting++;
126                                                err = candidate[0](resource, returnFromAsyncProc);
127                                                if(err===returnFromAsyncProc){
128                                                        // the transform proc must call returnFromAsyncProc when complete
129                                                        return;
130                                                }
131                                                bc.waiting--;
132                                                if(err){
133                                                        // notice we reportError can decide to continue or panic
134                                                        reportError(resource, err);
135                                                        // if reportError didn't panic, then this break will cause this resource to clear the next
136                                                        // gate; when all resources have cleared the next gate, passGate will notice error count and
137                                                        // quit
138                                                        break;
139                                                }
140                                        }else{
141                                                break;
142                                        }
143                                }
144
145                                // got through the gate; advise passGate which will decrement the lock we set at top of this function
146                                passGate();
147                        },
148
149                        advanceGate = function(currentGate){
150                                while(1){
151                                        bc.currentGate = ++currentGate;
152                                        bc.log("pacify", "starting " + bc.gates[bc.currentGate][2] + "...");
153                                        gateListeners.forEach(function(listener){
154                                                listener(bc.gates[bc.currentGate][1]);
155                                        });
156                                        if(currentGate==bc.gates.length-1 || bc.gates[currentGate+1][0]){
157                                                // if we've either advanced to the last gate or the next gate is a synchronized gate, then hold at the current gate
158                                                return;
159                                        }
160                                }
161                        },
162
163                        passGate = bc.passGate = function(){
164                                if(--bc.waiting){
165                                        return;
166                                } //    else all processes have passed through bc.currentGate
167
168                                if(bc.checkDiscovery){
169                                        //passing the first gate which is dicovery and just echoing discovery; therefore
170                                        process.exit(0);
171                                }
172
173                                if(bc.currentGate<bc.gates.length-1){
174                                        advanceGate(bc.currentGate);
175                                        // hold the next gate until all resources have been advised
176                                        bc.waiting++;
177                                        resources.forEach(function(resource){ advance(resource, 0); });
178                                        // release the hold placed above
179                                        passGate();
180                                }else{
181                                        if(!resources.length){
182                                                bc.log("discoveryFailed");
183                                        }
184                                        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");
185                                        if(!bc.exitCode && bc.getErrorCount()){
186                                                bc.exitCode = 1;
187                                        }
188                                        process.exit(bc.exitCode);
189                                        // that's all, folks...
190                                }
191                        };
192
193                bc.start = function(resource){
194                        // check for collisions
195                        var
196                                src = resource.src,
197                                dest = resource.dest;
198                        if(bc.resourcesByDest[src]){
199                                // a dest is scheduled to overwrite a source
200                                bc.log("overwrite", ["input", src, "resource destined for same location: ", bc.resourcesByDest[src].src]);
201                                return;
202                        }
203                        if(bc.resourcesByDest[dest]){
204                                // multiple srcs scheduled to write into a single dest
205                                bc.log("outputCollide", ["source-1", src, "source-2", bc.resourcesByDest[dest].src]);
206                                return;
207                        }
208                        // remember the resources in the global maps
209                        bc.resources[resource.src] = resource;
210                        bc.resourcesByDest[resource.dest] = resource;
211
212                        if(bc.checkDiscovery){
213                                bc.log("pacify", src + "-->" + dest);
214                                return;
215                        }
216
217                        // find the transformJob and start it...
218                        for(var i = 0; i<transformJobsLength; i++){
219                                if(transformJobs[i][0](resource, bc)){
220                                        // job gives a vector of functions to apply to the resource
221                                        // jobPos says the index in the job vector that has been applied
222                                        resources.push(resource);
223                                        resource.job = transformJobs[i][1];
224                                        resource.jobPos = -1;
225                                        advance(resource);
226                                        return;
227                                }
228                        }
229                        bc.log("noTransform", ["resoures", resource.src]);
230                };
231
232                function doBuild(){
233                        var
234                                transformNames = [],
235                                pluginNames = [],
236                                deps = [];
237                        bc.discoveryProcs.forEach(function(mid){ deps.push(mid); });
238                        for(var p in bc.transforms){
239                                // each item is a [AMD-MID, gateId] pair
240                                transformNames.push(p);
241                                deps.push(bc.transforms[p][0]);
242                        }
243                        for(p in bc.plugins){
244                                pluginNames.push(p);
245                                deps.push(bc.plugins[p]);
246                        }
247                        bc.plugins = {};
248                        require(deps, function(){
249                                // pull out the discovery procedures
250                                for(var discoveryProcs = [], argsPos = 0; argsPos<bc.discoveryProcs.length; discoveryProcs.push(arguments[argsPos++]));
251
252                                // replace the transformIds in the transformJobs with the actual transform procs; similarly for plugins
253                                for(var id, proc, i=0; i<transformNames.length;){
254                                        id = transformNames[i++];
255                                        proc = arguments[argsPos++];
256                                        // replace every occurence of id with proc
257                                        transformJobs.forEach(function(item){
258                                                // item is a [predicate, vector of [transformId, gateId] pairs] pairs
259                                                for(var transforms=item[1], i = 0; i<transforms.length; i++){
260                                                        if(transforms[i][0]==id){
261                                                                transforms[i][0] = proc;
262                                                                break;
263                                                        }
264                                                }
265                                        });
266                                }
267                                for(i=0; i<pluginNames.length;){
268                                        bc.plugins[bc.getSrcModuleInfo(pluginNames[i++]).mid] = arguments[argsPos++];
269                                }
270
271                                // start the transform engine: initialize bc.currentGate and bc.waiting, then discover and start each resource.
272                                // note: discovery procs will call bc.start with each discovered resource, which will call advance, which will
273                                // enter each resource in a race to the next gate, which will result in many bc.waiting incs/decs
274                                bc.waiting = 1; // matches *1*
275                                bc.log("pacify", "discovering resources...");
276                                advanceGate(-1);
277                                discoveryProcs.forEach(function(proc){ proc(); });
278                                passGate();      // matched *1*
279                        });
280                }
281
282                if(!bc.getErrorCount() && bc.release){
283                        doBuild();
284                }
285        });
286});
Note: See TracBrowser for help on using the repository browser.