source: Dev/trunk/src/client/util/build/argv.js @ 532

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

Added Dojo 1.9.3 release.

File size: 17.6 KB
Line 
1define([
2        "require",
3        "dojo/json",
4        "dojo/has",
5        "./fs",
6        "./fileUtils",
7        "./process",
8        "commandLineArgs",
9        "./stringify",
10        "./version",
11        "./messages",
12        "./v1xProfiles",
13        "dojo/text!./help.txt"
14], function(require, json, has, fs, fileUtils, process, argv, stringify, version, messages, v1xProfiles, help){
15        ///
16        // AMD-ID build/argv
17        //
18        // This module parses the command line and returns the result as a hash of from switch-name to switch value
19        // plus the additional property profiles which is a a vector of profile objects build objects, ordered as
20        // provided on the command line.
21        //
22        // Design of relative paths:
23        //
24        //       * All relative source paths and relative bc.releaseDir are relative to bc.basePath
25        //       * All relative destination paths are relative to bc.releaseDir
26        //       * Relative bd.basePath found in a build control script is relative to the directory that contains the script
27        //       * Any relative path found on the command line is relative to the current working directory
28        //
29        // For each build control script that is compiled, if bc.basePath is undefined, it is set to the directory that
30        // contains the script. Notice that this feature can be disabled by setting e.g., "basePath==0" in any build control script.
31
32        eval(require.scopeify("./fileUtils"));
33        var
34                // used to build up the result
35                result = {
36                        profiles:[]
37                },
38
39                cwd = process.cwd(),
40
41                // we need to know the dojo path and the path to /util/buildscripts to process v1.x profiles and profiles in the
42                // /util/buildscripts/profiles directory
43                dojoPath = computePath(require.toUrl("dojo/package.json").match(/(.+)\/package\.json$/)[1], cwd),
44                utilBuildscriptsPath = compactPath(catPath(dojoPath, "/../util/buildscripts")),
45
46                printVersion = 0,
47                printHelp = 0,
48                checkArgs = 0,
49
50                illegalArgumentValue = function(argumentName, position){
51                        messages.log("inputIllegalCommandlineArg", ["switch", argumentName, "position", position]);
52                },
53
54                evalScriptArg=
55                        function(arg){
56                                if(arg=="true"){
57                                        return true;
58                                }else if(arg=="false"){
59                                        return false;
60                                }else if(arg=="null"){
61                                        return null;
62                                }else if(isNaN(arg)){
63                                        return arg+"";
64                                }else{
65                                        return Number(arg);
66                                }
67                        },
68
69                readProfile = function(
70                        scriptType,
71                        filename
72                ){
73                        ///
74                        // Load, evaluate and return the result of the contents of the file given by
75                        // filename in a scope type given by scriptType as follows:
76                        //
77                        // When scriptType is "require", contents of filename should be either an application of require to a configuration object or
78                        // a bare require object. The contents is evaluated as follows:
79                        // `code
80                        // (function(){
81                        //       var __result = 0, require = function(config){__result=config;};
82                        //       <contents>
83                        //       return __result || require;
84                        // })();
85                        //
86                        // Notice if <contents> defines require as a bare object, it will overwrite the provided require, result will be 0, and the bare
87                        // object will be returned; otherwise, if <contents> contains an application to a configuration object, result will be truthy
88                        // and therefore be returned.
89                        //
90                        // When scriptType is "dojoConfig", contents of filename should include the variable "dojoConfig" which should hold a
91                        // a dojo loader configuration:
92                        // `code
93                        // (function(){
94                        //       <contents>
95                        //       return dojoConfig;
96                        // })();
97                        //
98                        // When scriptType is "profile", contents of filename should be Javascript code that either defines the variable "dependencies"
99                        // or the variable "profile". The name dependencies is deprecated; if both names exist, then the value of dependencies is ignored.
100                        // The value should be a Javascript object that contains profile properties necessary to effect the desired output. If filename does not
101                        // contain a ".js" suffix, then it is assumed to be a profile in the /util/buildscripts/profile directory
102                        // `code
103                        // (function(selfPath, profile, dependencies){
104                        //       return profile || dependencies;
105                        // })(<filename>, 0, 0);
106                        //
107                        // For script types "require" and "dojoConfig", if filename gives the filetype ".html" or ".htm", then the file is assumed to be
108                        // an html file that contains a <script> element that contains Javascript source code as described above.
109                        //
110                        // If a profile is processed and it contains the property prefixes or the property layers with a layer further containing the property
111                        // dependencies, then it is assumed to be a pre-version 1.7 build profile and the following additional processing is accomplished:
112                        //
113                        // If result contains the property basePath and/or build.basePath that is a relative path, then these are normalized
114                        // with respect to the path given by filename.
115
116                        // remember the the directory of the last build script processed; this is the default location of basePath
117                        var path = getFilepath(filename);
118
119                        if(!fileExists(filename)){
120                                messages.log("inputFileDoesNotExist", [scriptType, filename]);
121                                return 0;
122                        }
123
124                        try{
125                                var src = fs.readFileSync(filename, "utf8");
126                        }catch (e){
127                                messages.log("inputFailedReadfile", [scriptType, filename, "error", e]);
128                                return 0;
129                        }
130
131                        if(scriptType=="profileFile"){
132                                messages.log("inputProfileFileDeprecated");
133                                scriptType = "profile";
134                        }
135
136                        var fixupBasePath = function(profile){
137                                        // relative basePath is relative to the directory in which the profile resides
138                                        // all other relative paths are relative to basePath or releaseDir, and releaseDir, if relative, is relative to basePath
139                                        var fixupBasePath = function(path, referencePath){
140                                                if(path){
141                                                        path = computePath(path, referencePath);
142                                                }else if(typeof path == "undefined"){
143                                                        path = referencePath;
144                                                }
145                                                return path;
146                                        };
147                                        profile.basePath = fixupBasePath(profile.basePath, path);
148                                        if(profile.build && profile.build.basePath){
149                                                profile.build.basePath = fixupBasePath(profile.build.basePath, path);
150                                        }
151                                },
152                                f, profile;
153                        try{
154                                if(scriptType=="require"){
155                                        f = new Function("var __result, require= function(config){__result=config;};" + src + "; return __result || require;");
156                                        profile = f();
157                                        fixupBasePath(profile);
158                                }else if(scriptType=="dojoConfig"){
159                                        f = new Function(src + "; return dojoConfig;");
160                                        profile = f();
161                                        fixupBasePath(profile);
162                                }else if(scriptType=="profile"){
163                                        f = new Function("selfPath", "logger", "profile", "dependencies",
164                                                                         src + "; return {profile:profile, dependencies:dependencies}");
165                                        profile = f(path, messages, 0, 0);
166                                        if(profile.profile){
167                                                profile = profile.profile;
168                                                fixupBasePath(profile);
169                                        }else{
170                                                profile = v1xProfiles.processProfile(profile.dependencies, dojoPath, utilBuildscriptsPath, path);
171                                                // notice we do *not* fixup the basePath for legacy profiles since they have no concept of basePath
172                                        }
173                                }
174                                profile.selfFilename = filename;
175                                messages.log("pacify", "processing " + scriptType + " resource " + filename);
176                                return profile;
177                        }catch(e){
178                                messages.log("inputFailedToEvalProfile", [scriptType, filename, "error", e]);
179                                return 0;
180                        }
181                },
182
183                processHtmlDir = function(arg){
184                        if(!fileUtils.dirExists(arg)){
185                                messages.log("inputHTMLDirDoesNotExist", ["directory", arg]);
186                                return 0;
187                        }else{
188                                var htmlFiles = [];
189                                fs.readdirSync(arg).forEach(function(filename){
190                                        if(/\.html$/.test(filename)){
191                                                htmlFiles.push(arg + "/" + filename);
192                                        }
193                                });
194                                if(!htmlFiles.length){
195                                        messages.log("inputHTMLDirNoFiles", ["directory", arg]);
196                                        return 0;
197                                }else{
198                                        return v1xProfiles.processHtmlFiles(htmlFiles, dojoPath, utilBuildscriptsPath);
199                                }
200                        }
201                },
202
203                processHtmlFiles = function(arg){
204                        var htmlFiles = arg.split(",").filter(function(filename){
205                                if(!fileUtils.fileExists(filename)){
206                                        messages.log("inputHTMLFileDoesNotExist", ["filename", filename]);
207                                        return 0;
208                                }else{
209                                        return 1;
210                                }
211                        });
212                        if(htmlFiles.length){
213                                return v1xProfiles.processHtmlFiles(htmlFiles, dojoPath, utilBuildscriptsPath);
214                        }else{
215                                return 0;
216                        }
217                },
218
219                readPackageJson = function(filename, missingMessageId){
220                        if(!fileUtils.fileExists(filename)){
221                                messages.log(missingMessageId, ["filename", filename]);
222                        }else{
223                                try{
224                                        var result = json.parse(fs.readFileSync(filename, "utf8"));
225                                        result.selfFilename = filename;
226                                        return result;
227                                }catch(e){
228                                        messages.log("inputMalformedPackageJson", ["filename", filename]);
229                                }
230                        }
231                        return 0;
232                },
233
234                processPackageJson = function(packageRoot){
235                        // process all the packages given by package.json first since specific profiles are intended to override the defaults
236                        // packageRoot gives a path to a location where a package.json rides
237                        var packageJsonFilename = catPath(packageRoot, "package.json"),
238                                packageJson= readPackageJson(packageJsonFilename, "inputMissingPackageJson");
239                        if(packageJson){
240                                // use package.json to define a package config
241                                packageJson.selfFilename = packageJsonFilename;
242                                result.profiles.push({
243                                        packages:[{
244                                                name:packageJson.progName || packageJson.name,
245                                                packageJson:packageJson
246                                        }]
247                                });
248                        }
249                },
250
251                readCopyrightOrBuildNotice = function(filename, hint){
252                        if(!fileExists(filename)){
253                                messages.log("inputFileDoesNotExist", [hint, filename]);
254                        }
255                        try{
256                                var prop = hint=="copyrightFile" ? "copyright" : "buildNotice";
257                                result[prop] = fs.readFileSync(filename, "utf8");
258                        }catch (e){
259                                messages.log("inputFailedReadfile", [hint, filename, "error", e]);
260                        }
261                },
262
263                normalizeSwitch = {
264                        "-p":"profile",
265                        "--profile":"profile",
266                        "--profileFile":"profileFile",
267                        "p":"profile",
268                        "profile":"profile",
269                        "profileFile":"profileFile",
270
271                        "--package":"package",
272                        "package":"package",
273
274                        "--require":"require",
275                        "require":"require",
276
277                        "--dojoConfig":"dojoConfig",
278                        "dojoConfig":"dojoConfig",
279
280                        "--htmlDir":"htmlDir",
281                        "htmlDir":"htmlDir",
282
283                        "--htmlFiles":"htmlFiles",
284                        "htmlFiles":"htmlFiles",
285
286                        "--copyrightFile":"copyrightFile",
287                        "copyrightFile":"copyrightFile",
288
289                        "--buildNoticeFile":"buildNoticeFile",
290                        "buildNoticeFile":"buildNoticeFile"
291                };
292
293        //arg[0] is "load=build"; therefore, start with argv[1]
294        for (var arg, processVector = [], i = 1, end = argv.length; i<end;){
295                arg = argv[i++];
296                switch (arg){
297                        case "-p":
298                        case "--profile":
299                                if(i<end){
300                                        processVector.push([normalizeSwitch[arg], argv[i++], cwd]);
301                                }else{
302                                        illegalArgumentValue(arg, i);
303                                }
304                                break;
305
306                        case "--profileFile":
307                        case "--require":
308                        case "--dojoConfig":
309                        case "--htmlDir":
310                        case "--htmlFiles":
311                        case "--copyrightFile":
312                        case "--buildNoticeFile":
313                                if(i<end){
314                                        processVector.push([normalizeSwitch[arg], getAbsolutePath(argv[i++], cwd)]);
315                                }else{
316                                        illegalArgumentValue(arg, i);
317                                }
318                                break;
319
320                        case "--package":
321                                if(i<end){
322                                        argv[i++].split(",").forEach(function(path){
323                                                processVector.push(["package", getAbsolutePath(path, cwd)]);
324                                        });
325                                }else{
326                                        illegalArgumentValue(arg, i);
327                                }
328                                break;
329
330                        case "--writeProfile":
331                                if(i<end){
332                                        result.writeProfile = getAbsolutePath(argv[i++], cwd);
333                                }else{
334                                        illegalArgumentValue(arg, i);
335                                }
336                                break;
337
338                        case "--check":
339                                // read, process, and send the profile to the console and then exit
340                                result.check = true;
341                                break;
342
343                        case "--check-args":
344                                // read and process the command line args, send the profile to the console and then exit
345                                checkArgs = true;
346                                break;
347
348                        case "--check-discovery":
349                                // echo discovery and exit
350                                result.checkDiscovery = true;
351                                result.release = true;
352                                break;
353
354                        case "--debug-check":
355                                // read, process, and send the profile to the console, including gory details, and then exit
356                                result.debugCheck = true;
357                                break;
358
359                        case "--clean":
360                                // deprecated; warning given when the profile is processed
361                                result.clean = true;
362                                break;
363
364                        case "-r":
365                        case "--release":
366                                // do a build
367                                result.release = true;
368                                break;
369
370                        case "--help":
371                                // print help message
372                                printHelp = true;
373                                break;
374
375                        case "-v":
376                                // print the version
377                                printVersion = function(){
378                                        messages.log("pacify", version+"");
379                                };
380                                break;
381
382                        case "--unit-test":
383                                // special hook for testing
384                                if(i<end){
385                                        result.unitTest = argv[i++];
386                                }else{
387                                        illegalArgumentValue("unit-test", i);
388                                }
389                                break;
390
391                        case "--unit-test-param":
392                                // special hook for testing
393                                if(i<end){
394                                        result.unitTestParam = result.unitTestParam || [];
395                                        result.unitTestParam.push(evalScriptArg(argv[i++]));
396                                }else{
397                                        illegalArgumentValue("unit-test", i);
398                                }
399                                break;
400
401                        default:
402                                // possible formats
403                                //
404                                //       -switch value
405                                //       --switch value
406                                //       switch=value
407
408                                var match = arg.match(/^\-\-?(.+)/);
409                                if(match && i<end){
410                                        // all of the switches that take no values are listed above; therefore,
411                                        // *must* provide a value
412                                        if(i<=end){
413                                                result[match[1]] = evalScriptArg(argv[i++]);
414                                        }else{
415                                                illegalArgumentValue(arg, i);
416                                        }
417                                }else{
418                                        // the form switch=value does *not* provide an individual value arg (it's all one string)
419                                        var parts = arg.split("=");
420                                        if(parts.length==2){
421                                                switch(parts[0]){
422                                                        case "p":
423                                                        case "profile":
424                                                                processVector.push([normalizeSwitch[parts[0]], parts[1]]);
425                                                                break;
426
427                                                        case "package":
428                                                                parts[1].split(",").forEach(function(path){
429                                                                        processVector.push(["package", getAbsolutePath(path, cwd)]);
430                                                                });
431                                                                break;
432
433                                                        case "profileFile":
434                                                        case "require":
435                                                        case "dojoConfig":
436                                                        case "htmlDir":
437                                                        case "htmlFiles":
438                                                        case "copyrightFile":
439                                                        case "buildNoticeFile":
440                                                                processVector.push([normalizeSwitch[parts[0]], getAbsolutePath(parts[1], cwd)]);
441                                                                break;
442                                                        default:
443                                                                result[parts[0]] = evalScriptArg(parts[1]);
444                                                }
445                                        }else{
446                                                illegalArgumentValue(arg, i);
447                                        }
448                                }
449                }
450        }
451
452        // if processing html files and a nonexisting profile file is given, then assume the user intends to write
453        // the computed profile to that file. This feature is deprecated; use the switch --writeProfile
454        var processingHtmlFiles = processVector.some(function(item){ return item[0]=="htmlFiles" || item[0]=="htmlDir"; });
455        if(processingHtmlFiles){
456                for(i= 0; i<processVector.length; i++){
457                        if(processVector[i][0]=="profileFile" && !fileExists(processVector[i][1])){
458                                messages.log("outputToProfileFileDeprecated");
459                                result.writeProfile = processVector[i][1];
460                                processVector.splice(i, 1);
461                                break;
462                        }
463                }
464        }
465        processVector.forEach(function(item){
466                // item[0] is the switch
467                // item[1] is an absolute filename
468                var profile;
469                switch(item[0]){
470                        case "profile":
471                                var type = getFiletype(item[1], true), filename;
472                                if(type==""){
473                                        // prefer a user profile, then the stock profiles
474                                        filename = getAbsolutePath(item[1] + ".profile.js", cwd);
475                                        if(!fileExists(filename) && !/\//.test(item[1])){
476                                                // the name given include no path; maybe it's a stock profile
477                                                filename = catPath(utilBuildscriptsPath, "profiles/" + item[1] + ".profile.js");
478                                        }
479                                        if(!fileExists(filename)){
480                                                messages.log("inputFileDoesNotExist", ["filename", filename]);
481                                                break;
482                                        }
483                                }else if(/^(html|htm)$/.test(type)){
484                                        messages.log("inputProcessingHtmlFileNotImplemented", ["profile", filename]);
485                                        return;
486                                }else{
487                                        filename = getAbsolutePath(item[1], cwd);
488                                }
489                                profile = readProfile(item[0], filename);
490                                break;
491                        case "htmlDir":
492                                profile = processHtmlDir(item[1]);
493                                break;
494                        case "htmlFiles":
495                                profile = processHtmlFiles(item[1]);
496                                break;
497                        case "package":
498                                profile = processPackageJson(item[1]);
499                                break;
500                        case "copyrightFile":
501                        case "buildNoticeFile":
502                                profile = readCopyrightOrBuildNotice(item[1], item[0]);
503                                break;
504                        default:
505                                profile = readProfile(item[0], item[1]);
506                }
507                if(profile){
508                        result.profiles.push(profile);
509                }
510        });
511
512
513
514        if(((printHelp || printVersion) && argv.length==2) || (printHelp && printVersion && argv.length==3)){
515                //just asked for either help or version or both; don't do more work or reporting
516                if(printHelp){
517                        messages.log("pacify", help);
518                        messages.log("pacify", version+"");
519                        has("host-rhino") && messages.log("pacify", "running under rhino");
520                        has("host-node") && messages.log("pacify", "running under node");
521                }
522                printVersion && printVersion();
523                process.exit(0);
524                return 0;
525        }
526
527        printVersion && printVersion();
528
529        if (checkArgs){
530                messages.log("pacify", stringify(result));
531                process.exit(0);
532                return 0;
533        }
534
535        if(messages.getErrorCount()){
536                messages.log("pacify", "errors on command line; terminating application.");
537                process.exit(-1);
538                return 0;
539        }
540
541        if(!result.profiles.length){
542                messages.log("pacify", "no profile provided; use the option --help for help");
543                process.exit(-1);
544                return 0;
545        }
546
547        if(result.unitTest=="argv"){
548                var testId = result.unitTestParam[0],
549                        writingExpected = testId<0;
550                if(writingExpected){
551                        testId = -testId;
552                }
553                result.unitTestParam = testId;
554                var expectedFilename = compactPath(utilBuildscriptsPath + "/../build/tests/argvTestsExpected.js"),
555                        expected = json.parse(fs.readFileSync(expectedFilename, "utf8")),
556                        pathNormalize = utilBuildscriptsPath.match(/(.*)\/util\/buildscripts/)[1],
557                        testResult = stringify(result).replace(RegExp(pathNormalize, "g"), "~"),
558                        passed = 1;
559
560                if(writingExpected){
561                        // write out the expected result;
562                        console.log("result:");
563                        debug(testResult);
564                        expected[result.unitTestParam] = testResult;
565                        fs.writeFileSync(expectedFilename, json.stringify(expected), "utf8");
566                }else{
567                        passed = testResult==expected[result.unitTestParam];
568                        console.log(result.unitTestParam + ":" + (passed ? "PASSED" : "FAILED"));
569                        if(!passed){
570                                console.log("Expected:");
571                                console.log(expected[result.unitTestParam]);
572                                console.log("But Got:");
573                                console.log(testResult);
574                        }
575                }
576                process.exit(passed ? 0 : -1);
577        }
578
579        return {
580                args:result,
581                readPackageJson:readPackageJson,
582                readProfile:readProfile
583        };
584});
585
586
Note: See TracBrowser for help on using the repository browser.