587 lines
18 KiB
JavaScript
587 lines
18 KiB
JavaScript
define([
|
|
"require",
|
|
"dojo/json",
|
|
"dojo/has",
|
|
"./fs",
|
|
"./fileUtils",
|
|
"./process",
|
|
"commandLineArgs",
|
|
"./stringify",
|
|
"./version",
|
|
"./messages",
|
|
"./v1xProfiles",
|
|
"dojo/text!./help.txt"
|
|
], function(require, json, has, fs, fileUtils, process, argv, stringify, version, messages, v1xProfiles, help){
|
|
///
|
|
// AMD-ID build/argv
|
|
//
|
|
// This module parses the command line and returns the result as a hash of from switch-name to switch value
|
|
// plus the additional property profiles which is a a vector of profile objects build objects, ordered as
|
|
// provided on the command line.
|
|
//
|
|
// Design of relative paths:
|
|
//
|
|
// * All relative source paths and relative bc.releaseDir are relative to bc.basePath
|
|
// * All relative destination paths are relative to bc.releaseDir
|
|
// * Relative bd.basePath found in a build control script is relative to the directory that contains the script
|
|
// * Any relative path found on the command line is relative to the current working directory
|
|
//
|
|
// For each build control script that is compiled, if bc.basePath is undefined, it is set to the directory that
|
|
// contains the script. Notice that this feature can be disabled by setting e.g., "basePath==0" in any build control script.
|
|
|
|
eval(require.scopeify("./fileUtils"));
|
|
var
|
|
// used to build up the result
|
|
result = {
|
|
profiles:[]
|
|
},
|
|
|
|
cwd = process.cwd(),
|
|
|
|
// we need to know the dojo path and the path to /util/buildscripts to process v1.x profiles and profiles in the
|
|
// /util/buildscripts/profiles directory
|
|
dojoPath = computePath(require.toUrl("dojo/package.json").match(/(.+)\/package\.json$/)[1], cwd),
|
|
utilBuildscriptsPath = compactPath(catPath(dojoPath, "/../util/buildscripts")),
|
|
|
|
printVersion = 0,
|
|
printHelp = 0,
|
|
checkArgs = 0,
|
|
|
|
illegalArgumentValue = function(argumentName, position){
|
|
messages.log("inputIllegalCommandlineArg", ["switch", argumentName, "position", position]);
|
|
},
|
|
|
|
evalScriptArg=
|
|
function(arg){
|
|
if(arg=="true"){
|
|
return true;
|
|
}else if(arg=="false"){
|
|
return false;
|
|
}else if(arg=="null"){
|
|
return null;
|
|
}else if(isNaN(arg)){
|
|
return arg+"";
|
|
}else{
|
|
return Number(arg);
|
|
}
|
|
},
|
|
|
|
readProfile = function(
|
|
scriptType,
|
|
filename
|
|
){
|
|
///
|
|
// Load, evaluate and return the result of the contents of the file given by
|
|
// filename in a scope type given by scriptType as follows:
|
|
//
|
|
// When scriptType is "require", contents of filename should be either an application of require to a configuration object or
|
|
// a bare require object. The contents is evaluated as follows:
|
|
// `code
|
|
// (function(){
|
|
// var __result = 0, require = function(config){__result=config;};
|
|
// <contents>
|
|
// return __result || require;
|
|
// })();
|
|
//
|
|
// Notice if <contents> defines require as a bare object, it will overwrite the provided require, result will be 0, and the bare
|
|
// object will be returned; otherwise, if <contents> contains an application to a configuration object, result will be truthy
|
|
// and therefore be returned.
|
|
//
|
|
// When scriptType is "dojoConfig", contents of filename should include the variable "dojoConfig" which should hold a
|
|
// a dojo loader configuration:
|
|
// `code
|
|
// (function(){
|
|
// <contents>
|
|
// return dojoConfig;
|
|
// })();
|
|
//
|
|
// When scriptType is "profile", contents of filename should be Javascript code that either defines the variable "dependencies"
|
|
// or the variable "profile". The name dependencies is deprecated; if both names exist, then the value of dependencies is ignored.
|
|
// The value should be a Javascript object that contains profile properties necessary to effect the desired output. If filename does not
|
|
// contain a ".js" suffix, then it is assumed to be a profile in the /util/buildscripts/profile directory
|
|
// `code
|
|
// (function(selfPath, profile, dependencies){
|
|
// return profile || dependencies;
|
|
// })(<filename>, 0, 0);
|
|
//
|
|
// For script types "require" and "dojoConfig", if filename gives the filetype ".html" or ".htm", then the file is assumed to be
|
|
// an html file that contains a <script> element that contains Javascript source code as described above.
|
|
//
|
|
// If a profile is processed and it contains the property prefixes or the property layers with a layer further containing the property
|
|
// dependencies, then it is assumed to be a pre-version 1.7 build profile and the following additional processing is accomplished:
|
|
//
|
|
// If result contains the property basePath and/or build.basePath that is a relative path, then these are normalized
|
|
// with respect to the path given by filename.
|
|
|
|
// remember the the directory of the last build script processed; this is the default location of basePath
|
|
var path = getFilepath(filename);
|
|
|
|
if(!fileExists(filename)){
|
|
messages.log("inputFileDoesNotExist", [scriptType, filename]);
|
|
return 0;
|
|
}
|
|
|
|
try{
|
|
var src = fs.readFileSync(filename, "utf8");
|
|
}catch (e){
|
|
messages.log("inputFailedReadfile", [scriptType, filename, "error", e]);
|
|
return 0;
|
|
}
|
|
|
|
if(scriptType=="profileFile"){
|
|
messages.log("inputProfileFileDeprecated");
|
|
scriptType = "profile";
|
|
}
|
|
|
|
var fixupBasePath = function(profile){
|
|
// relative basePath is relative to the directory in which the profile resides
|
|
// all other relative paths are relative to basePath or releaseDir, and releaseDir, if relative, is relative to basePath
|
|
var fixupBasePath = function(path, referencePath){
|
|
if(path){
|
|
path = computePath(path, referencePath);
|
|
}else if(typeof path == "undefined"){
|
|
path = referencePath;
|
|
}
|
|
return path;
|
|
};
|
|
profile.basePath = fixupBasePath(profile.basePath, path);
|
|
if(profile.build && profile.build.basePath){
|
|
profile.build.basePath = fixupBasePath(profile.build.basePath, path);
|
|
}
|
|
},
|
|
f, profile;
|
|
try{
|
|
if(scriptType=="require"){
|
|
f = new Function("var __result, require= function(config){__result=config;};" + src + "; return __result || require;");
|
|
profile = f();
|
|
fixupBasePath(profile);
|
|
}else if(scriptType=="dojoConfig"){
|
|
f = new Function(src + "; return dojoConfig;");
|
|
profile = f();
|
|
fixupBasePath(profile);
|
|
}else if(scriptType=="profile"){
|
|
f = new Function("selfPath", "logger", "profile", "dependencies",
|
|
src + "; return {profile:profile, dependencies:dependencies}");
|
|
profile = f(path, messages, 0, 0);
|
|
if(profile.profile){
|
|
profile = profile.profile;
|
|
fixupBasePath(profile);
|
|
}else{
|
|
profile = v1xProfiles.processProfile(profile.dependencies, dojoPath, utilBuildscriptsPath, path);
|
|
// notice we do *not* fixup the basePath for legacy profiles since they have no concept of basePath
|
|
}
|
|
}
|
|
profile.selfFilename = filename;
|
|
messages.log("pacify", "processing " + scriptType + " resource " + filename);
|
|
return profile;
|
|
}catch(e){
|
|
messages.log("inputFailedToEvalProfile", [scriptType, filename, "error", e]);
|
|
return 0;
|
|
}
|
|
},
|
|
|
|
processHtmlDir = function(arg){
|
|
if(!fileUtils.dirExists(arg)){
|
|
messages.log("inputHTMLDirDoesNotExist", ["directory", arg]);
|
|
return 0;
|
|
}else{
|
|
var htmlFiles = [];
|
|
fs.readdirSync(arg).forEach(function(filename){
|
|
if(/\.html$/.test(filename)){
|
|
htmlFiles.push(arg + "/" + filename);
|
|
}
|
|
});
|
|
if(!htmlFiles.length){
|
|
messages.log("inputHTMLDirNoFiles", ["directory", arg]);
|
|
return 0;
|
|
}else{
|
|
return v1xProfiles.processHtmlFiles(htmlFiles, dojoPath, utilBuildscriptsPath);
|
|
}
|
|
}
|
|
},
|
|
|
|
processHtmlFiles = function(arg){
|
|
var htmlFiles = arg.split(",").filter(function(filename){
|
|
if(!fileUtils.fileExists(filename)){
|
|
messages.log("inputHTMLFileDoesNotExist", ["filename", filename]);
|
|
return 0;
|
|
}else{
|
|
return 1;
|
|
}
|
|
});
|
|
if(htmlFiles.length){
|
|
return v1xProfiles.processHtmlFiles(htmlFiles, dojoPath, utilBuildscriptsPath);
|
|
}else{
|
|
return 0;
|
|
}
|
|
},
|
|
|
|
readPackageJson = function(filename, missingMessageId){
|
|
if(!fileUtils.fileExists(filename)){
|
|
messages.log(missingMessageId, ["filename", filename]);
|
|
}else{
|
|
try{
|
|
var result = json.parse(fs.readFileSync(filename, "utf8"));
|
|
result.selfFilename = filename;
|
|
return result;
|
|
}catch(e){
|
|
messages.log("inputMalformedPackageJson", ["filename", filename]);
|
|
}
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
processPackageJson = function(packageRoot){
|
|
// process all the packages given by package.json first since specific profiles are intended to override the defaults
|
|
// packageRoot gives a path to a location where a package.json rides
|
|
var packageJsonFilename = catPath(packageRoot, "package.json"),
|
|
packageJson= readPackageJson(packageJsonFilename, "inputMissingPackageJson");
|
|
if(packageJson){
|
|
// use package.json to define a package config
|
|
packageJson.selfFilename = packageJsonFilename;
|
|
result.profiles.push({
|
|
packages:[{
|
|
name:packageJson.progName || packageJson.name,
|
|
packageJson:packageJson
|
|
}]
|
|
});
|
|
}
|
|
},
|
|
|
|
readCopyrightOrBuildNotice = function(filename, hint){
|
|
if(!fileExists(filename)){
|
|
messages.log("inputFileDoesNotExist", [hint, filename]);
|
|
}
|
|
try{
|
|
var prop = hint=="copyrightFile" ? "copyright" : "buildNotice";
|
|
result[prop] = fs.readFileSync(filename, "utf8");
|
|
}catch (e){
|
|
messages.log("inputFailedReadfile", [hint, filename, "error", e]);
|
|
}
|
|
},
|
|
|
|
normalizeSwitch = {
|
|
"-p":"profile",
|
|
"--profile":"profile",
|
|
"--profileFile":"profileFile",
|
|
"p":"profile",
|
|
"profile":"profile",
|
|
"profileFile":"profileFile",
|
|
|
|
"--package":"package",
|
|
"package":"package",
|
|
|
|
"--require":"require",
|
|
"require":"require",
|
|
|
|
"--dojoConfig":"dojoConfig",
|
|
"dojoConfig":"dojoConfig",
|
|
|
|
"--htmlDir":"htmlDir",
|
|
"htmlDir":"htmlDir",
|
|
|
|
"--htmlFiles":"htmlFiles",
|
|
"htmlFiles":"htmlFiles",
|
|
|
|
"--copyrightFile":"copyrightFile",
|
|
"copyrightFile":"copyrightFile",
|
|
|
|
"--buildNoticeFile":"buildNoticeFile",
|
|
"buildNoticeFile":"buildNoticeFile"
|
|
};
|
|
|
|
//arg[0] is "load=build"; therefore, start with argv[1]
|
|
for (var arg, processVector = [], i = 1, end = argv.length; i<end;){
|
|
arg = argv[i++];
|
|
switch (arg){
|
|
case "-p":
|
|
case "--profile":
|
|
if(i<end){
|
|
processVector.push([normalizeSwitch[arg], argv[i++], cwd]);
|
|
}else{
|
|
illegalArgumentValue(arg, i);
|
|
}
|
|
break;
|
|
|
|
case "--profileFile":
|
|
case "--require":
|
|
case "--dojoConfig":
|
|
case "--htmlDir":
|
|
case "--htmlFiles":
|
|
case "--copyrightFile":
|
|
case "--buildNoticeFile":
|
|
if(i<end){
|
|
processVector.push([normalizeSwitch[arg], getAbsolutePath(argv[i++], cwd)]);
|
|
}else{
|
|
illegalArgumentValue(arg, i);
|
|
}
|
|
break;
|
|
|
|
case "--package":
|
|
if(i<end){
|
|
argv[i++].split(",").forEach(function(path){
|
|
processVector.push(["package", getAbsolutePath(path, cwd)]);
|
|
});
|
|
}else{
|
|
illegalArgumentValue(arg, i);
|
|
}
|
|
break;
|
|
|
|
case "--writeProfile":
|
|
if(i<end){
|
|
result.writeProfile = getAbsolutePath(argv[i++], cwd);
|
|
}else{
|
|
illegalArgumentValue(arg, i);
|
|
}
|
|
break;
|
|
|
|
case "--check":
|
|
// read, process, and send the profile to the console and then exit
|
|
result.check = true;
|
|
break;
|
|
|
|
case "--check-args":
|
|
// read and process the command line args, send the profile to the console and then exit
|
|
checkArgs = true;
|
|
break;
|
|
|
|
case "--check-discovery":
|
|
// echo discovery and exit
|
|
result.checkDiscovery = true;
|
|
result.release = true;
|
|
break;
|
|
|
|
case "--debug-check":
|
|
// read, process, and send the profile to the console, including gory details, and then exit
|
|
result.debugCheck = true;
|
|
break;
|
|
|
|
case "--clean":
|
|
// deprecated; warning given when the profile is processed
|
|
result.clean = true;
|
|
break;
|
|
|
|
case "-r":
|
|
case "--release":
|
|
// do a build
|
|
result.release = true;
|
|
break;
|
|
|
|
case "--help":
|
|
// print help message
|
|
printHelp = true;
|
|
break;
|
|
|
|
case "-v":
|
|
// print the version
|
|
printVersion = function(){
|
|
messages.log("pacify", version+"");
|
|
};
|
|
break;
|
|
|
|
case "--unit-test":
|
|
// special hook for testing
|
|
if(i<end){
|
|
result.unitTest = argv[i++];
|
|
}else{
|
|
illegalArgumentValue("unit-test", i);
|
|
}
|
|
break;
|
|
|
|
case "--unit-test-param":
|
|
// special hook for testing
|
|
if(i<end){
|
|
result.unitTestParam = result.unitTestParam || [];
|
|
result.unitTestParam.push(evalScriptArg(argv[i++]));
|
|
}else{
|
|
illegalArgumentValue("unit-test", i);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// possible formats
|
|
//
|
|
// -switch value
|
|
// --switch value
|
|
// switch=value
|
|
|
|
var match = arg.match(/^\-\-?(.+)/);
|
|
if(match && i<end){
|
|
// all of the switches that take no values are listed above; therefore,
|
|
// *must* provide a value
|
|
if(i<=end){
|
|
result[match[1]] = evalScriptArg(argv[i++]);
|
|
}else{
|
|
illegalArgumentValue(arg, i);
|
|
}
|
|
}else{
|
|
// the form switch=value does *not* provide an individual value arg (it's all one string)
|
|
var parts = arg.split("=");
|
|
if(parts.length==2){
|
|
switch(parts[0]){
|
|
case "p":
|
|
case "profile":
|
|
processVector.push([normalizeSwitch[parts[0]], parts[1]]);
|
|
break;
|
|
|
|
case "package":
|
|
parts[1].split(",").forEach(function(path){
|
|
processVector.push(["package", getAbsolutePath(path, cwd)]);
|
|
});
|
|
break;
|
|
|
|
case "profileFile":
|
|
case "require":
|
|
case "dojoConfig":
|
|
case "htmlDir":
|
|
case "htmlFiles":
|
|
case "copyrightFile":
|
|
case "buildNoticeFile":
|
|
processVector.push([normalizeSwitch[parts[0]], getAbsolutePath(parts[1], cwd)]);
|
|
break;
|
|
default:
|
|
result[parts[0]] = evalScriptArg(parts[1]);
|
|
}
|
|
}else{
|
|
illegalArgumentValue(arg, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if processing html files and a nonexisting profile file is given, then assume the user intends to write
|
|
// the computed profile to that file. This feature is deprecated; use the switch --writeProfile
|
|
var processingHtmlFiles = processVector.some(function(item){ return item[0]=="htmlFiles" || item[0]=="htmlDir"; });
|
|
if(processingHtmlFiles){
|
|
for(i= 0; i<processVector.length; i++){
|
|
if(processVector[i][0]=="profileFile" && !fileExists(processVector[i][1])){
|
|
messages.log("outputToProfileFileDeprecated");
|
|
result.writeProfile = processVector[i][1];
|
|
processVector.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
processVector.forEach(function(item){
|
|
// item[0] is the switch
|
|
// item[1] is an absolute filename
|
|
var profile;
|
|
switch(item[0]){
|
|
case "profile":
|
|
var type = getFiletype(item[1], true), filename;
|
|
if(type==""){
|
|
// prefer a user profile, then the stock profiles
|
|
filename = getAbsolutePath(item[1] + ".profile.js", cwd);
|
|
if(!fileExists(filename) && !/\//.test(item[1])){
|
|
// the name given include no path; maybe it's a stock profile
|
|
filename = catPath(utilBuildscriptsPath, "profiles/" + item[1] + ".profile.js");
|
|
}
|
|
if(!fileExists(filename)){
|
|
messages.log("inputFileDoesNotExist", ["filename", filename]);
|
|
break;
|
|
}
|
|
}else if(/^(html|htm)$/.test(type)){
|
|
messages.log("inputProcessingHtmlFileNotImplemented", ["profile", filename]);
|
|
return;
|
|
}else{
|
|
filename = getAbsolutePath(item[1], cwd);
|
|
}
|
|
profile = readProfile(item[0], filename);
|
|
break;
|
|
case "htmlDir":
|
|
profile = processHtmlDir(item[1]);
|
|
break;
|
|
case "htmlFiles":
|
|
profile = processHtmlFiles(item[1]);
|
|
break;
|
|
case "package":
|
|
profile = processPackageJson(item[1]);
|
|
break;
|
|
case "copyrightFile":
|
|
case "buildNoticeFile":
|
|
profile = readCopyrightOrBuildNotice(item[1], item[0]);
|
|
break;
|
|
default:
|
|
profile = readProfile(item[0], item[1]);
|
|
}
|
|
if(profile){
|
|
result.profiles.push(profile);
|
|
}
|
|
});
|
|
|
|
|
|
|
|
if(((printHelp || printVersion) && argv.length==2) || (printHelp && printVersion && argv.length==3)){
|
|
//just asked for either help or version or both; don't do more work or reporting
|
|
if(printHelp){
|
|
messages.log("pacify", help);
|
|
messages.log("pacify", version+"");
|
|
has("host-rhino") && messages.log("pacify", "running under rhino");
|
|
has("host-node") && messages.log("pacify", "running under node");
|
|
}
|
|
printVersion && printVersion();
|
|
process.exit(0);
|
|
return 0;
|
|
}
|
|
|
|
printVersion && printVersion();
|
|
|
|
if (checkArgs){
|
|
messages.log("pacify", stringify(result));
|
|
process.exit(0);
|
|
return 0;
|
|
}
|
|
|
|
if(messages.getErrorCount()){
|
|
messages.log("pacify", "errors on command line; terminating application.");
|
|
process.exit(-1);
|
|
return 0;
|
|
}
|
|
|
|
if(!result.profiles.length){
|
|
messages.log("pacify", "no profile provided; use the option --help for help");
|
|
process.exit(-1);
|
|
return 0;
|
|
}
|
|
|
|
if(result.unitTest=="argv"){
|
|
var testId = result.unitTestParam[0],
|
|
writingExpected = testId<0;
|
|
if(writingExpected){
|
|
testId = -testId;
|
|
}
|
|
result.unitTestParam = testId;
|
|
var expectedFilename = compactPath(utilBuildscriptsPath + "/../build/tests/argvTestsExpected.js"),
|
|
expected = json.parse(fs.readFileSync(expectedFilename, "utf8")),
|
|
pathNormalize = utilBuildscriptsPath.match(/(.*)\/util\/buildscripts/)[1],
|
|
testResult = stringify(result).replace(RegExp(pathNormalize, "g"), "~"),
|
|
passed = 1;
|
|
|
|
if(writingExpected){
|
|
// write out the expected result;
|
|
console.log("result:");
|
|
debug(testResult);
|
|
expected[result.unitTestParam] = testResult;
|
|
fs.writeFileSync(expectedFilename, json.stringify(expected), "utf8");
|
|
}else{
|
|
passed = testResult==expected[result.unitTestParam];
|
|
console.log(result.unitTestParam + ":" + (passed ? "PASSED" : "FAILED"));
|
|
if(!passed){
|
|
console.log("Expected:");
|
|
console.log(expected[result.unitTestParam]);
|
|
console.log("But Got:");
|
|
console.log(testResult);
|
|
}
|
|
}
|
|
process.exit(passed ? 0 : -1);
|
|
}
|
|
|
|
return {
|
|
args:result,
|
|
readPackageJson:readPackageJson,
|
|
readProfile:readProfile
|
|
};
|
|
});
|
|
|
|
|