[483] | 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 | 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 | }); |
---|