Permalink
Browse files

Integrating Jakob Mattsson's file-task changes.

  • Loading branch information...
1 parent 0c8ca80 commit 29e5bd39a28ee1a17e4d5532ab2cba66334b4422 @mde mde committed Nov 27, 2010
Showing with 148 additions and 83 deletions.
  1. +124 −83 lib/jake.js
  2. +24 −0 tests/Jakefile
View
@@ -146,6 +146,9 @@ jake = new function () {
var _this = this
// The list of tasks/dependencies to run, parsed recursively
, _taskList = []
+ // A dictionary of loaded tasks, to ensure that all tasks
+ // run once and only once
+ , _taskDict = {}
// The args passed to the 'jake' invocation, after the task name
, _args;
@@ -157,44 +160,29 @@ jake = new function () {
* 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, root, callback) {
- // If this is the root task, it's a failure if the task cannot be found.
- // If this is not the root task, we'll just assume the name is a file.
- var task = root ? _this.getTask(name) : _this.tryGetTask(name),
- deps = task ? task.deps : [];
-
- if (task == null) {
- // No task could be found, so search for a file instead
- // and provide the callback with the last time it changed.
- fs.lstat(name, function(err, stats) {
- if (err) throw new Error(err.message);
- callback(stats.ctime);
- });
- } else if (task.isFile) {
- // The task was found and it was a file task.
- fs.lstat(name, function(err, stats) {
- if (deps && _isArray(deps) && deps.length) {
- // If the task has dependencies these are invoked in order.
- // If any of them were changed after the current file, or
- // if the current file does not exist, then push the current
- // task to the list of tasks and update the time of last change.
- stats = stats || { ctime: 0 };
- for (var i = 0, ii = deps.length, depsLeft = deps.length, maxTime = stats.ctime; i < ii; i++) {
+ return obj &&
+ typeof obj === 'object' &&
+ typeof obj.length === 'number' &&
+ typeof obj.splice === 'function' &&
+ !(obj.propertyIsEnumerable('length'));
+ }
+
+ , _taskHasDeps = function (deps) {
+ return !!(deps && _isArray(deps) && deps.length);
+ }
+
+ , _handleFileTask = function (err, stats, name, deps, callback) {
+ // If the task has dependencies these are invoked in order.
+ // If any of them were changed after the current file, or
+ // if the current file does not exist, then push the current
+ // task to the list of tasks and update the time of last change.
+ if (_taskHasDeps(deps)) {
+ stats = stats || {ctime: 0};
+ for (var i = 0, ii = deps.length, depsLeft = deps.length, maxTime = stats.ctime;
+ i < ii; i++) {
_parseDeps(deps[i], false, function (ctime) {
depsLeft -= 1;
- maxTime = maxTime == null || maxTime < ctime ? ctime : maxTime;
+ maxTime = (maxTime == null || maxTime < ctime) ? ctime : maxTime;
if (depsLeft == 0) {
if (maxTime > stats.ctime) {
_taskList.push(name);
@@ -203,43 +191,87 @@ jake = new function () {
}
});
}
- } else if (err && err.errno == 2) {
- // If it does not have dependencies and could not
- // be found, simply execute the task and use the
- // current time as the last time it changed.
- _taskList.push(name);
- callback(new Date());
- } else if (err) {
+ }
+ // If it does not have dependencies and could not
+ // be found, simply execute the task and use the
+ // current time as the last time it changed.
+ else if (err) {
+ if (err.errno == 2) {
+ _taskList.push(name);
+ callback(new Date());
+ }
// Errors are rethrown.
- throw new Error(err.message);
- } else {
- // No dependencies and the file already existed, then don't
- // do anything and just return the time of last changed.
+ else {
+ throw new Error(err.message);
+ }
+ }
+ // No dependencies and the file already existed, then don't
+ // do anything and just return the time of last changed.
+ else {
callback(stats.ctime);
}
- });
- } else {
- if (deps && _isArray(deps) && deps.length) {
- // If the task have dependencies, execute those first and then
- // push the task to the task list. In case it will be used as a
- // dependancy for a file task, the last time of change is set to
- // the current time in order to force files to update as well.
- for (var i = 0, ii = deps.length, ctr = deps.length; i < ii; i++) {
- _parseDeps(deps[i], false, function () {
- ctr -= 1;
- if (ctr == 0) {
+ }
+
+ /**
+ * 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, root, callback) {
+ var task = _this.getTask(name),
+ deps = task ? task.deps : [];
+
+ // No task found
+ if (!task) {
+
+ // If this is the root task, it's a failure if the task cannot be found.
+ if (root) {
+ throw new Error('Task "' + name + '" is not defined in the Jakefile.');
+ }
+ // If this is not the root task, we'll just assume the name is a file.
+ // Search for a file instead and provide the callback with the
+ // last time it changed
+ fs.lstat(name, function(err, stats) {
+ if (err) {
+ throw new Error(err.message);
+ }
+ callback(stats.ctime);
+ });
+ }
+ // The task was found
+ else {
+ // File task
+ if (task.isFile) {
+ fs.lstat(name, function(err, stats) {
+ _handleFileTask(err, stats, name, deps, callback);
+ });
+ }
+ // Normal task
+ else {
+ // If the task has dependencies, execute those first and then
+ // push the task to the task list. In case it will be used as a
+ // dependancy for a file task, the last time of change is set to
+ // the current time in order to force files to update as well.
+ if (_taskHasDeps(deps)) {
+ for (var i = 0, ii = deps.length, ctr = deps.length; i < ii; i++) {
+ _parseDeps(deps[i], false, function () {
+ ctr -= 1;
+ if (ctr == 0) {
+ _taskList.push(name);
+ callback(new Date());
+ }
+ });
+ }
+ }
+ // If the task does not have dependencies, just push it.
+ else {
_taskList.push(name);
callback(new Date());
}
- });
+ }
}
- } else {
- // If the task does not have dependencies, just push it.
- _taskList.push(name);
- callback(new Date());
- }
- }
- };
+ };
// Public properties
// =================
@@ -298,9 +330,6 @@ jake = new function () {
}
var task = jake.namespaceTasks[nsName][taskName];
- if (!task) {
- throw new Error('Task "' + name + '" is not defined in the Jakefile.');
- }
return task;
};
@@ -312,7 +341,8 @@ jake = new function () {
this.tryGetTask = function (name) {
try {
return this.getTask(name);
- } catch (e) {
+ }
+ catch (e) {
return null;
}
};
@@ -332,21 +362,32 @@ jake = new function () {
, 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 tasks only once, even if it ends up in the task queue
+ // multiple times
+ if (_taskDict[name]) {
+ complete();
}
+ // Okie, we haven't done this one
+ else {
+ // Flag this one as done, no repeatsies
+ _taskDict[name] = true;
- // Run this mofo
- task.handler.apply(task, passArgs);
+ task = this.getTask(name);
- // Async tasks call this themselves
- if (!task.async) {
- complete();
+ // 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();
+ }
}
}
};
@@ -523,7 +564,7 @@ try {
if (isCoffee) {
try {
CoffeeScript = require('coffee-script');
- }
+ }
catch (e) {
jake.die('CoffeeScript is missing! Try `npm install coffee-script`');
}
View
@@ -7,6 +7,30 @@ task('default', [], function () {
console.log(sys.inspect(arguments));
});
+desc('This is the asdf task. It depends on both qwer and the default');
+task('asdf', ['qwer', 'default'], function () {
+ console.log('Running the asdf task.');
+ console.log(sys.inspect(arguments));
+});
+
+desc('This is the qwer task. It depends on zxcv, uiop, and the default');
+task('qwer', ['zxcv', 'uiop', 'default'], function () {
+ console.log('Running the qwer task.');
+ console.log(sys.inspect(arguments));
+});
+
+desc('This is the zxcv task. It depends on the default');
+task('zxcv', ['default'], function () {
+ console.log('Running the zxcv task.');
+ console.log(sys.inspect(arguments));
+});
+
+desc('This is the uiop task. It depends on the default');
+task('uiop', ['default'], function () {
+ console.log('Running the uiop task.');
+ console.log(sys.inspect(arguments));
+});
+
desc('File task, concating two files together');
file('concat.txt', ['src1.txt', 'src2.txt'], function() {
console.log('doing concat.txt file-task');

0 comments on commit 29e5bd3

Please sign in to comment.