Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fix for async tasks, a bunch of refactoring and commenting.

  • Loading branch information...
commit 1f64c2dcf61958faa723f0ab1843014d6d0820b5 1 parent f1e9ac0
@mde mde authored
Showing with 122 additions and 49 deletions.
  1. +121 −48 lib/jake.js
  2. +1 −1  tests/Jakefile
View
169 lib/jake.js
@@ -128,20 +128,90 @@ parseopts.Parser.prototype = new function () {
* The main namespace for Jake
*/
var jake = new function () {
+
+ // Private variables
+ // =================
+ // Local reference for scopage
+ var _this = this
+ // The list of tasks/dependencies to run, parsed recursively
+ , _taskList = []
+ // The args passed to the 'jake' invocation, after the task name
+ , _args;
+ // Private functions
+ // =================
+ /**
+ * Crockfordian Array-test
+ * @param {???} obj A value of indeterminate type that may
+ * or may not be an Array
+ */
var _isArray = function (obj) {
return obj &&
typeof obj === 'object' &&
typeof obj.length === 'number' &&
typeof obj.splice === 'function' &&
!(obj.propertyIsEnumerable('length'));
+ }
+ /**
+ * Parses all dependencies of a task (and their dependencies, etc.)
+ * recursively, and adds them to the _taskList queue.
+ * @param {String} name The name of the current task whose
+ * dependencies are being parsed.
+ */
+ , _parseDeps = function (name) {
+ _taskList.push(name);
+ var task = _this.getTask(name)
+ , deps = task.deps;
+ if (deps && _isArray(deps) && deps.length) {
+ for (var i = 0, ii = deps.length; i < ii; i++) {
+ _parseDeps(deps[i]);
+ }
+ }
};
+ // Public properties
+ // =================
+ // For namespaced tasks -- tasks with no namespace are put into the
+ // 'default' namespace so lookup code can work the same for both
+ // namespaced and non-namespaced.
this.currentNamespace = 'default';
+ // Saves the description created by a 'desc' call that prefaces a
+ // 'task' call that defines a task.
this.currentTaskDescription = null;
+ // Name/value map of all the various tasks defined in a Jakefile.
+ // Non-namespaced tasks are placed into 'default.'
this.namespaceTasks = {
'default': {}
};
+
+ /**
+ * Initial function called to run the specified task. Parses all the
+ * dependencies and then kick off the queue-processing
+ * @param {String} name The name of the task to run
+ * @param {Array} args The list of command-line args passed after
+ * the task name -- may be a combination of plain positional args,
+ * or name/value pairs in the form of name:value or name=value to
+ * be placed in a final keyword/value object param
+ */
+ this.runTask = function (name, args) {
+ // Save the args as an inner-scope var to keep feeding to each
+ // task as it gets executed
+ _args = args;
+ // Parse all the dependencies up front. This allows use of a simple
+ // queue to run all the tasks in order, and treat sync/async essentially
+ // the same.
+ _parseDeps(name);
+ if (!_taskList.length) {
+ this.die('No tasks to run.');
+ }
+ // Kick off running the list of tasks
+ this.runNextTask(args);
+ };
+
+ /**
+ * Looks up a function object based on its name or namespace:name
+ * @param {String} name The name of the task to look up
+ */
this.getTask = function (name) {
var nameArr = name.split(':');
var nsName, taskName;
@@ -160,60 +230,53 @@ var jake = new function () {
}
return task;
};
- this.runTask = function (name, args) {
- var jake = this;
-
- var task = this.getTask(name);
- var deps = task.deps;
-
- // If we have some dependent tasks, run 'em
- if (deps && _isArray(deps)) {
- deps = deps.slice();
-
- var _complete = global.complete;
- var runDependencies = global.complete = function () {
- if (deps.length == 0) {
- global.complete = _complete;
- _doRunTask();
- if (!task.async) {
- global.complete();
- }
- return;
- }
- var async = false;
- do {
- if (!deps.length) {
- return runDependencies();
- }
-
- var depName = deps.shift();
- jake.runTask.call(jake, depName, args);
- async = jake.getTask(depName).async;
- } while (!async && global.complete === runDependencies);
- };
-
- runDependencies();
- }
- else {
- _doRunTask();
- }
-
- function _doRunTask () {
- var parsed = jake.parseArgs(args);
- var passArgs = parsed.cmds;
+
+ /**
+ * Runs the next task in the _taskList queue until none are left
+ * Synchronous tasks require calling "complete" afterward, and async
+ * ones are expected to do that themselves
+ * TODO Add a cancellable error-throw in a setTimeout to allow
+ * an async task to timeout instead of having the script hang
+ * indefinitely
+ */
+ this.runNextTask = function () {
+ var name = _taskList.shift()
+ , task
+ , parsed
+ , passArgs;
+ // If there are still tasks to run, do it
+ if (name) {
+ task = this.getTask(name);
+
+ // TODO Do this once instead of on each iteration
+ parsed = this.parseArgs(_args);
+ passArgs = parsed.cmds;
if (parsed.opts) {
passArgs = parsed.cmds.concat(parsed.opts);
}
+
+ // Run this mofo
task.handler.apply(task, passArgs);
- };
+
+ // Async tasks call this themselves
+ if (!task.async) {
+ complete();
+ }
+ }
};
+ /**
+ * Parse the list of args into positional args and a final keyword/value
+ * object to pass to the task invocations.
+ * @param {Array} args A list of arguments to parse.
+ */
this.parseArgs = function (args) {
- var cmds = [];
- var opts = {};
- var pat = /:|=/;
- var argItems;
- var hasOpts = false;
+ var cmds = []
+ , opts = {}
+ , pat = /:|=/
+ , argItems
+ , hasOpts = false;
+
for (var i = 0; i < args.length; i++) {
argItems = args[i].split(pat);
if (argItems.length > 1) {
@@ -228,11 +291,19 @@ var jake = new function () {
return {cmds: cmds, opts: opts};
};
+ /**
+ * Prints out a message and ends the jake program.
+ * @param {String} str The message to print out before dying.
+ */
this.die = function (str) {
sys.puts(str);
process.exit();
};
+ /**
+ * Displays the list of descriptions avaliable for tasks defined in
+ * a Jakefile
+ */
this.showAllTaskDescriptions = function () {
var nsTasks = this.namespaceTasks;
var str = '';
@@ -296,7 +367,9 @@ global.namespace = function (name, tasks) {
jake.currentNamespace = 'default';
};
-global.complete = function () {};
+global.complete = function () {
+ jake.runNextTask();
+};
// ========================
View
2  tests/Jakefile
@@ -8,7 +8,7 @@ task('default', [], function () {
namespace('foo', function () {
desc('This the foo:bar task');
- task('bar', ['default', 'foo:baz', 'foo:qux'], function () {
+ task('bar', ['default', 'foo:qux', 'foo:baz'], function () {
console.log('doing foo:bar task');
console.log(sys.inspect(arguments));
});
Please sign in to comment.
Something went wrong with that request. Please try again.