├── bin └── jake ├── package.json ├── PORTING-NOTES ├── LICENSE ├── README.md └── lib ├── jake ├── filecreationtask.js ├── clean.js ├── filetask.js ├── taskmanager.js ├── task.js ├── filelist.js └── application.js └── jake.js /bin/jake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env narwhal 2 | 3 | var JAKE = require("jake"); 4 | var SYSTEM = require("system"); 5 | 6 | JAKE.application().run({ args : SYSTEM.args }); 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jake", 3 | "author": "Francisco Tolmasky (http://tolmasky.com/) ", 4 | "contributors": [ 5 | "Tom Robinson (http://tlrobinson.net/) " 6 | ], 7 | "description": "A build system for CommonJS, lifted from Rake", 8 | "keywords": [ 9 | "build", 10 | "jake", 11 | "rake", 12 | "make" 13 | ], 14 | "version": "0.3" 15 | } -------------------------------------------------------------------------------- /PORTING-NOTES: -------------------------------------------------------------------------------- 1 | These Narwhal APIs are used: 2 | 3 | require("file") 4 | SYNC: 5 | FILE.absolute 6 | FILE.basename 7 | FILE.dirname 8 | FILE.join 9 | FILE.fnmatch 10 | FILE.cwd 11 | SYNC/ASYNC: 12 | FILE.mkdirs 13 | FILE.rmtree 14 | FILE.exists 15 | FILE.isDirectory 16 | FILE.glob 17 | FILE.mtime 18 | 19 | require("system") 20 | SYNC: 21 | SYSTEM.args 22 | SYSTEM.env 23 | 24 | require("os") 25 | SYNC: 26 | OS.enquote 27 | OS.exit 28 | SYNC/ASYNC: 29 | OS.system 30 | 31 | require("narwhal/util") 32 | SYNC: 33 | UTIL.copy 34 | 35 | require("printf") 36 | SYNC: 37 | PRINTF.printf 38 | 39 | require("narwhal/args") 40 | SYNC: 41 | ARGS.Parser 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 280 North, Inc. 2 | Copyright (c) 2003, 2004 Jim Weirich 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jake 2 | ==== 3 | 4 | Jake is a build tool similar to Make and Rake, but written in and for JavaScript. It's a port of Ruby's [Rake](http://rake.rubyforge.org/), which is inspired by the classic [Make](http://en.wikipedia.org/wiki/Make_(software\)) tool. 5 | 6 | Currently it runs on the [Narwhal](http://narwhaljs.org/) server-side JavaScript platform, but is intended to support any compliant [CommonJS](http://commonjs.org/) system as it matures. 7 | 8 | API 9 | --- 10 | 11 | The API is very similar to the Rake API, though with JavaScript syntax. 12 | 13 | - `jake.task(name, [dependencies], [action])` 14 | 15 | Declares a task called "name", with an optional array of dependent tasks, and optional function to perform. 16 | 17 | - `jake.file(path, [dependencies], [action])` 18 | 19 | Like `task`, but only runs the action if the target file ("name") doesn't exist or was last modified before at least on dependency. 20 | 21 | - jake.directory(directoryPath) 22 | - jake.filedir(path, [dependencies], action) 23 | 24 | *TODO: better API docs* 25 | 26 | Example "Jakefile" 27 | ------------------ 28 | 29 | var jake = require("jake"); 30 | 31 | // prints "default": 32 | jake.task("default", function(t) { 33 | print(t.name()); 34 | }); 35 | 36 | // runs tasks "bar" and "baz" 37 | jake.task("foo", ["bar", "baz"]); 38 | 39 | // only runs if "bar" is older than "bar.source" or non-existant 40 | jake.file("bar", ["bar.source"], function() { 41 | // stuff to compile "bar.source" to "bar" 42 | }); 43 | 44 | // does nothing 45 | jake.task("baz"); 46 | 47 | Example Usage 48 | ------------- 49 | 50 | # runs "default" task if no task names are given 51 | jake 52 | 53 | # runs "bar", "baz" dependent tasks, then "foo" task 54 | jake foo 55 | 56 | # runs "bar", "baz", "foo", and "default" tasks 57 | jake foo default 58 | -------------------------------------------------------------------------------- /lib/jake/filecreationtask.js: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2009 280 North, Inc. (francisco@280north.com) 3 | // Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to 7 | // deal in the Software without restriction, including without limitation the 8 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | // sell copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | // IN THE SOFTWARE. 22 | // 23 | 24 | var FILE = require("file"); 25 | 26 | var JAKE = require("../jake"); 27 | var FileTask = require("./filetask").FileTask; 28 | 29 | // ######################################################################### 30 | // A FileCreationTask is a file task that when used as a dependency will be 31 | // needed if and only if the file has not been created. Once created, it is 32 | // not re-triggered if any of its dependencies are newer, nor does trigger 33 | // any rebuilds of tasks that depend on it whenever it is updated. 34 | // 35 | var FileCreationTask = function() { 36 | FileTask.apply(this, arguments); 37 | } 38 | 39 | FileCreationTask.__proto__ = FileTask; 40 | FileCreationTask.prototype.__proto__ = FileTask.prototype; 41 | 42 | //print("IT IS " + FileTask.defineTask + " " + FileCreationTask.defineTask); 43 | // Is this file task needed? Yes if it doesn't exist. 44 | FileCreationTask.prototype.isNeeded = function() { 45 | return !FILE.exists(this.name()); 46 | } 47 | 48 | // Time stamp for file creation task. This time stamp is earlier 49 | // than any other time stamp. 50 | FileCreationTask.prototype.timestamp = function() { 51 | return JAKE.EARLY; 52 | } 53 | 54 | // Exports 55 | exports.FileCreationTask = FileCreationTask; 56 | -------------------------------------------------------------------------------- /lib/jake/clean.js: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2009 280 North, Inc. (francisco@280north.com) 3 | // Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to 7 | // deal in the Software without restriction, including without limitation the 8 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | // sell copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | // IN THE SOFTWARE. 22 | // 23 | 24 | 25 | // The 'jake/clean' file defines two file lists (CLEAN and CLOBBER) and 26 | // two jake tasks ("clean" and "clobber"). 27 | // 28 | // ["clean"] Clean up the project by deleting scratch files and backup 29 | // files. Add files to the CLEAN file list to have the :clean 30 | // target handle them. 31 | // 32 | // ["clobber"] Clobber all generated and non-source files in a project. 33 | // The task depends on :clean, so all the clean files will 34 | // be deleted as well as files in the CLOBBER file list. 35 | // The intent of this task is to return a project to its 36 | // pristine, just unpacked state. 37 | 38 | var FILE = require("file"); 39 | var JAKE = require("../jake"); 40 | var FileList = require("./filelist").FileList; 41 | 42 | 43 | var CLEAN = new FileList("**/*~", "**/*.bak", "**/core"); 44 | 45 | CLEAN.clearExclude().exclude(function(aFilename) { 46 | return FILE.basename(aFilename) === "core" && FILE.isDirectory(aFilename); 47 | }); 48 | 49 | // desc "Remove any temporary products." 50 | JAKE.task("clean", function() { 51 | CLEAN.forEach(function(aFilename) { 52 | try { 53 | FILE.rmtree(aFilename); 54 | } catch(anException) { 55 | } 56 | }); 57 | }); 58 | 59 | var CLOBBER = new FileList(); 60 | 61 | // desc "Remove any generated file." 62 | JAKE.task("clobber", ["clean"], function() { 63 | CLOBBER.forEach(function(aFilename) { 64 | try { 65 | FILE.rmtree(aFilename); 66 | } catch(anException) { 67 | } 68 | }); 69 | }); 70 | 71 | exports.CLEAN = CLEAN; 72 | exports.CLOBBER = CLOBBER; 73 | -------------------------------------------------------------------------------- /lib/jake/filetask.js: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2009 280 North, Inc. (francisco@280north.com) 3 | // Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to 7 | // deal in the Software without restriction, including without limitation the 8 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | // sell copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | // IN THE SOFTWARE. 22 | // 23 | 24 | var FILE = require("file"); 25 | 26 | var JAKE = require("../jake"); 27 | var Task = require("./task").Task; 28 | 29 | // A FileTask is a task that includes time based dependencies. If any of a 30 | // FileTask's prerequisites have a timestamp that is later than the file 31 | // represented by this task, then the file must be rebuilt (using the 32 | // supplied actions). 33 | var FileTask = function() { 34 | Task.apply(this, arguments); 35 | } 36 | 37 | FileTask.__proto__ = Task; 38 | FileTask.prototype.__proto__ = Task.prototype; 39 | 40 | // Is this file task needed? Yes if it doesn't exist, or if its time stamp 41 | // is out of date. 42 | FileTask.prototype.isNeeded = function() { 43 | return !FILE.exists(this.name()) || this.outOfDate(this.timestamp()); 44 | } 45 | 46 | // Time stamp for file task. 47 | FileTask.prototype.timestamp = function() { 48 | if (FILE.exists(this.name())) 49 | return FILE.mtime(this.name()); 50 | 51 | return JAKE.EARLY; 52 | } 53 | 54 | // Are there any prerequisites with a later time than the given time stamp? 55 | FileTask.prototype.outOfDate = function(aTimestamp) { 56 | var application = this.application(); 57 | 58 | return this._prerequisites.some(function(aTaskName) { 59 | return application.lookupTask(aTaskName).timestamp() > aTimestamp; 60 | }, this); 61 | } 62 | 63 | // Apply the scope to the task name according to the rules for this kind 64 | // of task. File based tasks ignore the scope when creating the name. 65 | FileTask.scopeName = function(/*Array*/ aScope, /*String*/ aTaskName) { 66 | return aTaskName; 67 | } 68 | 69 | // EXPORTS 70 | exports.FileTask = FileTask; 71 | -------------------------------------------------------------------------------- /lib/jake.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env narwhal 2 | 3 | // Copyright 2009 280 North, Inc. (francisco@280north.com) 4 | // Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to 8 | // deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | // sell copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | // IN THE SOFTWARE. 23 | // 24 | 25 | var FILE = require("file"); 26 | var SYSTEM = require("system"); 27 | var UTIL = require("narwhal/util"); 28 | var OS = require("os"); 29 | 30 | var Task = require("./jake/task").Task; 31 | var FileTask = require("./jake/filetask").FileTask; 32 | var FileCreationTask = require("./jake/filecreationtask").FileCreationTask; 33 | var TaskManager = require("./jake/taskmanager").TaskManager; 34 | var Application = require("./jake/application").Application; 35 | var FileList = require("./jake/filelist").FileList; 36 | 37 | // Exports 38 | exports.Task = Task; 39 | exports.FileTask = FileTask; 40 | exports.FileCreationTask = FileCreationTask; 41 | exports.TaskManager = TaskManager; 42 | exports.Application = Application; 43 | exports.FileList = FileList; 44 | 45 | var application = null; 46 | 47 | exports.application = function() { 48 | if (!application) 49 | application = new Application(); 50 | 51 | return application; 52 | } 53 | 54 | exports.setApplication = function(/*Application*/ anApplication) { 55 | application = anApplication; 56 | } 57 | 58 | exports.EARLY = new Date(-10000,1,1,0,0,0,0).getTime(); 59 | 60 | exports.task = function() { 61 | return Task.defineTask.apply(Task, arguments); 62 | } 63 | 64 | exports.file = function() { 65 | return FileTask.defineTask.apply(FileTask, arguments); 66 | } 67 | 68 | exports.fileCreate = function() { 69 | return FileCreationTask.defineTask.apply(FileCreationTask, arguments); 70 | } 71 | 72 | exports.directory = function(aDirectory) { 73 | var oldLength = null; 74 | 75 | while (aDirectory !== "." && aDirectory.length !== oldLength) { 76 | exports.fileCreate(aDirectory, function(aTask) { 77 | var taskName = aTask.name(); 78 | 79 | if (!FILE.exists(taskName)) 80 | FILE.mkdirs(taskName); 81 | }); 82 | 83 | oldLength = aDirectory.length; 84 | aDirectory = FILE.dirname(aDirectory); 85 | } 86 | } 87 | 88 | exports.filedir = function() { 89 | var fileTask = FileTask.defineTask.apply(FileTask, arguments); 90 | var fileDirectory = FILE.dirname(fileTask.name()); 91 | 92 | exports.directory (fileDirectory); 93 | exports.file (fileTask.name(), fileDirectory); 94 | } 95 | 96 | /* 97 | # Return the original directory where the Rake application was started. 98 | def original_dir 99 | application.original_dir 100 | end 101 | */ 102 | 103 | // record the initial SYSTEM.env so we know what needs to be serialized later 104 | var envInitial = Object.freeze(UTIL.copy(SYSTEM.env)); 105 | function serializeEnv(/*Object*/ env) { 106 | return Object.keys(env).map(function(key) { 107 | return (env[key] !== envInitial[key]) ? key + "=" + OS.enquote(env[key]) : null; 108 | }).filter(function(x) { return !!x; }).join(" "); 109 | } 110 | 111 | exports.subjake = function(/*Array | String*/ directories, /*String*/ aTaskName, /*Object*/ env) { 112 | if (!Array.isArray(directories)) 113 | directories = [directories]; 114 | 115 | 116 | directories.forEach(function(/*String*/ aDirectory) { 117 | if (FILE.isDirectory(aDirectory)) { 118 | if (OS.system("cd " + OS.enquote(aDirectory) + " && " + serializeEnv(env) + " jake " + OS.enquote(aTaskName))) 119 | OS.exit(1); 120 | } 121 | }); 122 | } 123 | -------------------------------------------------------------------------------- /lib/jake/taskmanager.js: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2009 280 North, Inc. (francisco@280north.com) 3 | // Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to 7 | // deal in the Software without restriction, including without limitation the 8 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | // sell copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | // IN THE SOFTWARE. 22 | // 23 | 24 | var FILE = require("file"); 25 | 26 | var FileTask = require("./filetask").FileTask; 27 | 28 | TaskManager = function() { 29 | this._tasks = { }; 30 | this._rules = []; 31 | this._scope = []; 32 | // @last_description = nil 33 | } 34 | /* 35 | def create_rule(*args, &block) 36 | pattern, arg_names, deps = resolve_args(args) 37 | pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern 38 | @rules << [pattern, deps, block] 39 | end 40 | */ 41 | 42 | TaskManager.prototype.defineTask = function(aTaskClass, aTaskName) { 43 | if (arguments.length < 1) 44 | throw "No class passed to Task.defineTask"; 45 | 46 | if (arguments.length < 2) 47 | throw "No name passed to Task.defineTask"; 48 | 49 | aTaskName = aTaskClass.scopeName(this._scope, aTaskName); 50 | 51 | var task = this.intern(aTaskClass, aTaskName); 52 | var result = this.resolveArguments(Array.prototype.slice.apply(arguments, [2])); 53 | 54 | task.setArgumentNames(result[0]); 55 | //task.add_description(@last_description) 56 | //@last_description = nil 57 | task.enhance(result[1], result[2]); 58 | 59 | return task; 60 | } 61 | 62 | // Lookup a task. Return an existing task if found, otherwise 63 | // create a task of the current type. 64 | TaskManager.prototype.intern = function(/*Function*/ aTaskClass, /*String*/ aTaskName) { 65 | var task = this._tasks[aTaskName]; 66 | 67 | if (!task) { 68 | task = new aTaskClass(aTaskName, this); 69 | this._tasks[aTaskName] = task; 70 | } 71 | 72 | return task; 73 | } 74 | 75 | TaskManager.prototype.lookupTask = function(/*String*/ aTaskName, /*Array*/ scopes) { 76 | var task = this.lookup(aTaskName, scopes) || /* enhance_with_matching_rule(task_name) or ||*/ this.synthesizeFileTask(aTaskName); 77 | 78 | if (!task) 79 | throw "Don't know how to build task '" + aTaskName + "'"; 80 | 81 | return task; 82 | } 83 | 84 | TaskManager.prototype.synthesizeFileTask = function(/*String*/ aTaskName) { 85 | if (!FILE.exists(aTaskName)) 86 | return null; 87 | 88 | return this.defineTask(FileTask, aTaskName); 89 | } 90 | 91 | // Resolve the arguments for a task/rule. Returns a triplet of 92 | // [task_name, arg_name_list, prerequisites]. 93 | // 94 | // The patterns recognized by this argument resolving function are: 95 | // 96 | // task(taskName, action) 97 | // task(taskName, [dependency]) 98 | // task(taskName, [dependency], action) 99 | // task(taskName, [argumentName], [dependency], action) 100 | // 101 | TaskManager.prototype.resolveArguments = function(args) { 102 | var action = null; 103 | 104 | if (args.length && (typeof args[args.length - 1] === "function")) 105 | action = args.pop(); 106 | 107 | var dependencies = []; 108 | 109 | if (args.length) 110 | dependencies = args.pop(); 111 | 112 | var argumentNames = []; 113 | 114 | if (args.length) 115 | argumentNames = args.pop(); 116 | 117 | return [argumentNames, dependencies, action]; 118 | } 119 | 120 | TaskManager.prototype.tasks = function() { 121 | return Object.keys(this._tasks).sort().map(function(name) { 122 | return this._tasks[name]; 123 | }, this); 124 | } 125 | 126 | // List of all the tasks defined in the given scope (and its 127 | // sub-scopes). 128 | TaskManager.prototype.tasksInScope = function(/*Array*/ aScope) { 129 | var prefix = aScope.join(":"); 130 | var regexp = new Regexp("^" + prefix + ":"); 131 | 132 | return this._tasks.filter(function(/*Task*/ aTask) { 133 | return !!aTask.name().match(regexp); 134 | }); 135 | } 136 | 137 | // Clear all tasks in this application. 138 | TaskManager.prototype.clear = function() { 139 | this._tasks = []; 140 | this._rules = []; 141 | } 142 | 143 | // Lookup a task, using scope and the scope hints in the task name. 144 | // This method performs straight lookups without trying to 145 | // synthesize file tasks or rules. Special scope names (e.g. '^') 146 | // are recognized. If no scope argument is supplied, use the 147 | // current scope. Return nil if the task cannot be found. 148 | TaskManager.prototype.lookup = function(/*String*/ aTaskName, /*Array*/ initialScope) { 149 | if (!initialScope) 150 | initialScope = this._scope; 151 | 152 | var scopes = initialScope; 153 | var matches = null; 154 | 155 | if (aTaskName.match(/^jake:/)) { 156 | scopes = []; 157 | aTaskName = aTaskName.replace(/^jake:/, ""); 158 | } 159 | else if (matches = aTaskName.match(/^(\^+)/)) { 160 | scopes = initialScope.slice(0, initialScope.length - matches[1].length); 161 | aTaskName = aTaskName.replace(/^(\^+)/, ""); 162 | } 163 | 164 | return this.lookupInScope(aTaskName, scopes); 165 | } 166 | 167 | // Lookup the task name 168 | TaskManager.prototype.lookupInScope = function(/*String*/ aTaskName, /*Array*/ aScope) { 169 | var count = aScope.length; 170 | 171 | while (count >= 0) { 172 | var task = this._tasks[aScope.slice(0, count).concat([aTaskName]).join(':')]; 173 | 174 | if (task) 175 | return task; 176 | 177 | count--; 178 | } 179 | 180 | return null; 181 | } 182 | 183 | // Return the list of scope names currently active in the task 184 | // manager. 185 | TaskManager.prototype.currentScope = function() { 186 | return this._scope.slice(); 187 | } 188 | /* 189 | # Evaluate the block in a nested namespace named +name+. Create 190 | # an anonymous namespace if +name+ is nil. 191 | def in_namespace(name) 192 | name ||= generate_name 193 | @scope.push(name) 194 | ns = NameSpace.new(self, @scope) 195 | yield(ns) 196 | ns 197 | ensure 198 | @scope.pop 199 | end 200 | 201 | private 202 | 203 | # Generate an anonymous namespace name. 204 | def generate_name 205 | @seed ||= 0 206 | @seed += 1 207 | "_anon_#{@seed}" 208 | end 209 | 210 | def trace_rule(level, message) 211 | puts "#{" "*level}#{message}" if Rake.application.options.trace_rules 212 | end 213 | 214 | # Attempt to create a rule given the list of prerequisites. 215 | def attempt_rule(task_name, extensions, block, level) 216 | sources = make_sources(task_name, extensions) 217 | prereqs = sources.collect { |source| 218 | trace_rule level, "Attempting Rule #{task_name} => #{source}" 219 | if File.exist?(source) || Rake::Task.task_defined?(source) 220 | trace_rule level, "(#{task_name} => #{source} ... EXIST)" 221 | source 222 | elsif parent = enhance_with_matching_rule(source, level+1) 223 | trace_rule level, "(#{task_name} => #{source} ... ENHANCE)" 224 | parent.name 225 | else 226 | trace_rule level, "(#{task_name} => #{source} ... FAIL)" 227 | return nil 228 | end 229 | } 230 | task = FileTask.define_task({task_name => prereqs}, &block) 231 | task.sources = prereqs 232 | task 233 | end 234 | 235 | # Make a list of sources from the list of file name extensions / 236 | # translation procs. 237 | def make_sources(task_name, extensions) 238 | extensions.collect { |ext| 239 | case ext 240 | when /%/ 241 | task_name.pathmap(ext) 242 | when %r{/} 243 | ext 244 | when /^\./ 245 | task_name.ext(ext) 246 | when String 247 | ext 248 | when Proc 249 | if ext.arity == 1 250 | ext.call(task_name) 251 | else 252 | ext.call 253 | end 254 | else 255 | fail "Don't know how to handle rule dependent: #{ext.inspect}" 256 | end 257 | }.flatten 258 | end 259 | 260 | end # TaskManager 261 | */ 262 | 263 | // EXPORTS 264 | exports.TaskManager = TaskManager; 265 | -------------------------------------------------------------------------------- /lib/jake/task.js: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2009 280 North, Inc. (francisco@280north.com) 3 | // Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to 7 | // deal in the Software without restriction, including without limitation the 8 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | // sell copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | // IN THE SOFTWARE. 22 | // 23 | 24 | var SYSTEM = require("system"); 25 | 26 | var JAKE = require("../jake"); 27 | 28 | var Task = function(/*String*/ aName, /*Application*/ anApplication) { 29 | this._name = aName; 30 | this._prerequisites = []; 31 | this._actions = []; 32 | this._alreadyInvoked = false; 33 | this._fullComment = null; 34 | this._comment = null; 35 | //this.lock = Monitor.new 36 | this._application = anApplication; 37 | this._scope = anApplication.currentScope(); 38 | this._argumentNames = []; 39 | } 40 | 41 | Task.prototype.toString = function() { 42 | return "Task (" + this.name() + ")"; 43 | } 44 | 45 | Task.prototype.name = function() { 46 | return this._name; 47 | } 48 | 49 | Task.prototype.fullComment = function() { 50 | return this._fullComment || ""; 51 | } 52 | 53 | Task.prototype.comment = function() { 54 | return this._comment || ""; 55 | } 56 | 57 | Task.prototype.prerequisites = function() { 58 | return this._prerequisites; 59 | } 60 | 61 | Task.prototype.actions = function() { 62 | return this._actions; 63 | } 64 | 65 | Task.prototype.application = function() { 66 | return this._application; 67 | } 68 | 69 | Task.prototype.enhance = function(/*Array|FileList*/ prerequisites, /*Function*/ anAction) { 70 | if (prerequisites) 71 | this._prerequisites = this._prerequisites.concat(prerequisites.toArray ? prerequisites.toArray() : prerequisites); 72 | 73 | if (anAction) 74 | this._actions.push(anAction); 75 | } 76 | 77 | Task.prototype.reenable = function() { 78 | this._alreadyInvoked = false; 79 | } 80 | 81 | Task.prototype.clear = function() { 82 | this.clearPrerequisites(); 83 | this.clearActions(); 84 | } 85 | 86 | Task.prototype.clearPrerequisites = function() { 87 | this._prerequisites = []; 88 | } 89 | 90 | Task.prototype.clearActions = function() { 91 | this._actions = []; 92 | } 93 | 94 | Task.prototype.invoke = function() { 95 | var taskArguments = new TaskArguments(this._argumentNames, Array.prototype.slice.apply(arguments)); 96 | 97 | this.invokeWithCallChain(taskArguments, InvocationChain.EMPTY); 98 | } 99 | 100 | // Same as invoke, but explicitly pass a call chain to detect 101 | // circular dependencies. 102 | Task.prototype.invokeWithCallChain = function(taskArguments, anInvocationChain) { 103 | var newChain = InvocationChain.append(this, anInvocationChain); 104 | // @lock.synchronize do 105 | // if application.options.trace 106 | // puts "** Invoke #{name} #{format_trace_flags}" 107 | // end 108 | 109 | if (this._alreadyInvoked) 110 | return; 111 | 112 | this._alreadyInvoked = true; 113 | 114 | this.invokePrerequisites(taskArguments, newChain); 115 | 116 | if (this.isNeeded()) 117 | this.execute(taskArguments); 118 | } 119 | 120 | // Invoke all the prerequisites of a task. 121 | Task.prototype.invokePrerequisites = function(taskArguments, invocationChain) { 122 | this._prerequisites.forEach(function(/*String*/ aPrerequisiteName) { 123 | var prerequisite = this._application.lookupTask(aPrerequisiteName, this._scope); 124 | 125 | prerequisiteArguments = taskArguments.newScope(prerequisite.argumentNames()); 126 | prerequisite.invokeWithCallChain(prerequisiteArguments, invocationChain); 127 | }, this); 128 | } 129 | 130 | Task.prototype.setArgumentNames = function(argumentNames) { 131 | this._argumentNames = argumentNames; 132 | } 133 | 134 | Task.prototype.argumentNames = function() { 135 | return this._argumentNames; 136 | } 137 | 138 | // Execute the actions associated with this task. 139 | Task.prototype.execute = function(taskArguments) { 140 | taskArguments = taskArguments || EMPTY_TASK_ARGS; 141 | 142 | // if application.options.dryrun 143 | // puts "** Execute (dry run) #{name}" 144 | // return 145 | // end 146 | // if application.options.trace 147 | // puts "** Execute #{name}" 148 | // end 149 | 150 | // application.enhance_with_matching_rule(name) if @actions.empty? 151 | 152 | this._actions.forEach(function(anAction) { 153 | anAction(this); 154 | //anAction(This, args) 155 | }, this); 156 | } 157 | 158 | Task.prototype.isNeeded = function() { 159 | return true; 160 | } 161 | 162 | Task.prototype.timestamp = function() { 163 | if (this._prerequisites.length <= 0) 164 | return new Date().getTime(); 165 | 166 | return Math.max.apply(null, this._prerequisites.map(function(/*String*/ aPrerequisiteName) { 167 | return this._application.lookupTask(aPrerequisiteName, this._scope).timestamp(); 168 | }, this)); 169 | } 170 | 171 | Task.clear = function() { 172 | JAKE.application.clear(); 173 | } 174 | 175 | Task.tasks = function() { 176 | return JAKE.application.tasks(); 177 | } 178 | 179 | Task.taskNamed = function(/*String*/ aTaskName) { 180 | return JAKE.application.taskWithName(aTaskName); 181 | } 182 | 183 | Task.taskWithNameIsDefined = function(/*String*/ aTaskName) { 184 | return !!JAKE.application.lookupTaskWithName(aTaskName); 185 | } 186 | 187 | Task.defineTask = function() { 188 | var args = [this]; 189 | var application = JAKE.application(); 190 | 191 | // Can't simply use concat because we don't want to flatten inner arrays. 192 | Array.prototype.forEach.call(arguments, function(object) { 193 | args.push(object); 194 | }); 195 | 196 | return application.defineTask.apply(application, args); 197 | } 198 | 199 | Task.scopeName = function(/*Array*/ aScope, /*String*/ aTaskName) { 200 | return aScope.concat(aTaskName).join(':'); 201 | } 202 | /* 203 | # Track the last comment made in the Rakefile. 204 | attr_accessor :last_description 205 | alias :last_comment :last_description # Backwards compatibility 206 | */ 207 | 208 | var TaskArguments = function(/*Array*/ names, /*Array*/ values, /*TaskArguments*/ aParent) { 209 | this._names = names.slice(); 210 | this._parent = aParent; 211 | this._hash = { }; 212 | 213 | this._names.forEach(function(/*String*/ aName, /*Number*/ anIndex) { 214 | if (values[anIndex] !== undefined) 215 | this._hash[aName] = values[anIndex]; 216 | }, this); 217 | } 218 | 219 | TaskArguments.prototype.newScope = function(/*Array*/ names) { 220 | var values = names.map(function(/*String*/ aName) { 221 | return this.lookup(aName); 222 | }, this); 223 | 224 | return new TaskArguments(names, values, this); 225 | } 226 | 227 | TaskArguments.prototype.withDefaults = function(/*Object*/ defaults) { 228 | var hash = this._hash; 229 | 230 | for (key in defaults) 231 | if (defaults.hasOwnProperty(key) && !hash.hasOwnProperty(key)) 232 | hash[key] = defaults[key]; 233 | } 234 | 235 | TaskArguments.prototype.forEach = function(/*Function*/ aFunction, /*Object*/ thisObject) { 236 | if (!aFunction) 237 | return; 238 | 239 | var hash = this._hash; 240 | 241 | if (typeof thisObject === "undefined") 242 | thisObject = aFunction; 243 | 244 | for (key in hash) 245 | aFunction.apply(thisObject, [key, hash[key]]); 246 | } 247 | 248 | TaskArguments.prototype.toHash = function() { 249 | return this._hash; 250 | } 251 | 252 | TaskArguments.prototype.toString = function() { 253 | return this._hash; 254 | } 255 | 256 | TaskArguments.prototype.lookup = function(/*String*/ aName) { 257 | var hash = this._hash; 258 | 259 | if (hash.hasOwnProperty(aName)) 260 | return hash[Name]; 261 | 262 | var env = SYSTEM.env; 263 | 264 | if (env.hasOwnProperty(aName)) 265 | return env[aName]; 266 | 267 | var upperCaseName = aName.toUpperCase(); 268 | 269 | if (env.hasOwnProperty(upperCaseName)) 270 | return env[upperCaseName]; 271 | 272 | if (this._parent) 273 | return this._parent.lookup(aName); 274 | 275 | return null; 276 | } 277 | 278 | var EMPTY_TASK_ARGS = new TaskArguments([], []); 279 | 280 | var InvocationChain = function(aValue, /*InvocationChain*/ aTail) { 281 | this._value = aValue; 282 | this._tail = aTail; 283 | } 284 | 285 | InvocationChain.prototype.isMember = function(/*Object*/ anObject) { 286 | return this._value == anObject || this._tail.isMember(anObject); 287 | } 288 | 289 | InvocationChain.prototype.append = function(/*Object*/ anObject) { 290 | if (this.isMember(anObject)) 291 | throw "Circular dependency detected: " + this + " => " + this._value; 292 | 293 | return new InvocationChain(this._value, this); 294 | } 295 | 296 | InvocationChain.prototype.toString = function() { 297 | return this.prefix() + this._value; 298 | } 299 | 300 | InvocationChain.append = function(aValue, /*InvocationChain*/ aChain) { 301 | return aChain.append(aValue); 302 | } 303 | 304 | InvocationChain.prototype.prefix = function() { 305 | return this._tail + " => "; 306 | } 307 | 308 | var EmptyInvocationChain = function() { 309 | } 310 | 311 | EmptyInvocationChain.prototype.isMember = function(/*Object*/ anObject) { 312 | return false; 313 | } 314 | 315 | EmptyInvocationChain.prototype.append = function(/*Object*/ aValue) { 316 | return new InvocationChain(aValue, this); 317 | } 318 | 319 | EmptyInvocationChain.prototype.toString = function() { 320 | return "TOP"; 321 | } 322 | 323 | InvocationChain.EMPTY = new EmptyInvocationChain; 324 | 325 | // EXPORTS 326 | exports.Task = Task; 327 | -------------------------------------------------------------------------------- /lib/jake/filelist.js: -------------------------------------------------------------------------------- 1 | 2 | var FILE = require("file"); 3 | 4 | /* 5 | # ######################################################################### 6 | # A FileList is essentially an array with a few helper methods defined to 7 | # make file manipulation a bit easier. 8 | # 9 | # FileLists are lazy. When given a list of glob patterns for possible files 10 | # to be included in the file list, instead of searching the file structures 11 | # to find the files, a FileList holds the pattern for latter use. 12 | # 13 | # This allows us to define a number of FileList to match any number of 14 | # files, but only search out the actual files when then FileList itself is 15 | # actually used. The key is that the first time an element of the 16 | # FileList/Array is requested, the pending patterns are resolved into a real 17 | # list of file names. 18 | 19 | # == Method Delegation 20 | # 21 | # The lazy evaluation magic of FileLists happens by implementing all the 22 | # array specific methods to call +resolve+ before delegating the heavy 23 | # lifting to an embedded array object (@items). 24 | # 25 | # In addition, there are two kinds of delegation calls. The regular kind 26 | # delegates to the @items array and returns the result directly. Well, 27 | # almost directly. It checks if the returned value is the @items object 28 | # itself, and if so will return the FileList object instead. 29 | # 30 | # The second kind of delegation call is used in methods that normally 31 | # return a new Array object. We want to capture the return value of these 32 | # methods and wrap them in a new FileList object. We enumerate these 33 | # methods in the +SPECIAL_RETURN+ list below. 34 | 35 | # List of array methods (that are not in +Object+) that need to be 36 | # delegated. 37 | ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s } 38 | 39 | # List of additional methods that must be delegated. 40 | MUST_DEFINE = %w[to_a inspect] 41 | 42 | # List of methods that should not be delegated here (we define special 43 | # versions of them explicitly below). 44 | MUST_NOT_DEFINE = %w[to_a to_ary partition *] 45 | 46 | # List of delegated methods that return new array values which need 47 | # wrapping. 48 | SPECIAL_RETURN = %w[ 49 | map collect sort sort_by select find_all reject grep 50 | compact flatten uniq values_at 51 | + - & | 52 | ] 53 | 54 | DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq 55 | 56 | # Now do the delegation. 57 | DELEGATING_METHODS.each_with_index do |sym, i| 58 | if SPECIAL_RETURN.include?(sym) 59 | ln = __LINE__+1 60 | class_eval %{ 61 | def #{sym}(*args, &block) 62 | resolve 63 | result = @items.send(:#{sym}, *args, &block) 64 | FileList.new.import(result) 65 | end 66 | }, __FILE__, ln 67 | else 68 | ln = __LINE__+1 69 | class_eval %{ 70 | def #{sym}(*args, &block) 71 | resolve 72 | result = @items.send(:#{sym}, *args, &block) 73 | result.object_id == @items.object_id ? self : result 74 | end 75 | }, __FILE__, ln 76 | end 77 | end 78 | */ 79 | 80 | var DEFAULT_IGNORE_PATTERNS = [ 81 | /(^|[\/\\])CVS([\/\\]|$)/, 82 | /(^|[\/\\])\.svn([\/\\]|$)/, 83 | /\.bak$/, 84 | /~$/ 85 | ], 86 | DEFAULT_IGNORE_PROCS = [ 87 | function(fn) { return /(^|[\/\\])core$/.test(fn) && !FILE.isDirectory(fn) } 88 | ]; 89 | 90 | // Create a file list from the globbable patterns given. If you wish to 91 | // perform multiple includes or excludes at object build time, use the 92 | // "yield self" pattern. 93 | // 94 | // Example: 95 | // file_list = FileList.new('lib/**/*.rb', 'test/test*.rb') 96 | // 97 | // pkg_files = FileList.new('lib/**/*') do |fl| 98 | // fl.exclude(/\bCVS\b/) 99 | // end 100 | // 101 | function FileList(/*Strings, vararg*/) { 102 | this._pendingAdd = []; 103 | this._pending = false; 104 | this._excludedPatterns = DEFAULT_IGNORE_PATTERNS.slice(); 105 | this._excludedProcs = DEFAULT_IGNORE_PROCS.slice(); 106 | this._items = []; 107 | 108 | Array.prototype.forEach.call(arguments, function(anArgument) { 109 | this.include(anArgument); 110 | }, this); 111 | 112 | this.__defineGetter__("length", FileList.prototype.size); 113 | } 114 | 115 | ["forEach", "filter", "map", "select", 116 | "sort", "uniq", "push", "pop", "shift", 117 | "unshift"].forEach(function(string) { 118 | FileList.prototype[string] = function() { 119 | return (Array.prototype[string]).apply(this.items(), arguments); 120 | }; 121 | }); 122 | 123 | FileList.prototype.items = function() { 124 | this.resolve(); 125 | return this._items; 126 | } 127 | 128 | FileList.prototype.size = function() { 129 | return this.items().length; 130 | } 131 | 132 | // Add file names defined by glob patterns to the file list. If an array 133 | // is given, add each element of the array. 134 | // 135 | // Example: 136 | // file_list.include("*.java", "*.cfg") 137 | // file_list.include %w( math.c lib.h *.o ) 138 | // 139 | FileList.prototype.include = function(/*Strings | Arrays*/) { 140 | // TODO: check for pending 141 | Array.prototype.forEach.apply(arguments, [function(/*String|Array|FileList*/ anObject) { 142 | if (Array.isArray(anObject)) 143 | this.include.apply(this, anObject); 144 | 145 | else if (typeof anObject.toArray === "function") 146 | this.include.apply(this, anObject.toArray()); 147 | 148 | else 149 | this._pendingAdd.push(anObject); 150 | }, this]); 151 | 152 | this._pending = true; 153 | 154 | return this; 155 | } 156 | 157 | FileList.prototype.add = FileList.prototype.include; 158 | 159 | // Register a list of file name patterns that should be excluded from the 160 | // list. Patterns may be regular expressions, glob patterns or regular 161 | // strings. In addition, a block given to exclude will remove entries that 162 | // return true when given to the block. 163 | // 164 | // Note that glob patterns are expanded against the file system. If a file 165 | // is explicitly added to a file list, but does not exist in the file 166 | // system, then an glob pattern in the exclude list will not exclude the 167 | // file. 168 | // 169 | // Examples: 170 | // FileList['a.c', 'b.c'].exclude("a.c") => ['b.c'] 171 | // FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c'] 172 | // 173 | // If "a.c" is a file, then ... 174 | // FileList['a.c', 'b.c'].exclude("a.*") => ['b.c'] 175 | // 176 | // If "a.c" is not a file, then ... 177 | // FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c'] 178 | // 179 | FileList.prototype.exclude = function(/*Strings|Functions*/) { 180 | Array.prototype.forEach.call(arguments, function(/*String|Function*/ anObject) { 181 | if (typeof anObject === "function") 182 | this._excludedProcs.push(anObject); 183 | 184 | else 185 | this._excludedPatterns.push(anObject); 186 | }, this); 187 | 188 | if (!this._pending) 189 | this._resolveExclude(); 190 | 191 | return this; 192 | } 193 | 194 | // Clear all the exclude patterns so that we exclude nothing. 195 | FileList.prototype.clearExclude = function() { 196 | this._excludedPatterns = []; 197 | this._excludedProcs = []; 198 | 199 | return this; 200 | } 201 | 202 | /* 203 | # Define equality. 204 | def ==(array) 205 | to_ary == array 206 | end 207 | */ 208 | 209 | // Return the internal array object. 210 | FileList.prototype.toArray = function() { 211 | return this.items().slice(); 212 | } 213 | /* 214 | # Lie about our class. 215 | def is_a?(klass) 216 | klass == Array || super(klass) 217 | end 218 | alias kind_of? is_a? 219 | 220 | # Redefine * to return either a string or a new file list. 221 | def *(other) 222 | result = @items * other 223 | case result 224 | when Array 225 | FileList.new.import(result) 226 | else 227 | result 228 | end 229 | end 230 | */ 231 | 232 | // Resolve all the pending adds now. 233 | FileList.prototype.resolve = function() { 234 | if (this._pending) { 235 | this._pending = false; 236 | this._pendingAdd.forEach(function(aFilename) { 237 | this._resolveAdd(aFilename); 238 | }, this); 239 | 240 | this._pendingAdd = []; 241 | this._resolveExclude(); 242 | } 243 | 244 | return this; 245 | } 246 | 247 | FileList.prototype._resolveAdd = function(aFilename) { 248 | if (aFilename.match(/[\*?\[\{]/)) 249 | this._addMatching(aFilename); 250 | else 251 | this._items.push(aFilename); 252 | } 253 | 254 | FileList.prototype._resolveExclude = function() { 255 | this._items = this._items.filter(function(/*String*/ aFilename) { 256 | return !this._shouldExclude(aFilename); 257 | }, this); 258 | 259 | return this; 260 | } 261 | 262 | FileList.prototype._shouldExclude = function(aFilename) { 263 | return this._excludedPatterns.some(function(aPattern) { 264 | if (aPattern.constructor === RegExp && aPattern.test(aFilename)) 265 | return true; 266 | 267 | if (aPattern.match && aPattern.match(/[*?]/)) { 268 | return FILE.fnmatch(aPattern, aFilename); 269 | } 270 | 271 | if (aFilename === aPattern) 272 | return true; 273 | }) || 274 | this._excludedProcs.some(function(aFunction) { 275 | return aFunction.apply(this, [aFilename]); 276 | }, this); 277 | } 278 | 279 | /* 280 | # Return a new FileList with the results of running +sub+ against each 281 | # element of the oringal list. 282 | # 283 | # Example: 284 | # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o'] 285 | # 286 | def sub(pat, rep) 287 | inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) } 288 | end 289 | 290 | # Return a new FileList with the results of running +gsub+ against each 291 | # element of the original list. 292 | # 293 | # Example: 294 | # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\") 295 | # => ['lib\\test\\file', 'x\\y'] 296 | # 297 | def gsub(pat, rep) 298 | inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) } 299 | end 300 | 301 | # Same as +sub+ except that the oringal file list is modified. 302 | def sub!(pat, rep) 303 | each_with_index { |fn, i| self[i] = fn.sub(pat,rep) } 304 | self 305 | end 306 | 307 | # Same as +gsub+ except that the original file list is modified. 308 | def gsub!(pat, rep) 309 | each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) } 310 | self 311 | end 312 | 313 | # Apply the pathmap spec to each of the included file names, returning a 314 | # new file list with the modified paths. (See String#pathmap for 315 | # details.) 316 | def pathmap(spec=nil) 317 | collect { |fn| fn.pathmap(spec) } 318 | end 319 | 320 | // Return a new FileList with String#ext method applied 321 | // to each member of the array. 322 | // 323 | // This method is a shortcut for: 324 | // 325 | // array.collect { |item| item.ext(newext) } 326 | // 327 | // +ext+ is a user added method for the Array class. 328 | def ext(newext='') 329 | collect { |fn| fn.ext(newext) } 330 | end 331 | 332 | # Grep each of the files in the filelist using the given pattern. If a 333 | # block is given, call the block on each matching line, passing the file 334 | # name, line number, and the matching line of text. If no block is given, 335 | # a standard emac style file:linenumber:line message will be printed to 336 | # standard out. 337 | def egrep(pattern, *options) 338 | each do |fn| 339 | open(fn, "rb", *options) do |inf| 340 | count = 0 341 | inf.each do |line| 342 | count += 1 343 | if pattern.match(line) 344 | if block_given? 345 | yield fn, count, line 346 | else 347 | puts "#{fn}:#{count}:#{line}" 348 | end 349 | end 350 | end 351 | end 352 | end 353 | end 354 | */ 355 | // Return a new file list that only contains file names from the current 356 | // file list that exist on the file system. 357 | FileList.prototype.existing = function(/*String*/ aFilename) { 358 | return (new FileList())._import(this.items().filter(function(aFilename) { 359 | return FILE.exists(aFilename); 360 | }, this)); 361 | } 362 | 363 | // Modify the current file list so that it contains only file name that 364 | // exist on the file system. 365 | FileList.prototype.keepExisting = function() { 366 | this._items = this.filter(function(aFilename) { 367 | return FILE.exists(aFilename); 368 | }, this); 369 | 370 | return this; 371 | } 372 | /* 373 | # FileList version of partition. Needed because the nested arrays should 374 | # be FileLists in this version. 375 | def partition(&block) # :nodoc: 376 | resolve 377 | result = @items.partition(&block) 378 | [ 379 | FileList.new.import(result[0]), 380 | FileList.new.import(result[1]), 381 | ] 382 | end 383 | */ 384 | // Convert a FileList to a string by joining all elements with a space. 385 | FileList.prototype.toString = function() { 386 | return this.items().join(' '); 387 | } 388 | 389 | // Add matching glob patterns. 390 | FileList.prototype._addMatching = function(/*String*/ aPattern) { 391 | FILE.glob(aPattern).forEach(function(fileName){ 392 | this._items.push(fileName); 393 | }, this); 394 | 395 | /* Dir[pattern].each do |fn| 396 | self << fn unless exclude?(fn) 397 | end*/ 398 | } 399 | /* 400 | # Should the given file name be excluded? 401 | def exclude?(fn) 402 | return true if @exclude_patterns.any? do |pat| 403 | case pat 404 | when Regexp 405 | fn =~ pat 406 | when /[*?]/ 407 | File.fnmatch?(pat, fn, File::FNM_PATHNAME) 408 | else 409 | fn == pat 410 | end 411 | end 412 | @exclude_procs.any? { |p| p.call(fn) } 413 | end 414 | */ 415 | 416 | FileList.prototype._import = function(/*Array*/ anArray) { 417 | this._items = anArray.slice(); 418 | return this; 419 | } 420 | 421 | exports.FileList = FileList; 422 | 423 | /* 424 | module Rake 425 | class << self 426 | 427 | # Yield each file or directory component. 428 | def each_dir_parent(dir) # :nodoc: 429 | old_length = nil 430 | while dir != '.' && dir.length != old_length 431 | yield(dir) 432 | old_length = dir.length 433 | dir = File.dirname(dir) 434 | end 435 | end 436 | end 437 | end # module Rake 438 | */ 439 | -------------------------------------------------------------------------------- /lib/jake/application.js: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2009 280 North, Inc. (francisco@280north.com) 3 | // Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to 7 | // deal in the Software without restriction, including without limitation the 8 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | // sell copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | // IN THE SOFTWARE. 22 | // 23 | 24 | var FILE = require("file"); 25 | var SYSTEM = require("system"); 26 | var printf = require("printf").printf; 27 | 28 | var Task = require("./task").Task; 29 | var FileTask = require("./filetask").FileTask; 30 | var FileCreationTask = require("./filecreationtask").FileCreationTask; 31 | var TaskManager = require("./taskmanager").TaskManager; 32 | 33 | 34 | var DEFAULT_JAKEFILES = ["jakefile", "Jakefile", "jakefile.js", "Jakefile.js", "jakefile.j", "Jakefile.j"]; 35 | 36 | var parser = new (require("narwhal/args").Parser)(); 37 | parser.usage("targets..."); 38 | 39 | parser.option("--jakefile", "-f", "file") 40 | .set() 41 | .help("Use FILE as the jakefile."); 42 | 43 | parser.option("--tasks", "-T", "showTasks") 44 | .set("tasks") 45 | .help("Display the tasks "/*(matching optional PATTERN) */+"with descriptions, then exit."); 46 | 47 | parser.option("--describe", "-D", "showTasks") 48 | .set("describe") 49 | .help("Describe the tasks "/*(matching optional PATTERN), */+"then exit."); 50 | 51 | parser.option("--prereqs", "-P", "showPrereqs") 52 | .set(true) 53 | .help("Display the tasks and dependencies, then exit."); 54 | 55 | parser.option("--verbose", "-v", "verbose") 56 | .set(true) 57 | .help("Log message to standard output."); 58 | 59 | parser.helpful(); 60 | 61 | 62 | var Application = function() { 63 | TaskManager.call(this); 64 | 65 | this._name = "jake"; 66 | this._jakefiles = DEFAULT_JAKEFILES.slice(); 67 | this._jakefile = null; 68 | this._options = { args : [] }; 69 | // this._pendingImports = []; 70 | // this._imported = []; 71 | // this.loaders = { }; 72 | // this.defaultLoader = Rake::DefaultLoader.new 73 | this._originalDirectory = FILE.cwd(); 74 | this._topLevelTasks = []; 75 | /* 76 | add_loader('rb', DefaultLoader.new) 77 | add_loader('rf', DefaultLoader.new) 78 | add_loader('rake', DefaultLoader.new) 79 | @tty_output = STDOUT.tty?*/ 80 | } 81 | 82 | Application.prototype.name = function() { 83 | return this._name; 84 | } 85 | 86 | Application.__proto__ = TaskManager; 87 | Application.prototype.__proto__ = TaskManager.prototype; 88 | 89 | // Run the Jake application. The run method performs the following three steps: 90 | // 91 | // * Initialize the command line options (+init+). 92 | // * Define the tasks (+loadRakefile+). 93 | // * Run the top level tasks (+runTasks+). 94 | // 95 | // If you wish to build a custom jake command, you should call +init+ on your 96 | // application. Then define any tasks. Finally, call +topLevel+ to run your top 97 | // level tasks. 98 | Application.prototype.run = function(options) { 99 | this.init(options); 100 | this.loadJakefile(); 101 | this.topLevel(); 102 | } 103 | 104 | function timeDeltaString(/*Date*/ aTimeDelta) { 105 | var string = ""; 106 | 107 | if (aTimeDelta > 1000 * 60 * 60) { 108 | var hours = Math.floor(aTimeDelta / (1000 * 60 * 60)); 109 | 110 | aTimeDelta -= hours * 1000 * 60 * 60; 111 | 112 | string = hours + (hours === 1 ? " hour" : " hours"); 113 | } 114 | 115 | if (aTimeDelta > 1000 * 60) { 116 | var minutes = Math.floor(aTimeDelta / (1000 * 60)) 117 | 118 | aTimeDelta -= minutes * 1000 * 60; 119 | 120 | string += (string.length ? " " : "" ) + minutes + (minutes === 1 ? " minute" : " minutes"); 121 | } 122 | 123 | if (aTimeDelta > 1000) { 124 | var seconds = Math.floor(aTimeDelta / 1000); 125 | 126 | aTimeDelta -= seconds * 1000; 127 | 128 | string += (string.length ? " " : "" ) + seconds + (seconds === 1 ? " second" : " seconds"); 129 | } 130 | 131 | if (!string.length) 132 | string = aTimeDelta + (aTimeDelta === 1 ? " millisecond" : " millseconds"); 133 | 134 | return string; 135 | } 136 | 137 | // Initialize the command line parameters and app name. 138 | Application.prototype.init = function(options) { 139 | this._name = options.appName || "jake"; 140 | this.handleOptions(options.args || SYSTEM.args); 141 | this.collectTasks(); 142 | } 143 | 144 | // Find the rakefile and then load it and any pending imports. 145 | Application.prototype.loadJakefile = function() { 146 | this.rawLoadJakefile(); 147 | } 148 | 149 | // Run the top level tasks of a Rake application. 150 | Application.prototype.topLevel = function() { 151 | if (this._options.showTasks) 152 | this.displayTasksAndComments(); 153 | else if (this._options.showPrereqs) 154 | this.displayPrerequisites(); 155 | else { 156 | var start = new Date(); 157 | this._topLevelTasks.forEach(function(/*String*/ aTaskName) { 158 | this.invokeTask(aTaskName); 159 | }, this); 160 | print("Build took " + timeDeltaString(new Date() - start)); 161 | } 162 | } 163 | 164 | /* 165 | # Add a loader to handle imported files ending in the extension 166 | # +ext+. 167 | Application.prototype. 168 | 169 | def add_loader(ext, loader) 170 | ext = ".#{ext}" unless ext =~ /^\./ 171 | @loaders[ext] = loader 172 | end 173 | 174 | # Application options from the command line 175 | def options 176 | @options ||= OpenStruct.new 177 | end 178 | 179 | # private ---------------------------------------------------------------- 180 | */ 181 | 182 | Application.prototype.invokeTask = function(/*String*/ aTaskString) { 183 | var result = this.parseTaskString(aTaskString); 184 | var task = this.lookupTask(result[0]); 185 | 186 | task.invoke.apply(task, result[1]); 187 | } 188 | 189 | Application.prototype.parseTaskString = function(/*String*/ aString) { 190 | var matches = aString.match(/^([^\[]+)(\[(.*)\])$/); 191 | 192 | if (matches) 193 | return [matches[0], matches[3].split(/\s*,\s*/)]; 194 | 195 | return [aString, []]; 196 | } 197 | /* 198 | # Provide standard execption handling for the given block. 199 | def standard_exception_handling 200 | begin 201 | yield 202 | rescue SystemExit => ex 203 | # Exit silently with current status 204 | exit(ex.status) 205 | rescue SystemExit, OptionParser::InvalidOption => ex 206 | # Exit silently 207 | exit(1) 208 | rescue Exception => ex 209 | # Exit with error message 210 | $stderr.puts "#{name} aborted!" 211 | $stderr.puts ex.message 212 | if options.trace 213 | $stderr.puts ex.backtrace.join("\n") 214 | else 215 | $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || "" 216 | $stderr.puts "(See full trace by running task with --trace)" 217 | end 218 | exit(1) 219 | end 220 | end 221 | */ 222 | 223 | // True if one of the files in JAKEFILES is in the current directory. 224 | // If a match is found, it is copied into @jakefile. 225 | Application.prototype.hasJakefile = function(/*String*/ aDirectory) { 226 | for (var i = 0; i < this._jakefiles.length; ++i) { 227 | var jakefile = this._jakefiles[i]; 228 | 229 | if (FILE.exists(FILE.join(aDirectory, jakefile))) 230 | return jakefile; 231 | 232 | else if (jakefile === "") 233 | return null; 234 | } 235 | 236 | return null; 237 | } 238 | /* 239 | # True if we are outputting to TTY, false otherwise 240 | def tty_output? 241 | @tty_output 242 | end 243 | 244 | # Override the detected TTY output state (mostly for testing) 245 | def tty_output=( tty_output_state ) 246 | @tty_output = tty_output_state 247 | end 248 | 249 | # We will truncate output if we are outputting to a TTY or if we've been 250 | # given an explicit column width to honor 251 | def truncate_output? 252 | tty_output? || ENV['RAKE_COLUMNS'] 253 | end 254 | */ 255 | 256 | // Display the tasks and comments. 257 | Application.prototype.displayTasksAndComments = function() { 258 | var displayableTasks = Object.keys(this._tasks).map(function(name) { return this._tasks[name]; }, this).filter(function(t) { 259 | return !!t.fullComment(); 260 | }, this); 261 | 262 | switch (this._options.showTasks) { 263 | case "describe" : 264 | displayableTasks.forEach(function(t) { 265 | printf("%s %s", this.name(), t.name()); 266 | t.fullComment().split("\n").forEach(function(line) { 267 | print(" " + line); 268 | }) 269 | print(""); 270 | }, this); 271 | break 272 | case "tasks" : 273 | var width = displayableTasks.reduce(function(width, t) { return Math.max(width, t.name().length); }, 0); 274 | displayableTasks.forEach(function(t) { 275 | printf("%s %-"+width+"s # %s", this.name(), t.name(), t.comment()); 276 | }, this); 277 | break; 278 | } 279 | } 280 | 281 | /* 282 | def display_tasks_and_comments 283 | displayable_tasks = tasks.select { |t| 284 | t.comment && t.name =~ options.show_task_pattern 285 | } 286 | if options.full_description 287 | displayable_tasks.each do |t| 288 | puts "#{name} #{t.name_with_args}" 289 | t.full_comment.split("\n").each do |line| 290 | puts " #{line}" 291 | end 292 | puts 293 | end 294 | else 295 | width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10 296 | max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil 297 | displayable_tasks.each do |t| 298 | printf "#{name} %-#{width}s # %s\n", 299 | t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment 300 | end 301 | end 302 | end 303 | 304 | def terminal_width 305 | if ENV['RAKE_COLUMNS'] 306 | result = ENV['RAKE_COLUMNS'].to_i 307 | else 308 | result = unix? ? dynamic_width : 80 309 | end 310 | (result < 10) ? 80 : result 311 | rescue 312 | 80 313 | end 314 | 315 | # Calculate the dynamic width of the 316 | def dynamic_width 317 | @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) 318 | end 319 | 320 | def dynamic_width_stty 321 | %x{stty size 2>/dev/null}.split[1].to_i 322 | end 323 | 324 | def dynamic_width_tput 325 | %x{tput cols 2>/dev/null}.to_i 326 | end 327 | 328 | def unix? 329 | RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i 330 | end 331 | 332 | def windows? 333 | Win32.windows? 334 | end 335 | 336 | def truncate(string, width) 337 | if string.length <= width 338 | string 339 | else 340 | ( string[0, width-3] || "" ) + "..." 341 | end 342 | end 343 | */ 344 | 345 | // Display the tasks and prerequisites 346 | Application.prototype.displayPrerequisites = function() { 347 | this.tasks().forEach(function(t) { 348 | printf("%s %s", this.name(), t.name()); 349 | t.prerequisites().forEach(function(pre) { 350 | printf(" %s", pre); 351 | }); 352 | }, this); 353 | } 354 | 355 | /* 356 | def display_prerequisites 357 | tasks.each do |t| 358 | puts "#{name} #{t.name}" 359 | t.prerequisites.each { |pre| puts " #{pre}" } 360 | end 361 | end 362 | 363 | # A list of all the standard options used in rake, suitable for 364 | # passing to OptionParser. 365 | def standard_rake_options 366 | [ 367 | ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace", 368 | lambda { |value| 369 | require 'rake/classic_namespace' 370 | options.classic_namespace = true 371 | } 372 | ], 373 | ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.", 374 | lambda { |value| 375 | options.show_tasks = true 376 | options.full_description = true 377 | options.show_task_pattern = Regexp.new(value || '') 378 | } 379 | ], 380 | ['--dry-run', '-n', "Do a dry run without executing actions.", 381 | lambda { |value| 382 | verbose(true) 383 | nowrite(true) 384 | options.dryrun = true 385 | options.trace = true 386 | } 387 | ], 388 | ['--execute', '-e CODE', "Execute some Ruby code and exit.", 389 | lambda { |value| 390 | eval(value) 391 | exit 392 | } 393 | ], 394 | ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.", 395 | lambda { |value| 396 | puts eval(value) 397 | exit 398 | } 399 | ], 400 | ['--execute-continue', '-E CODE', 401 | "Execute some Ruby code, then continue with normal task processing.", 402 | lambda { |value| eval(value) } 403 | ], 404 | ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.", 405 | lambda { |value| $:.push(value) } 406 | ], 407 | ['--prereqs', '-P', "Display the tasks and dependencies, then exit.", 408 | lambda { |value| options.show_prereqs = true } 409 | ], 410 | ['--quiet', '-q', "Do not log messages to standard output.", 411 | lambda { |value| verbose(false) } 412 | ], 413 | ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.", 414 | lambda { |value| 415 | value ||= '' 416 | @rakefiles.clear 417 | @rakefiles << value 418 | } 419 | ], 420 | ['--rakelibdir', '--rakelib', '-R RAKELIBDIR', 421 | "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')", 422 | lambda { |value| options.rakelib = value.split(':') } 423 | ], 424 | ['--require', '-r MODULE', "Require MODULE before executing rakefile.", 425 | lambda { |value| 426 | begin 427 | require value 428 | rescue LoadError => ex 429 | begin 430 | rake_require value 431 | rescue LoadError => ex2 432 | raise ex 433 | end 434 | end 435 | } 436 | ], 437 | ['--rules', "Trace the rules resolution.", 438 | lambda { |value| options.trace_rules = true } 439 | ], 440 | ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.", 441 | lambda { |value| options.nosearch = true } 442 | ], 443 | ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.", 444 | lambda { |value| 445 | verbose(false) 446 | options.silent = true 447 | } 448 | ], 449 | ['--system', '-g', 450 | "Using system wide (global) rakefiles (usually '~/.rake/*.rake').", 451 | lambda { |value| options.load_system = true } 452 | ], 453 | ['--no-system', '--nosystem', '-G', 454 | "Use standard project Rakefile search paths, ignore system wide rakefiles.", 455 | lambda { |value| options.ignore_system = true } 456 | ], 457 | ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.", 458 | lambda { |value| 459 | options.show_tasks = true 460 | options.show_task_pattern = Regexp.new(value || '') 461 | options.full_description = false 462 | } 463 | ], 464 | ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.", 465 | lambda { |value| 466 | options.trace = true 467 | verbose(true) 468 | } 469 | ], 470 | ['--verbose', '-v', "Log message to standard output.", 471 | lambda { |value| verbose(true) } 472 | ], 473 | ['--version', '-V', "Display the program version.", 474 | lambda { |value| 475 | puts "rake, version #{RAKEVERSION}" 476 | exit 477 | } 478 | ] 479 | ] 480 | end 481 | */ 482 | 483 | // Read and handle the command line options. 484 | Application.prototype.handleOptions = function(args) { 485 | this._options = parser.parse(args.slice()); 486 | 487 | if (this._options.file) 488 | this._jakefiles = [this._options.file]; 489 | } 490 | 491 | /* 492 | def handle_options 493 | options.rakelib = ['rakelib'] 494 | 495 | OptionParser.new do |opts| 496 | opts.banner = "rake [-f rakefile] {options} targets..." 497 | opts.separator "" 498 | opts.separator "Options are ..." 499 | 500 | opts.on_tail("-h", "--help", "-H", "Display this help message.") do 501 | puts opts 502 | exit 503 | end 504 | 505 | standard_rake_options.each { |args| opts.on(*args) } 506 | end.parse! 507 | 508 | # If class namespaces are requested, set the global options 509 | # according to the values in the options structure. 510 | if options.classic_namespace 511 | $show_tasks = options.show_tasks 512 | $show_prereqs = options.show_prereqs 513 | $trace = options.trace 514 | $dryrun = options.dryrun 515 | $silent = options.silent 516 | end 517 | end 518 | 519 | # Similar to the regular Ruby +require+ command, but will check 520 | # for *.rake files in addition to *.rb files. 521 | def rake_require(file_name, paths=$LOAD_PATH, loaded=$") 522 | return false if loaded.include?(file_name) 523 | paths.each do |path| 524 | fn = file_name + ".rake" 525 | full_path = File.join(path, fn) 526 | if File.exist?(full_path) 527 | load full_path 528 | loaded << fn 529 | return true 530 | end 531 | end 532 | fail LoadError, "Can't find #{file_name}" 533 | end 534 | */ 535 | 536 | Application.prototype.findJakefileLocation = function() { 537 | var directory = FILE.cwd(); 538 | var filename = null; 539 | 540 | while (!(filename = this.hasJakefile(directory)) && directory !== "/")// && !this._options.nosearch) 541 | directory = FILE.absolute(FILE.join(directory, "..")); 542 | 543 | if (!filename) 544 | return null; 545 | 546 | return [filename, directory]; 547 | } 548 | 549 | Application.prototype.rawLoadJakefile = function() { 550 | var result = this.findJakefileLocation(); 551 | var jakefile = result ? result[0] : null; 552 | var location = result ? result[1] : null; 553 | var options = this._options; 554 | /* 555 | if (!options.ignore_system && (options.load_system || !rakefile) && system_dir && FILE.directory?(system_dir) 556 | if (options["silent"]) 557 | print("(in "); 558 | puts "(in #{Dir.pwd})" unless options.silent 559 | glob("#{system_dir}/*.rake") do |name| 560 | add_import name 561 | end 562 | } 563 | else*/ { 564 | if (!jakefile) 565 | throw "No Jakefile found (looking for: " + this._jakefiles.join(', ') + ")"; 566 | 567 | this._jakefile = jakefile; 568 | 569 | // file.chdir(location); 570 | 571 | if (!options["silent"]) 572 | print("(in " + FILE.cwd() + ")"); 573 | 574 | // $rakefile = @rakefile if options.classic_namespace 575 | 576 | if (jakefile && jakefile.length)//expand_path? 577 | require(FILE.absolute(FILE.join(location, jakefile))); 578 | 579 | /* options.rakelib.each do |rlib| 580 | glob("#{rlib}/*.rake") do |name| 581 | add_import name 582 | end 583 | */ 584 | } 585 | //load_imports 586 | } 587 | 588 | /* 589 | def glob(path, &block) 590 | Dir[path.gsub("\\", '/')].each(&block) 591 | end 592 | private :glob 593 | */ 594 | /* 595 | # The directory path containing the system wide rakefiles. 596 | def system_dir 597 | @system_dir ||= 598 | begin 599 | if ENV['RAKE_SYSTEM'] 600 | ENV['RAKE_SYSTEM'] 601 | elsif Win32.windows? 602 | Win32.win32_system_dir 603 | else 604 | standard_system_dir 605 | end 606 | end 607 | end 608 | 609 | # The standard directory containing system wide rake files. 610 | def standard_system_dir #:nodoc: 611 | File.join(File.expand_path('~'), '.rake') 612 | end 613 | private :standard_system_dir 614 | */ 615 | 616 | // Collect the list of tasks on the command line. If no tasks are 617 | // given, return a list containing only the default task. 618 | // Environmental assignments are processed at this time as well. 619 | Application.prototype.collectTasks = function() { 620 | this._topLevelTasks = []; 621 | 622 | var topLevelTasks = this._topLevelTasks; 623 | 624 | this._options.args.forEach(function(/*String*/ anArgument) { 625 | var matches = anArgument.match(/^(\w+)=(.*)$/); 626 | if (matches) 627 | SYSTEM.env[matches[1]] = matches[2]; 628 | else 629 | topLevelTasks.push(anArgument); 630 | }); 631 | 632 | if (topLevelTasks.length <= 0) 633 | topLevelTasks.push("default"); 634 | } 635 | /* 636 | # Add a file to the list of files to be imported. 637 | def add_import(fn) 638 | @pending_imports << fn 639 | end 640 | 641 | # Load the pending list of imported files. 642 | def load_imports 643 | while fn = @pending_imports.shift 644 | next if @imported.member?(fn) 645 | if fn_task = lookup(fn) 646 | fn_task.invoke 647 | end 648 | ext = File.extname(fn) 649 | loader = @loaders[ext] || @default_loader 650 | loader.load(fn) 651 | @imported << fn 652 | end 653 | end 654 | 655 | # Warn about deprecated use of top level constant names. 656 | def const_warning(const_name) 657 | @const_warning ||= false 658 | if ! @const_warning 659 | $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } + 660 | %{found at: #{rakefile_location}} # ' 661 | $stderr.puts %{ Use --classic-namespace on rake command} 662 | $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile} 663 | end 664 | @const_warning = true 665 | end 666 | 667 | def rakefile_location 668 | begin 669 | fail 670 | rescue RuntimeError => ex 671 | ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || "" 672 | end 673 | end 674 | */ 675 | 676 | // Exports 677 | exports.Application = Application; 678 | --------------------------------------------------------------------------------