source: Dev/branches/rest-dojo-ui/client/util/build/argv.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 17.6 KB
Line 
1define([
2        "require",
3        "dojo",
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, dojo, 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 dojo.fromJson("{\"result\":\"" + arg + "\"}").result;
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                                src = fs.readFileSync(filename, "utf8");
155                                if(scriptType=="require"){
156                                        f = new Function("var __result, require= function(config){__result=config;};" + src + "; return __result || require;");
157                                        profile = f();
158                                        fixupBasePath(profile);
159                                }else if(scriptType=="dojoConfig"){
160                                        f = new Function(src + "; return dojoConfig;");
161                                        profile = f();
162                                        fixupBasePath(profile);
163                                }else if(scriptType=="profile"){
164                                        f = new Function("selfPath", "logger", "profile", "dependencies",
165                                                                         src + "; return {profile:profile, dependencies:dependencies}");
166                                        profile = f(path, messages, 0, 0, 0);
167                                        if(profile.profile){
168                                                profile = profile.profile;
169                                                fixupBasePath(profile);
170                                        }else{
171                                                profile = v1xProfiles.processProfile(profile.dependencies, dojoPath, utilBuildscriptsPath, path);
172                                                // notice we do *not* fixup the basePath for legacy profiles since they have no concept of basePath
173                                        }
174                                }
175                                profile.selfFilename = filename;
176                                messages.log("pacify", "processing " + scriptType + " resource " + filename);
177                                return profile;
178                        }catch(e){
179                                messages.log("inputFailedToEvalProfile", [scriptType, filename, "error", e]);
180                                return 0;
181                        }
182                },
183
184                processHtmlDir = function(arg){
185                        if(!fileUtils.dirExists(arg)){
186                                messages.log("inputHTMLDirDoesNotExist", ["directory", arg]);
187                                return 0;
188                        }else{
189                                var htmlFiles = [];
190                                fs.readdirSync(arg).forEach(function(filename){
191                                        if(/\.html$/.test(filename)){
192                                                htmlFiles.push(arg + "/" + filename);
193                                        }
194                                });
195                                if(!htmlFiles.length){
196                                        messages.log("inputHTMLDirNoFiles", ["directory", arg]);
197                                        return 0;
198                                }else{
199                                        return v1xProfiles.processHtmlFiles(htmlFiles, dojoPath, utilBuildscriptsPath);
200                                }
201                        }
202                },
203
204                processHtmlFiles = function(arg){
205                        var htmlFiles = arg.split(",").filter(function(filename){
206                                if(!fileUtils.fileExists(filename)){
207                                        messages.log("inputHTMLFileDoesNotExist", ["filename", filename]);
208                                        return 0;
209                                }else{
210                                        return 1;
211                                }
212                        });
213                        if(htmlFiles.length){
214                                return v1xProfiles.processHtmlFiles(htmlFiles, dojoPath, utilBuildscriptsPath);
215                        }else{
216                                return 0;
217                        }
218                },
219
220                readPackageJson = function(filename, missingMessageId){
221                        if(!fileUtils.fileExists(filename)){
222                                messages.log(missingMessageId, ["filename", filename]);
223                        }else{
224                                try{
225                                        var result = dojo.fromJson(fs.readFileSync(filename, "utf8"));
226                                        result.selfFilename = filename;
227                                        return result;
228                                }catch(e){
229                                        messages.log("inputMalformedPackageJson", ["filename", filename]);
230                                }
231                        }
232                        return 0;
233                },
234
235                processPackageJson = function(packageRoot){
236                        // process all the packages given by package.json first since specific profiles are intended to override the defaults
237                        // packageRoot gives a path to a location where a package.json rides
238                        var packageJsonFilename = catPath(packageRoot, "package.json"),
239                                packageJson= readPackageJson(packageJsonFilename, "inputMissingPackageJson");
240                        if(packageJson){
241                                // use package.json to define a package config
242                                packageJson.selfFilename = packageJsonFilename;
243                                result.profiles.push({
244                                        packages:[{
245                                                name:packageJson.progName || packageJson.name,
246                                                packageJson:packageJson
247                                        }]
248                                });
249                        }
250                },
251
252                readCopyrightOrBuildNotice = function(filename, hint){
253                        if(!fileExists(filename)){
254                                messages.log("inputFileDoesNotExist", [hint, filename]);
255                        }
256                        try{
257                                var prop = hint=="copyrightFile" ? "copyright" : "buildNotice";
258                                result[prop] = fs.readFileSync(filename, "utf8");
259                        }catch (e){
260                                messages.log("inputFailedReadfile", [hint, filename, "error", e]);
261                        }
262                },
263
264                normalizeSwitch = {
265                        "-p":"profile",
266                        "--profile":"profile",
267                        "--profileFile":"profileFile",
268                        "p":"profile",
269                        "profile":"profile",
270                        "profileFile":"profileFile",
271
272                        "--package":"package",
273                        "package":"package",
274
275                        "--require":"require",
276                        "require":"require",
277
278                        "--dojoConfig":"dojoConfig",
279                        "dojoConfig":"dojoConfig",
280
281                        "--htmlDir":"htmlDir",
282                        "htmlDir":"htmlDir",
283
284                        "--htmlFiles":"htmlFiles",
285                        "htmlFiles":"htmlFiles",
286
287                        "--copyrightFile":"copyrightFile",
288                        "copyrightFile":"copyrightFile",
289
290                        "--buildNoticeFile":"buildNoticeFile",
291                        "buildNoticeFile":"buildNoticeFile"
292                };
293
294        //arg[0] is "load=build"; therefore, start with argv[1]
295        for (var arg, processVector = [], i = 1, end = argv.length; i<end;){
296                arg = argv[i++];
297                switch (arg){
298                        case "-p":
299                        case "--profile":
300                                if(i<end){
301                                        processVector.push([normalizeSwitch[arg], argv[i++], cwd]);
302                                }else{
303                                        illegalArgumentValue(arg, i);
304                                }
305                                break;
306
307                        case "--profileFile":
308                        case "--require":
309                        case "--dojoConfig":
310                        case "--htmlDir":
311                        case "--htmlFiles":
312                        case "--copyrightFile":
313                        case "--buildNoticeFile":
314                                if(i<end){
315                                        processVector.push([normalizeSwitch[arg], getAbsolutePath(argv[i++], cwd)]);
316                                }else{
317                                        illegalArgumentValue(arg, i);
318                                }
319                                break;
320
321                        case "--package":
322                                if(i<end){
323                                        argv[i++].split(",").forEach(function(path){
324                                                processVector.push(["package", getAbsolutePath(path, cwd)]);
325                                        });
326                                }else{
327                                        illegalArgumentValue(arg, i);
328                                }
329                                break;
330
331                        case "--writeProfile":
332                                if(i<end){
333                                        result.writeProfile = getAbsolutePath(argv[i++], cwd);
334                                }else{
335                                        illegalArgumentValue(arg, i);
336                                }
337                                break;
338
339                        case "--check":
340                                // read, process, and send the profile to the console and then exit
341                                result.check = true;
342                                break;
343
344                        case "--check-args":
345                                // read and process the command line args, send the profile to the console and then exit
346                                checkArgs = true;
347                                break;
348
349                        case "--check-discovery":
350                                // echo discovery and exit
351                                result.checkDiscovery = true;
352                                result.release = true;
353                                break;
354
355                        case "--debug-check":
356                                // read, process, and send the profile to the console, including gory details, and then exit
357                                result.debugCheck = true;
358                                break;
359
360                        case "--clean":
361                                // deprecated; warning given when the profile is processed
362                                result.clean = true;
363                                break;
364
365                        case "-r":
366                        case "--release":
367                                // do a build
368                                result.release = true;
369                                break;
370
371                        case "--help":
372                                // print help message
373                                printHelp = true;
374                                break;
375
376                        case "-v":
377                                // print the version
378                                printVersion = function(){
379                                        messages.log("pacify", version+"");
380                                };
381                                break;
382
383                        case "--unit-test":
384                                // special hook for testing
385                                if(i<end){
386                                        result.unitTest = argv[i++];
387                                }else{
388                                        illegalArgumentValue("unit-test", i);
389                                }
390                                break;
391
392                        case "--unit-test-param":
393                                // special hook for testing
394                                if(i<end){
395                                        result.unitTestParam = result.unitTestParam || [];
396                                        result.unitTestParam.push(evalScriptArg(argv[i++]));
397                                }else{
398                                        illegalArgumentValue("unit-test", i);
399                                }
400                                break;
401
402                        default:
403                                // possible formats
404                                //
405                                //   -switch value
406                                //   --switch value
407                                //   switch=value
408
409                                var match = arg.match(/^\-\-?(.+)/);
410                                if(match && i<end){
411                                        // all of the switches that take no values are listed above; therefore,
412                                        // *must* provide a value
413                                        result[match[1]] = evalScriptArg(argv[i++]);
414                                }else{
415                                        // the form switch=value does *not* provide an individual value arg (it's all one string)
416                                        var parts = arg.split("=");
417                                        if(parts.length==2){
418                                                switch(parts[0]){
419                                                        case "p":
420                                                        case "profile":
421                                                                processVector.push([normalizeSwitch[parts[0]], parts[1]]);
422                                                                break;
423
424                                                        case "package":
425                                                                parts[1].split(",").forEach(function(path){
426                                                                        processVector.push(["package", getAbsolutePath(path, cwd)]);
427                                                                });
428                                                                break;
429
430                                                        case "profileFile":
431                                                        case "require":
432                                                        case "dojoConfig":
433                                                        case "htmlDir":
434                                                        case "htmlFiles":
435                                                        case "copyrightFile":
436                                                        case "buildNoticeFile":
437                                                                processVector.push([normalizeSwitch[parts[0]], getAbsolutePath(parts[1], cwd)]);
438                                                                break;
439                                                        default:
440                                                                result[parts[0]] = evalScriptArg(parts[1]);
441                                                }
442                                        }else{
443                                                illegalArgumentValue(arg, i);
444                                        }
445                                }
446                }
447        }
448
449        // if processing html files and a nonexisting profile file is given, then assume the user intends to write
450        // the computed profile to that file. This feature is deprecated; use the switch --writeProfile
451        var processingHtmlFiles = processVector.some(function(item){ return item[0]=="htmlFiles" || item[0]=="htmlDir"; });
452        if(processingHtmlFiles){
453                for(i= 0; i<processVector.length; i++){
454                        if(processVector[i][0]=="profileFile" && !fileExists(processVector[i][1])){
455                                messages.log("outputToProfileFileDeprecated");
456                                result.writeProfile = processVector[i][1];
457                                processVector.splice(i, 1);
458                                break;
459                        }
460                }
461        }
462        processVector.forEach(function(item){
463                // item[0] is the switch
464                // item[1] is an absolute filename
465                var profile;
466                switch(item[0]){
467                        case "profile":
468                                var type = getFiletype(item[1], true), filename;
469                                if(type==""){
470                                        // prefer a user profile, then the stock profiles
471                                        filename = getAbsolutePath(item[1] + ".profile.js", cwd);
472                                        if(!fileExists(filename) && !/\//.test(item[1])){
473                                                // the name given include no path; maybe it's a stock profile
474                                                filename = catPath(utilBuildscriptsPath, "profiles/" + item[1] + ".profile.js");
475                                        }
476                                        if(!fileExists(filename)){
477                                                messages.log("inputFileDoesNotExist", ["filename", filename]);
478                                                break;
479                                        }
480                                }else if(/^(html|htm)$/.test(type)){
481                                        messages.log("inputProcessingHtmlFileNotImplemented", ["profile", filename]);
482                                        return;
483                                }else{
484                                        filename = getAbsolutePath(item[1], cwd);
485                                }
486                                profile = readProfile(item[0], filename);
487                                break;
488                        case "htmlDir":
489                                profile = processHtmlDir(item[1]);
490                                break;
491                        case "htmlFiles":
492                                profile = processHtmlFiles(item[1]);
493                                break;
494                        case "package":
495                                profile = processPackageJson(item[1]);
496                                break;
497                        case "copyrightFile":
498                        case "buildNoticeFile":
499                                profile = readCopyrightOrBuildNotice(item[1], item[0]);
500                                break;
501                        default:
502                                profile = readProfile(item[0], item[1]);
503                }
504                if(profile){
505                        result.profiles.push(profile);
506                }
507        });
508
509
510
511        if(((printHelp || printVersion) && argv.length==2) || (printHelp && printVersion && argv.length==3)){
512                //just asked for either help or version or both; don't do more work or reporting
513                if(printHelp){
514                        messages.log("pacify", help);
515                        messages.log("pacify", version+"");
516                        has("host-rhino") && messages.log("pacify", "running under rhino");
517                        has("host-node") && messages.log("pacify", "running under node");
518                }
519                printVersion && printVersion();
520                process.exit(0);
521                return 0;
522        }
523
524        printVersion && printVersion();
525
526        if (checkArgs){
527                messages.log("pacify", stringify(result));
528                process.exit(0);
529                return 0;
530        }
531
532        if(messages.getErrorCount()){
533                messages.log("pacify", "errors on command line; terminating application.");
534                process.exit(-1);
535                return 0;
536        }
537
538        if(!result.profiles.length){
539                messages.log("pacify", "no profile provided; use the option --help for help");
540                process.exit(-1);
541                return 0;
542        }
543
544        if(result.unitTest=="argv"){
545                var testId = result.unitTestParam[0],
546                        writingExpected = testId<0;
547                if(writingExpected){
548                        testId = -testId;
549                }
550                result.unitTestParam = testId;
551                var expectedFilename = compactPath(utilBuildscriptsPath + "/../build/tests/argvTestsExpected.js"),
552                        expected = dojo.fromJson(fs.readFileSync(expectedFilename, "utf8")),
553                        pathNormalize = utilBuildscriptsPath.match(/(.*)\/util\/buildscripts/)[1],
554                        testResult = stringify(result).replace(RegExp(pathNormalize, "g"), "~"),
555                        passed = 1;
556
557                if(writingExpected){
558                        // write out the expected result;
559                        console.log("result:");
560                        debug(testResult);
561                        expected[result.unitTestParam] = testResult;
562                        fs.writeFileSync(expectedFilename, dojo.toJson(expected), "utf8");
563                }else{
564                        passed = testResult==expected[result.unitTestParam];
565                        console.log(result.unitTestParam + ":" + (passed ? "PASSED" : "FAILED"));
566                        if(!passed){
567                                console.log("Expected:");
568                                console.log(expected[result.unitTestParam]);
569                                console.log("But Got:");
570                                console.log(testResult);
571                        }
572                }
573                process.exit(passed ? 0 : -1);
574        }
575
576        return {
577                args:result,
578                readPackageJson:readPackageJson,
579                readProfile:readProfile
580        };
581});
582
583
Note: See TracBrowser for help on using the repository browser.