You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
9.6 KiB
288 lines
9.6 KiB
// |
|
// The Dojo Build System |
|
// |
|
// This is application is implemented as an AMD package intended to be loaded and executed by dojo. It is known to work correctly |
|
// with node.js (fast!) and rhino (slow!). The program may be started from a command prompt as follows: |
|
// |
|
// node.js: |
|
// >node path/to/dojotoolkit/dojo/dojo.js load=build <arguments> |
|
// |
|
// rhino: |
|
// >java -jar path/to/js.jar path/to/dojotoolkit/dojo/dojo.js baseUrl=path/to/dojotoolkit/dojo load=build <arguments> |
|
// |
|
// * notice that, owing to the defective design of rhino, it is impossible for a script to know the location from |
|
// which it was executed; therefore, the baseUrl must be provided. |
|
// |
|
// util/buildscripts/bng: |
|
// TODOC |
|
// |
|
// The application proceeds as follows: |
|
// |
|
// 1. Process the command line and then process the build control script(s)/profile as specified by the command line. |
|
// 2. Discover all resources as instructed by the build control script |
|
// 3. Move the resources through an ordered set of gates. Zero to many synchronous and/or asynchronous transforms may be applied to various |
|
// resources as specified by the build control script. Different resources can be subject to different transforms. Resources are allowed |
|
// to move through gates without stopping until a "synchronized" gate is encountered. All transforms must complete for the previous gate before |
|
// any transform is allowed on the synchronized gate. |
|
// 4. After the last gate has been completed, print a done message and terminate. |
|
// |
|
// See also: |
|
// |
|
// project home: http://bdframework.org/bdBuild/index |
|
// fossil: http://bdframework.org/bdBuild/repo |
|
// github: https://github.com/altoviso/bdBuild |
|
// docs: http://bdframework.org/bdBuild/docs |
|
|
|
define(["require", "dojo/has"], function(require, has){ |
|
|
|
// host-dependent environment initialization |
|
if(has("host-node")){ |
|
define("commandLineArgs", function(){ |
|
//arg[0] is node; argv[1] is dojo.js; therefore, start with argv[2] |
|
return process.argv.slice(2); |
|
}); |
|
|
|
// helps during dev or heavily async node... |
|
var util = require.nodeRequire("util"); |
|
debug = function(it, depth, inspect){ |
|
util.debug(inspect ? util.inspect(it, false, depth) : it); |
|
}; |
|
|
|
has.add("is-windows", process.platform == "win32"); |
|
}else if(has("host-rhino")){ |
|
define("commandLineArgs", [], function(){ |
|
var result = []; |
|
require.rawConfig.commandLineArgs.forEach(function(item){ |
|
var parts = item.split("="); |
|
if(parts[0]!="baseUrl"){ |
|
result.push(item); |
|
} |
|
}); |
|
return result; |
|
}); |
|
// TODO: make this real |
|
has.add("is-windows", /indows/.test(environment["os.name"])); |
|
}else{ |
|
console.log("unknown environment; terminating."); |
|
return 0; |
|
} |
|
|
|
this.require.scopeify = function(moduleList){ |
|
for(var p, mid, module, text = "", contextRequire = this, args = moduleList.split(","), i = 0; i<args.length;){ |
|
mid = args[i++].match(/\S+/)[0]; |
|
module = contextRequire(mid); |
|
mid = mid.match(/[^\/]+$/)[0]; |
|
for(p in module){ |
|
text+= "var " + p + "=" + mid + "." + p + ";\n"; |
|
} |
|
} |
|
return text; |
|
}; |
|
|
|
// run the build program |
|
require(["./buildControl", "./process"], function(bc, process){ |
|
var |
|
gateListeners = bc.gateListeners = [], |
|
|
|
transforms = bc.transforms, |
|
transformJobs = bc.transformJobs, |
|
transformJobsLength = transformJobs.length, |
|
|
|
// all discovered resources |
|
resources = [], |
|
|
|
reportError = function(resource, err){ |
|
bc.log("transformFailed", ["resource", resource.src, "transform", resource.jobPos, "error", err]); |
|
resource.error = true; |
|
}, |
|
|
|
returnFromAsyncProc = function(resource, err){ |
|
bc.waiting--; |
|
if(err){ |
|
// notice reportError can decide to continue or panic |
|
reportError(resource, err); |
|
} |
|
advance(resource, true); |
|
}, |
|
|
|
advance = function(resource, continuingSameGate){ |
|
if(resource.error){ |
|
return; |
|
} |
|
if(!continuingSameGate){ |
|
// first time trying to advance through the current gate |
|
bc.waiting++; |
|
} |
|
|
|
// apply all transforms with a gateId <= the current gate for resource that have not yet been applied |
|
var err, nextJobPos, candidate; |
|
while(1){ |
|
nextJobPos = resource.jobPos + 1; |
|
candidate = nextJobPos<resource.job.length && resource.job[nextJobPos]; |
|
// candidate (if any) is a [transformProc, gateId] pair |
|
if(candidate && candidate[1]<=bc.currentGate){ |
|
resource.jobPos++; |
|
bc.waiting++; |
|
err = candidate[0](resource, returnFromAsyncProc); |
|
if(err===returnFromAsyncProc){ |
|
// the transform proc must call returnFromAsyncProc when complete |
|
return; |
|
} |
|
bc.waiting--; |
|
if(err){ |
|
// notice we reportError can decide to continue or panic |
|
reportError(resource, err); |
|
// if reportError didn't panic, then this break will cause this resource to clear the next |
|
// gate; when all resources have cleared the next gate, passGate will notice error count and |
|
// quit |
|
break; |
|
} |
|
}else{ |
|
break; |
|
} |
|
} |
|
|
|
// got through the gate; advise passGate which will decrement the lock we set at top of this function |
|
passGate(); |
|
}, |
|
|
|
advanceGate = function(currentGate){ |
|
while(1){ |
|
bc.currentGate = ++currentGate; |
|
bc.log("pacify", "starting " + bc.gates[bc.currentGate][2] + "..."); |
|
gateListeners.forEach(function(listener){ |
|
listener(bc.gates[bc.currentGate][1]); |
|
}); |
|
if(currentGate==bc.gates.length-1 || bc.gates[currentGate+1][0]){ |
|
// if we've either advanced to the last gate or the next gate is a synchronized gate, then hold at the current gate |
|
return; |
|
} |
|
} |
|
}, |
|
|
|
passGate = bc.passGate = function(){ |
|
if(--bc.waiting){ |
|
return; |
|
} // else all processes have passed through bc.currentGate |
|
|
|
if(bc.checkDiscovery){ |
|
//passing the first gate which is dicovery and just echoing discovery; therefore |
|
process.exit(0); |
|
} |
|
|
|
if(bc.currentGate<bc.gates.length-1){ |
|
advanceGate(bc.currentGate); |
|
// hold the next gate until all resources have been advised |
|
bc.waiting++; |
|
resources.forEach(function(resource){ advance(resource, 0); }); |
|
// release the hold placed above |
|
passGate(); |
|
}else{ |
|
if(!resources.length){ |
|
bc.log("discoveryFailed"); |
|
} |
|
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"); |
|
if(!bc.exitCode && bc.getErrorCount()){ |
|
bc.exitCode = 1; |
|
} |
|
process.exit(bc.exitCode); |
|
// that's all, folks... |
|
} |
|
}; |
|
|
|
bc.start = function(resource){ |
|
// check for collisions |
|
var |
|
src = resource.src, |
|
dest = resource.dest; |
|
if(bc.resourcesByDest[src]){ |
|
// a dest is scheduled to overwrite a source |
|
bc.log("overwrite", ["input", src, "resource destined for same location: ", bc.resourcesByDest[src].src]); |
|
return; |
|
} |
|
if(bc.resourcesByDest[dest]){ |
|
// multiple srcs scheduled to write into a single dest |
|
if(src !== bc.resourcesByDest[dest].src){ // don't squawk if same file |
|
bc.log("outputCollide", ["source-1", src, "source-2", bc.resourcesByDest[dest].src]); |
|
} |
|
return; |
|
} |
|
// remember the resources in the global maps |
|
bc.resources[resource.src] = resource; |
|
bc.resourcesByDest[resource.dest] = resource; |
|
|
|
if(bc.checkDiscovery){ |
|
bc.log("pacify", src + "-->" + dest); |
|
return; |
|
} |
|
|
|
// find the transformJob and start it... |
|
for(var i = 0; i<transformJobsLength; i++){ |
|
if(transformJobs[i][0](resource, bc)){ |
|
// job gives a vector of functions to apply to the resource |
|
// jobPos says the index in the job vector that has been applied |
|
resources.push(resource); |
|
resource.job = transformJobs[i][1]; |
|
resource.jobPos = -1; |
|
advance(resource); |
|
return; |
|
} |
|
} |
|
bc.log("noTransform", ["resoures", resource.src]); |
|
}; |
|
|
|
function doBuild(){ |
|
var |
|
transformNames = [], |
|
pluginNames = [], |
|
deps = []; |
|
bc.discoveryProcs.forEach(function(mid){ deps.push(mid); }); |
|
for(var p in bc.transforms){ |
|
// each item is a [AMD-MID, gateId] pair |
|
transformNames.push(p); |
|
deps.push(bc.transforms[p][0]); |
|
} |
|
for(p in bc.plugins){ |
|
pluginNames.push(p); |
|
deps.push(bc.plugins[p]); |
|
} |
|
bc.plugins = {}; |
|
require(deps, function(){ |
|
// pull out the discovery procedures |
|
for(var discoveryProcs = [], argsPos = 0; argsPos<bc.discoveryProcs.length; discoveryProcs.push(arguments[argsPos++])); |
|
|
|
// replace the transformIds in the transformJobs with the actual transform procs; similarly for plugins |
|
for(var id, proc, i=0; i<transformNames.length;){ |
|
id = transformNames[i++]; |
|
proc = arguments[argsPos++]; |
|
// replace every occurence of id with proc |
|
transformJobs.forEach(function(item){ |
|
// item is a [predicate, vector of [transformId, gateId] pairs] pairs |
|
for(var transforms=item[1], i = 0; i<transforms.length; i++){ |
|
if(transforms[i][0]==id){ |
|
transforms[i][0] = proc; |
|
break; |
|
} |
|
} |
|
}); |
|
} |
|
for(i=0; i<pluginNames.length;){ |
|
bc.plugins[bc.getSrcModuleInfo(pluginNames[i++]).mid] = arguments[argsPos++]; |
|
} |
|
|
|
// start the transform engine: initialize bc.currentGate and bc.waiting, then discover and start each resource. |
|
// note: discovery procs will call bc.start with each discovered resource, which will call advance, which will |
|
// enter each resource in a race to the next gate, which will result in many bc.waiting incs/decs |
|
bc.waiting = 1; // matches *1* |
|
bc.log("pacify", "discovering resources..."); |
|
advanceGate(-1); |
|
discoveryProcs.forEach(function(proc){ proc(); }); |
|
passGate(); // matched *1* |
|
}); |
|
} |
|
|
|
if(!bc.getErrorCount() && bc.release){ |
|
doBuild(); |
|
} |
|
}); |
|
});
|
|
|