pax_global_header 0000666 0000000 0000000 00000000064 12240565131 0014511 g ustar 00root root 0000000 0000000 52 comment=34aa42f9737824767e10b407a61282d9f273a9bb
jake-0.7.4/ 0000775 0000000 0000000 00000000000 12240565131 0012433 5 ustar 00root root 0000000 0000000 jake-0.7.4/.gitignore 0000664 0000000 0000000 00000000110 12240565131 0014413 0 ustar 00root root 0000000 0000000 *.swp
*.swo
dist
.idea/
tags
nbproject/
node_modules
tmtags
*.DS_Store
jake-0.7.4/.jshintignore 0000664 0000000 0000000 00000000015 12240565131 0015133 0 ustar 00root root 0000000 0000000 node_modules
jake-0.7.4/.jshintrc 0000664 0000000 0000000 00000000473 12240565131 0014264 0 ustar 00root root 0000000 0000000 {
"laxcomma": true,
"laxbreak": true,
"supernew": true,
"curly": true,
"immed": true,
"undef": true,
"node": true,
"globals": {
"jake": false,
"task": false,
"namespace": false,
"desc": false,
"complete": false,
"file": false,
"directory": false,
"fail": false
}
}
jake-0.7.4/.travis.yml 0000664 0000000 0000000 00000000122 12240565131 0014537 0 ustar 00root root 0000000 0000000 language: node_js
node_js:
- "0.10"
- "0.8"
script: ./bin/cli.js test --trace
jake-0.7.4/Jakefile 0000664 0000000 0000000 00000002024 12240565131 0014066 0 ustar 00root root 0000000 0000000 var fs = require('fs')
, path = require('path');
testTask('Jake', function () {
this.testFiles.include('test/*.js');
this.testFiles.exclude('test/helpers.js');
});
namespace('doc', function () {
task('generate', ['doc:clobber'], function () {
var cmd = '../node-jsdoc-toolkit/app/run.js -n -r=100 ' +
'-t=../node-jsdoc-toolkit/templates/codeview -d=./doc/ ./lib';
jake.logger.log('Generating docs ...');
jake.exec([cmd], function () {
jake.logger.log('Done.');
complete();
});
}, {async: true});
task('clobber', function () {
var cmd = 'rm -fr ./doc/*';
jake.exec([cmd], function () {
jake.logger.log('Clobbered old docs.');
complete();
});
}, {async: true});
});
desc('Generate docs for Jake');
task('doc', ['doc:generate']);
npmPublishTask('jake', function () {
this.packageFiles.include([
'Makefile'
, 'Jakefile'
, 'README.md'
, 'package.json'
, 'lib/**'
, 'bin/**'
, 'test/**'
]);
this.packageFiles.exclude([
'test/tmp'
]);
});
jake-0.7.4/Makefile 0000664 0000000 0000000 00000002447 12240565131 0014102 0 ustar 00root root 0000000 0000000 #
# Jake JavaScript build tool
# Copyright 2112 Matthew Eernisse (mde@fleegix.org)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
.PHONY: all build install clean uninstall
PREFIX=/usr/local
DESTDIR=
all: build
build:
@echo 'Jake built.'
install:
@mkdir -p $(DESTDIR)$(PREFIX)/bin && \
mkdir -p $(DESTDIR)$(PREFIX)/lib/node_modules/jake && \
mkdir -p ./node_modules && \
npm install utilities minimatch && \
cp -R ./* $(DESTDIR)$(PREFIX)/lib/node_modules/jake/ && \
ln -snf ../lib/node_modules/jake/bin/cli.js $(DESTDIR)$(PREFIX)/bin/jake && \
chmod 755 $(DESTDIR)$(PREFIX)/lib/node_modules/jake/bin/cli.js && \
echo 'Jake installed.'
clean:
@true
uninstall:
@rm -f $(DESTDIR)$(PREFIX)/bin/jake && \
rm -fr $(DESTDIR)$(PREFIX)/lib/node_modules/jake/ && \
echo 'Jake uninstalled.'
jake-0.7.4/README.md 0000664 0000000 0000000 00000100620 12240565131 0013711 0 ustar 00root root 0000000 0000000 ### Jake -- JavaScript build tool for Node.js
[](https://travis-ci.org/mde/jake)
### Installing with [NPM](http://npmjs.org/)
Install globally with:
npm install -g jake
Or you may also install it as a development dependency in a package.json file:
// package.json
"devDependencies": {
"jake": "latest"
}
Then install it with `npm install`
Note Jake is intended to be mostly a command-line tool, but lately there have been
changes to it so it can be either embedded, or run from inside your project.
### Installing from source
Prerequisites: Jake requires [Node.js](), and the
[utilities](https://npmjs.org/package/utilities) and
[minimatch](https://npmjs.org/package/minimatch) modules.
Get Jake:
git clone git://github.com/mde/jake.git
Build Jake:
cd jake && make && sudo make install
Even if you're installing Jake from source, you'll still need NPM for installing
the few modules Jake depends on. `make install` will do this automatically for
you.
By default Jake is installed in "/usr/local." To install it into a different
directory (e.g., one that doesn't require super-user privilege), pass the PREFIX
variable to the `make install` command. For example, to install it into a
"jake" directory in your home directory, you could use this:
make && make install PREFIX=~/jake
If do you install Jake somewhere special, you'll need to add the "bin" directory
in the install target to your PATH to get access to the `jake` executable.
### Windows, installing from source
For Windows users installing from source, there are some additional steps.
*Assumed: current directory is the same directory where node.exe is present.*
Get Jake:
git clone git://github.com/mde/jake.git node_modules/jake
Copy jake.bat and jake to the same directory as node.exe
copy node_modules/jake/jake.bat jake.bat
copy node_modules/jake/jake jake
Add the directory of node.exe to the environment PATH variable.
### Basic usage
jake [options ...] [env variables ...] target
### Description
Jake is a simple JavaScript build program with capabilities similar to the
regular make or rake command.
Jake has the following features:
* Jakefiles are in standard JavaScript syntax
* Tasks with prerequisites
* Namespaces for tasks
* Async task execution
### Options
-V/v
--version Display the Jake version.
-h
--help Display help message.
-f *FILE*
--jakefile *FILE* Use FILE as the Jakefile.
-C *DIRECTORY*
--directory *DIRECTORY* Change to DIRECTORY before running tasks.
-q
--quiet Do not log messages to standard output.
-J *JAKELIBDIR*
--jakelibdir *JAKELIBDIR* Auto-import any .jake files in JAKELIBDIR.
(default is 'jakelib')
-B
--always-make Unconditionally make all targets.
-t
--trace Enable full backtrace.
-T/ls
--tasks Display the tasks (matching optional PATTERN)
with descriptions, then exit.
### Jakefile syntax
A Jakefile is just executable JavaScript. You can include whatever JavaScript
you want in it.
## API Docs
API docs [can be found here](http://mde.github.com/jake/doc/).
## Tasks
Use `task` to define tasks. It has one required argument, the task-name, and
three optional arguments:
```javascript
task(name, [prerequisites], [action], [opts]);
```
The `name` argument is a String with the name of the task, and `prerequisites`
is an optional Array arg of the list of prerequisite tasks to perform first.
The `action` is a Function defininng the action to take for the task. (Note that
Object-literal syntax for name/prerequisites in a single argument a la Rake is
also supported, but JavaScript's lack of support for dynamic keys in Object
literals makes it not very useful.) The action is invoked with the Task object
itself as the execution context (i.e, "this" inside the action references the
Task object).
The `opts` argument is the normal JavaScript-style 'options' object. When a
task's operations are asynchronous, the `async` property should be set to
`true`, and the task must call `complete()` to signal to Jake that the task is
done, and execution can proceed. By default the `async` property is `false`.
Tasks created with `task` are always executed when asked for (or are a
prerequisite). Tasks created with `file` are only executed if no file with the
given name exists or if any of its file-prerequisites are more recent than the
file named by the task. Also, if any prerequisite is a regular task, the file
task will always be executed.
Use `desc` to add a string description of the task.
Here's an example:
```javascript
desc('This is the default task.');
task('default', function (params) {
console.log('This is the default task.');
});
desc('This task has prerequisites.');
task('hasPrereqs', ['foo', 'bar', 'baz'], function (params) {
console.log('Ran some prereqs first.');
});
```
And here's an example of an asynchronous task:
```javascript
desc('This is an asynchronous task.');
task('asyncTask', {async: true}, function () {
setTimeout(complete, 1000);
});
```
A Task is also an EventEmitter which emits the 'start' event when it begins to
run, and the 'complete' event when it is finished. This allows asynchronous
tasks to be run from within other asked via either `invoke` or `execute`, and
ensure they will complete before the rest of the containing task executes. See
the section "Running tasks from within other tasks," below.
### File-tasks
Create a file-task by calling `file`.
File-tasks create a file from one or more other files. With a file-task, Jake
checks both that the file exists, and also that it is not older than the files
specified by any prerequisite tasks. File-tasks are particularly useful for
compiling something from a tree of source files.
```javascript
desc('This builds a minified JS file for production.');
file('foo-minified.js', ['bar', 'foo-bar.js', 'foo-baz.js'], function () {
// Code to concat and minify goes here
});
```
### Directory-tasks
Create a directory-task by calling `directory`.
Directory-tasks create a directory for use with for file-tasks. Jake checks for
the existence of the directory, and only creates it if needed.
```javascript
desc('This creates the bar directory for use with the foo-minified.js file-task.');
directory('bar');
```
This task will create the directory when used as a prerequisite for a file-task,
or when run from the command-line.
### Namespaces
Use `namespace` to create a namespace of tasks to perform. Call it with two arguments:
```javascript
namespace(name, namespaceTasks);
```
Where is `name` is the name of the namespace, and `namespaceTasks` is a function
with calls inside it to `task` or `desc` definining all the tasks for that
namespace.
Here's an example:
```javascript
desc('This is the default task.');
task('default', function () {
console.log('This is the default task.');
});
namespace('foo', function () {
desc('This the foo:bar task');
task('bar', function () {
console.log('doing foo:bar task');
});
desc('This the foo:baz task');
task('baz', ['default', 'foo:bar'], function () {
console.log('doing foo:baz task');
});
});
```
In this example, the foo:baz task depends on the the default and foo:bar tasks.
### Rules
When you add a filename as a prerequisite for a task, but there is not a a
file-task defined for it, Jake can create file-tasks on the fly from Rules.
Here's an example:
```javascript
rule('.o', '.c', {async: true}, function () {
var cmd = 'cc ' + this.source + ' -c -o ' + this.name;
jake.exec(cmd, function () {
complete();
});
});
```
This rule will take effect for any task-name that ends in '.o', but will require
the existence of a prerequisite source file with the same name ending in '.c'.
For example, with this rule, if you reference a task 'foobarbaz.o' as a
prerequisite somewhere in one of your Jake tasks, rather than complaining about
this file not existing, or the lack of a task with that name, Jake will
automatically create a FileTask for 'foobarbaz.o' with the action specified in
the rule you've defined. (The usual action would be to create 'foobarbaz.o' from
'foobarbaz.c'). If 'foobarbaz.c' does not exist, it will recursively attempt
synthesize a viable rule for it as well.
#### Regex patterns
You can use regular expresions to match file extensions as well:
```javascript
rule(/\.o$/, '.c', {async: true}, function () {
var cmd = 'cc ' + this.source + ' -c -o ' + this.name;
jake.exec(cmd, function () {
complete();
});
});
```
#### Source files from functions
You can also use a function to calculate the name of the desired source-file to
use, instead of assuming simple suffix-substitution:
```javascript
// Match .less.css or .scss.css and run appropriate preprocessor
var getSourceFilename = function (name) {
// Strip off the extension for the filename
return name.replace(/\.css$/, '');
};
rule(/\.\w{2,4}\.css$/, getSourceFilename, {async: true}, function () {
// Get appropriate preprocessor for this.source, e.g., foo.less
// Generate a file with filename of this.name, e.g., foo.less.css
});
```
### Passing parameters to jake
Parameters can be passed to Jake two ways: plain arguments, and environment
variables.
To pass positional arguments to the Jake tasks, enclose them in square braces,
separated by commas, after the name of the task on the command-line. For
example, with the following Jakefile:
```javascript
desc('This is an awesome task.');
task('awesome', function (a, b, c) {
console.log(a, b, c);
});
```
You could run `jake` like this:
jake awesome[foo,bar,baz]
And you'd get the following output:
foo bar baz
Note that you *cannot* uses spaces between the commas separating the parameters.
Any parameters passed after the Jake task that contain an equals sign (=) will
be added to process.env.
With the following Jakefile:
```javascript
desc('This is an awesome task.');
task('awesome', function (a, b, c) {
console.log(a, b, c);
console.log(process.env.qux, process.env.frang);
});
```
You could run `jake` like this:
jake awesome[foo,bar,baz] qux=zoobie frang=asdf
And you'd get the following output:
foo bar baz
zoobie asdf
Running `jake` with no arguments runs the default task.
__Note for zsh users__ : you will need to escape the brackets or wrap in single
quotes like this to pass parameters :
jake 'awesome[foo,bar,baz]'
An other solution is to desactivate permannently file-globbing for the `jake`
command. You can do this by adding this line to your `.zshrc` file :
alias jake="noglob jake"
### Cleanup after all tasks run, jake 'complete' event
The base 'jake' object is an EventEmitter, and fires a 'start' event before
running, an 'error' event after an uncaught exception, and a 'complete' event after running all tasks.
This is sometimes useful when a task starts a process which keeps the Node
event-loop running (e.g., a database connection). If you know you want to stop
the running Node process after all tasks have finished, you can set a listener
for the 'complete' event, like so:
```javascript
jake.addListener('complete', function () {
process.exit();
});
```
### Running tasks from within other tasks
Jake supports the ability to run a task from within another task via the
`invoke` and `execute` methods.
The `invoke` method will run the desired task, along with its prerequisites:
```javascript
desc('Calls the foo:bar task and its prerequisites.');
task('invokeFooBar', function () {
// Calls foo:bar and its prereqs
jake.Task['foo:bar'].invoke();
});
```
The `invoke` method will only run the task once, even if you call it repeatedly.
```javascript
desc('Calls the foo:bar task and its prerequisites.');
task('invokeFooBar', function () {
// Calls foo:bar and its prereqs
jake.Task['foo:bar'].invoke();
// Does nothing
jake.Task['foo:bar'].invoke();
});
```
The `execute` method will run the desired task without its prerequisites:
```javascript
desc('Calls the foo:bar task without its prerequisites.');
task('executeFooBar', function () {
// Calls foo:bar without its prereqs
jake.Task['foo:baz'].execute();
});
```
Calling `execute` repeatedly will run the desired task repeatedly.
```javascript
desc('Calls the foo:bar task without its prerequisites.');
task('executeFooBar', function () {
// Calls foo:bar without its prereqs
jake.Task['foo:baz'].execute();
// Can keep running this over and over
jake.Task['foo:baz'].execute();
jake.Task['foo:baz'].execute();
});
```
If you want to run the task and its prerequisites more than once, you can use
`invoke` with the `reenable` method.
```javascript
desc('Calls the foo:bar task and its prerequisites.');
task('invokeFooBar', function () {
// Calls foo:bar and its prereqs
jake.Task['foo:bar'].invoke();
// Does nothing
jake.Task['foo:bar'].invoke();
// Only re-runs foo:bar, but not its prerequisites
jake.Task['foo:bar'].reenable();
jake.Task['foo:bar'].invoke();
});
```
The `reenable` method takes a single Boolean arg, a 'deep' flag, which reenables
the task's prerequisites if set to true.
```javascript
desc('Calls the foo:bar task and its prerequisites.');
task('invokeFooBar', function () {
// Calls foo:bar and its prereqs
jake.Task['foo:bar'].invoke();
// Does nothing
jake.Task['foo:bar'].invoke();
// Re-runs foo:bar and all of its prerequisites
jake.Task['foo:bar'].reenable(true);
jake.Task['foo:bar'].invoke();
});
```
It's easy to pass params on to a sub-task run via `invoke` or `execute`:
```javascript
desc('Passes params on to other tasks.');
task('passParams', function () {
var t = jake.Task['foo:bar'];
// Calls foo:bar, passing along current args
t.invoke.apply(t, arguments);
});
```
### Getting values out of tasks
Passing a value to the `complete` function for async tasks (or simply returning
a value from sync tasks) will set a 'value' property on the completed task. This
same value will also be passed as the task emits its 'complete' event.
After a task is completed, this value will be also available in the '.value'
property on the task. Calling `reenable` on the task will clear this value.
```javascript
task('environment', {async: true}, function () {
// Do some sort of I/O to figure out the environment value
doSomeAsync(function (err, val) {
if (err) { throw err }
complete(val);
});
});
task("someTaskWithEnvViaPrereq", ["envrionment"], function () {
api = jake.Task["envrionment"].value;
console.log(api);
});
task("someTaskWithEnvViaInvoke", {async: true}, function () {
var env = jake.Task["envrionment"];
env.addListener('complete', function (api) {
console.log(api);
complete();
});
env.invoke();
});
```
### Managing asynchrony without prereqs (e.g., when using `invoke`)
You can mix sync and async without problems when using normal prereqs, because
the Jake execution loop takes care of the difference for you. But when you call
`invoke` or `execute`, you have to manage the asynchrony yourself.
Here's a correct working example:
```javascript
task('async1', ['async2'], {async: true}, function () {
console.log('-- async1 start ----------------');
setTimeout(function () {
console.log('-- async1 done ----------------');
complete();
}, 1000);
});
task('async2', {async: true}, function () {
console.log('-- async2 start ----------------');
setTimeout(function () {
console.log('-- async2 done ----------------');
complete();
}, 500);
});
task('init', ['async1', 'async2'], {async: true}, function () {
console.log('-- init start ----------------');
setTimeout(function () {
console.log('-- init done ----------------');
complete();
}, 100);
});
task('default', {async: true}, function () {
console.log('-- default start ----------------');
var init = jake.Task.init;
init.addListener('complete', function () {
console.log('-- default done ----------------');
complete();
});
init.invoke();
});
```
You have to declare the "default" task as asynchronous as well, and call
`complete` on it when "init" finishes. Here's the output:
-- default start ----------------
-- async2 start ----------------
-- async2 done ----------------
-- async1 start ----------------
-- async1 done ----------------
-- init start ----------------
-- init done ----------------
-- default done ----------------
You get what you expect -- "default" starts, the rest runs, and finally
"default" finishes.
### Evented tasks
Tasks are EventEmitters. They can fire 'complete' and 'error' events.
If a task called via `invoke` is asynchronous, you can set a listener on the
'complete' event to run any code that depends on it.
```javascript
desc('Calls the async foo:baz task and its prerequisites.');
task('invokeFooBaz', {async: true}, function () {
var t = jake.Task['foo:baz'];
t.addListener('complete', function () {
console.log('Finished executing foo:baz');
// Maybe run some other code
// ...
// Complete the containing task
complete();
});
// Kick off foo:baz
t.invoke();
});
```
If you want to handle the errors in a task in some specific way, you can set a
listener for the 'error' event, like so:
```javascript
namespace('vronk', function () {
task('groo', function () {
var t = jake.Task['vronk:zong'];
t.addListener('error', function (e) {
console.log(e.message);
});
t.invoke();
});
task('zong', function () {
throw new Error('OMFGZONG');
});
});
```
If no specific listener is set for the "error" event, errors are handled by
Jake's generic error-handling.
### Aborting a task
You can abort a task by calling the `fail` function, and Jake will abort the
currently running task. You can pass a customized error message to `fail`:
```javascript
desc('This task fails.');
task('failTask', function () {
fail('Yikes. Something back happened.');
});
```
You can also pass an optional exit status-code to the fail command, like so:
```javascript
desc('This task fails with an exit-status of 42.');
task('failTaskQuestionCustomStatus', function () {
fail('What is the answer?', 42);
});
```
The process will exit with a status of 42.
Uncaught errors will also abort the currently running task.
### Showing the list of tasks
Passing `jake` the -T or --tasks flag will display the full list of tasks
available in a Jakefile, along with their descriptions:
$ jake -T
jake default # This is the default task.
jake asdf # This is the asdf task.
jake concat.txt # File task, concating two files together
jake failure # Failing task.
jake lookup # Jake task lookup by name.
jake foo:bar # This the foo:bar task
jake foo:fonebone # This the foo:fonebone task
Setting a value for -T/--tasks will filter the list by that value:
$ jake -T foo
jake foo:bar # This the foo:bar task
jake foo:fonebone # This the foo:fonebone task
The list displayed will be all tasks whose namespace/name contain the filter-string.
## Breaking things up into multiple files
Jake will automatically look for files with a .jake extension in a 'jakelib'
directory in your project, and load them (via `require`) after loading your
Jakefile. (The directory name can be overridden using the -J/--jakelibdir
command-line option.)
This allows you to break your tasks up over multiple files -- a good way to do
it is one namespace per file: e.g., a `zardoz` namespace full of tasks in
'jakelib/zardox.jake'.
Note that these .jake files each run in their own module-context, so they don't
have access to each others' data. However, the Jake API methods, and the
task-hierarchy are globally available, so you can use tasks in any file as
prerequisites for tasks in any other, just as if everything were in a single
file.
Environment-variables set on the command-line are likewise also naturally
available to code in all files via process.env.
## File-utils
Since shelling out in Node is an asynchronous operation, Jake comes with a few
useful file-utilities with a synchronous API, that make scripting easier.
The `jake.mkdirP` utility recursively creates a set of nested directories. It
will not throw an error if any of the directories already exists. Here's an example:
```javascript
jake.mkdirP('app/views/layouts');
```
The `jake.cpR` utility does a recursive copy of a file or directory. It takes
two arguments, the file/directory to copy, and the destination. Note that this
command can only copy files and directories; it does not perform globbing (so
arguments like '*.txt' are not possible).
```javascript
jake.cpR(path.join(sourceDir, '/templates'), currentDir);
```
This would copy 'templates' (and all its contents) into `currentDir`.
The `jake.readdirR` utility gives you a recursive directory listing, giving you
output somewhat similar to the Unix `find` command. It only works with a
directory name, and does not perform filtering or globbing.
```javascript
jake.readdirR('pkg');
```
This would return an array of filepaths for all files in the 'pkg' directory,
and all its subdirectories.
The `jake.rmRf` utility recursively removes a directory and all its contents.
```javascript
jake.rmRf('pkg');
```
This would remove the 'pkg' directory, and all its contents.
## Running shell-commands: `jake.exec` and `jake.createExec`
Jake also provides a more general utility function for running a sequence of
shell-commands.
### `jake.exec`
The `jake.exec` command takes an array of shell-command strings, and an optional
callback to run after completing them. Here's an example from Jake's Jakefile,
that runs the tests:
```javascript
desc('Runs the Jake tests.');
task('test', {async: true}, function () {
var cmds = [
'node ./tests/parseargs.js'
, 'node ./tests/task_base.js'
, 'node ./tests/file_task.js'
];
jake.exec(cmds, {printStdout: true}, function () {
console.log('All tests passed.');
complete();
});
desc('Runs some apps in interactive mode.');
task('interactiveTask', {async: true}, function () {
var cmds = [
'node' // Node conosle
, 'vim' // Open Vim
];
jake.exec(cmds, {interactive: true}, function () {
complete();
});
});
```
It also takes an optional options-object, with the following options:
* `interactive` (tasks are interactive, trumps printStdout and
printStderr below, default false)
* `printStdout` (print to stdout, default false)
* `printStderr` (print to stderr, default false)
* `breakOnError` (stop execution on error, default true)
This command doesn't pipe input between commands -- it's for simple execution.
### `jake.createExec` and the evented Exec object
Jake also provides an evented interface for running shell commands. Calling
`jake.createExec` returns an instance of `jake.Exec`, which is an `EventEmitter`
that fires events as it executes commands.
It emits the following events:
* 'cmdStart': When a new command begins to run. Passes one arg, the command
being run.
* 'cmdEnd': When a command finishes. Passes one arg, the command
being run.
* 'stdout': When the stdout for the child-process recieves data. This streams
the stdout data. Passes one arg, the chunk of data.
* 'stderr': When the stderr for the child-process recieves data. This streams
the stderr data. Passes one arg, the chunk of data.
* 'error': When a shell-command exits with a non-zero status-code. Passes two
args -- the error message, and the status code. If you do not set an error
handler, and a command exits with an error-code, Jake will throw the unhandled
error. If `breakOnError` is set to true, the Exec object will emit and 'error'
event after the first error, and stop any further execution.
To begin running the commands, you have to call the `run` method on it. It also
has an `append` method for adding new commands to the list of commands to run.
Here's an example:
```javascript
var ex = jake.createExec(['do_thing.sh']);
ex.addListener('error', function (msg, code) {
if (code == 127) {
console.log("Couldn't find do_thing script, trying do_other_thing");
ex.append('do_other_thing.sh');
}
else {
fail('Fatal error: ' + msg, code);
}
});
ex.run();
```
Using the evented Exec object gives you a lot more flexibility in running shell
commmands. But if you need something more sophisticated, Procstreams
() might be a good option.
## Logging and output
Using the -q/--quiet flag at the command-line will stop Jake from sending its
normal output to standard output. Note that this only applies to built-in output
from Jake; anything you output normally from your tasks will still be displayed.
If you want to take advantage of the -q/--quiet flag in your own programs, you
can use `jake.logger.log` and `jake.logger.error` for displaying output. These
two commands will respect the flag, and suppress output correctly when the
quiet-flag is on.
You can check the current value of this flag in your own tasks by using
`jake.program.opts.quiet`. If you want the output of a `jake.exec` shell-command
to respect the quiet-flag, set your `printStdout` and `printStderr` options to
false if the quiet-option is on:
```javascript
task('echo', {async: true}, function () {
jake.exec(['echo "hello"'], function () {
jake.logger.log('Done.');
complete();
}, {printStdout: !jake.program.opts.quiet});
});
```
### FileList
Jake's FileList takes a list of glob-patterns and file-names, and lazy-creates a
list of files to include. Instead of immediately searching the filesystem to
find the files, a FileList holds the pattern until it is actually used.
When any of the normal JavaScript Array methods (or the `toArray` method) are
called on the FileList, the pending patterns are resolved into an actual list of
file-names. FileList uses the [minimatch](https://github.com/isaacs/minimatch) module.
To build the list of files, use FileList's `include` and `exclude` methods:
```javascript
var list = new jake.FileList();
list.include('foo/*.txt');
list.include(['bar/*.txt', 'README.md']);
list.include('Makefile', 'package.json');
list.exclude('foo/zoobie.txt');
list.exclude(/foo\/src.*.txt/);
console.log(list.toArray());
```
The `include` method can be called either with an array of items, or multiple
single parameters. Items can be either glob-patterns, or individual file-names.
The `exclude` method will prevent files from being included in the list. These
files must resolve to actual files on the filesystem. It can be called either
with an array of items, or mutliple single parameters. Items can be
glob-patterns, individual file-names, string-representations of
regular-expressions, or regular-expression literals.
## PackageTask
When you create a PackageTask, it programmically creates a set of tasks for
packaging up your project for distribution. Here's an example:
```javascript
packageTask('fonebone', 'v0.1.2112', function () {
var fileList = [
'Jakefile'
, 'README.md'
, 'package.json'
, 'lib/*'
, 'bin/*'
, 'tests/*'
];
this.packageFiles.include(fileList);
this.needTarGz = true;
this.needTarBz2 = true;
});
```
This will automatically create a 'package' task that will assemble the specified
files in 'pkg/fonebone-v0.1.2112,' and compress them according to the specified
options. After running `jake package`, you'll have the following in pkg/:
fonebone-v0.1.2112
fonebone-v0.1.2112.tar.bz2
fonebone-v0.1.2112.tar.gz
PackageTask also creates a 'clobber' task that removes the pkg/
directory.
The [PackageTask API
docs](http://mde.github.com/jake/doc/symbols/jake.PackageTask.html) include a
lot more information, including different archiving options.
## TestTask
When you create a TestTask, it programmically creates a simple task for running
tests for your project. The first argument of the constructor is the
project-name (used in the description of the task), and the second argument is a
function that defines the task. It allows you to specifify what files to run as
tests, and what to name the task that gets created (defaults to "test" if
unset).
```javascript
testTask('fonebone', function () {
var fileList = [
'tests/*'
, 'lib/adapters/**/test.js'
];
this.testFiles.include(fileList);
this.testFiles.exclude('tests/helper.js');
this.testName = 'testMainAndAdapters';
});
```
Tests in the specified file should be in the very simple format of
test-functions hung off the export. These tests are converted into Jake tasks
which Jake then runs normally.
If a test needs to run asynchronously, simply define the test-function with a
single argument, a callback. Jake will define this as an asynchronous task, and
will wait until the callback is called in the test function to run the next test.
If you name your test 'before', it will run before any of the other tests you
export. You can use it for test-setup. If you name a test 'after', it will run
after all the other tests have finished. You can use it for teardown. The
'before' and 'after' will only run once per test module -- *not* before and
after each test.
Here's an example test-file:
```javascript
var assert = require('assert')
, tests;
tests = {
'before': function () {
// Do some setup here
}
, 'after': function () {
// Do some teardown here
}
, 'sync test': function () {
// Assert something
assert.ok(true);
}
, 'async test': function (next) {
// Assert something else
assert.ok(true);
// Won't go next until this is called
next();
}
, 'another sync test': function () {
// Assert something else
assert.ok(true);
}
};
module.exports = tests;
```
Jake's tests are also a good example of use of a TestTask.
## WatchTask
When you create a WatchTask, it will watch a directory of files for changes, and
run a task or set of tasks anytime there's a change.
```javascript
// Assumes there's an 'assets' task
watchTask(['assets'], function () {
this.watchFiles.include([
'./**/*.ejs'
]);
});
```
Run `jake watch` to start up the WatchTask.
By default, it will watch the current directory for these files:
```javascript
[ './**/*.js'
, './**/*.coffee'
, './**/*.css'
, './**/*.less'
, './**/*.scss'
]
```
It will exclude these files:
```javascript
[ 'node_modules/**'
, '.git/**'
]
```
The list of watched files is in a FileList, with the normal `include`/`exclude`
API.
## NpmPublishTask
The NpmPublishTask builds on top of PackageTask to allow you to do a version
tump of your project, package it, and publish it to NPM. Define the task with
your project's name, and call `include`/`exclude` on the `packageFiles` FileList
to create the list of files you want packaged and published to NPM.
Here's an example from Jake's Jakefile:
```javascript
npmPublishTask('jake', function () {
this.packageFiles.include([
'Makefile'
, 'Jakefile'
, 'README.md'
, 'package.json'
, 'lib/**'
, 'bin/**'
, 'test/**'
]);
this.packageFiles.exclude([
'test/tmp'
]);
});
```
The NpmPublishTask will automatically create a `publish` task which performs the
following steps:
1. Bump the version number in your package.json
2. Commit change in git, push it to GitHub
3. Create a git tag for the version
4. Push the tag to GitHub
5. Package the new version of your project
6. Publish it to NPM
7. Clean up the package
If you want to publish to a private NPM repository, you can specify a custom publishing command:
```javascript
npmPublishTask('jake', function () {
this.packageFiles.include([
, 'index.js'
, 'package.json'
]);
// Publishes using the gemfury cli
// `%filename` will be replaced with the package filename
this.publishCmd = 'fury push %filename';
});
```
## CoffeeScript Jakefiles
Jake can also handle Jakefiles in CoffeeScript. Be sure to make it
Jakefile.coffee so Jake knows it's in CoffeeScript.
Here's an example:
```coffeescript
util = require('util')
desc 'This is the default task.'
task 'default', (params) ->
console.log 'Ths is the default task.'
console.log(util.inspect(arguments))
jake.Task['new'].invoke []
task 'new', ->
console.log 'ello from new'
jake.Task['foo:next'].invoke ['param']
namespace 'foo', ->
task 'next', (param) ->
console.log 'ello from next with param: ' + param
```
## Related projects
James Coglan's "Jake":
Confusingly, this is a Ruby tool for building JavaScript packages from source code.
280 North's Jake:
This is also a JavaScript port of Rake, which runs on the Narwhal platform.
### License
Licensed under the Apache License, Version 2.0
()
jake-0.7.4/bin/ 0000775 0000000 0000000 00000000000 12240565131 0013203 5 ustar 00root root 0000000 0000000 jake-0.7.4/bin/cli.js 0000775 0000000 0000000 00000001421 12240565131 0014311 0 ustar 00root root 0000000 0000000 #!/usr/bin/env node
/*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Load `jake` global
require('../lib/jake');
var args = process.argv.slice(2);
jake.run.apply(jake, args);
jake-0.7.4/changelog.md 0000664 0000000 0000000 00000003610 12240565131 0014704 0 ustar 00root root 0000000 0000000 ### 0.7
+ Updated API for include/exclude in NPMPublishTask
+ beforeEach/afterEach for TaskTask
+ Custom publishing command for NPMPublishTask
+ FileList fixes for Windows
+ Fixed output for stdout/stderr when piped
### 0.6
+ Rule, for generating file-tasks on the fly
+ WatchTask, for running tasks on file/directory changes
+ Return values for tasks
+ Multiple Git branches for NpmPublishTask
+ Better API for including/excluding files from NpmPublishTask
+ JSHinted codebase
### 0.5
+ Interactive exec
+ Faster FileTask execution
+ Better Windows support for FileList pathnames
+ Node v0.10 compatibility (fixed their broken PPI)
+ Docs for async management with manual invocation
### 0.4
+ Embeddable Jake, non-CLI use
### 0.3
+ TestTask, for minimal test-running
+ Chaining support for FileList API
+ Node v0.8 compatibility
+ Migrated FileUtils into 'utilities' NPM library
+ `namespace` appends instead of overwriting
+ Numerous PackageTask archiving fixes
### 0.2
+ NpmPublishTask, for automating NPM publishing
+ FileUtils, for sync filesystem manipulation
+ Evented tasks
+ Streamed output from jake.exec
+ Numerous Windows fixes for jake.exec
+ Massive refactor of task/file-task/directory-task
+ Shit-ton of new tests
### 0.1
+ FileTask, DirectoryTask
+ PackageTask, for easy project packaging
+ FileList, for lazy-access of long lists of filenames, pattern-matching
+ zsh support
+ Multiple targets from CLI
+ 'async' Boolean to options object
+ Always-make flag
+ -T for listing tasks
+ Passed params from CLI
+ `invoke`, `execute`, `reenable` methods on tasks
+ Custom exit-status for fail
+ Recursive directory-search for Jakefile
+ Switch from node-glob to minimatch for globbing
+ Added proper license notice
+ Basic suite of integration tests
+ Added package.json
+ Coffeescript support
### v0.0
+ Use Rake-style syntax instead of object-literal to define tasks
+ jake main module as basic task-runner
jake-0.7.4/jake 0000664 0000000 0000000 00000000305 12240565131 0013266 0 ustar 00root root 0000000 0000000 #!/bin/sh
if [ -x "`dirname "$0"`/node.exe" ]; then
"`dirname "$0"`/node.exe" "`dirname "$0"`/node_modules/jake/bin/cli.js" "$@"
else
node "`dirname "$0"`/node_modules/jake/bin/cli.js" "$@"
fi
jake-0.7.4/jake.bat 0000664 0000000 0000000 00000000105 12240565131 0014031 0 ustar 00root root 0000000 0000000 @ECHO OFF
@"%~dp0node.exe" "%~dp0/node_modules/jake/bin/cli.js" %*
jake-0.7.4/lib/ 0000775 0000000 0000000 00000000000 12240565131 0013201 5 ustar 00root root 0000000 0000000 jake-0.7.4/lib/api.js 0000664 0000000 0000000 00000024554 12240565131 0014322 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var api = new (function () {
/**
@name task
@static
@function
@description Creates a Jake Task
`
@param {String} name The name of the Task
@param {Array} [prereqs] Prerequisites to be run before this task
@param {Function} [action] The action to perform for this task
@param {Object} [opts]
@param {Boolean} [opts.asyc=false] Perform this task asynchronously.
If you flag a task with this option, you must call the global
`complete` method inside the task's action, for execution to proceed
to the next task.
@example
desc('This is the default task.');
task('default', function (params) {
console.log('This is the default task.');
});
desc('This task has prerequisites.');
task('hasPrereqs', ['foo', 'bar', 'baz'], function (params) {
console.log('Ran some prereqs first.');
});
desc('This is an asynchronous task.');
task('asyncTask', function () {
setTimeout(complete, 1000);
}, {async: true});
*/
this.task = function (name, prereqs, action, opts) {
var args = Array.prototype.slice.call(arguments)
, type
, createdTask;
args.unshift('task');
createdTask = jake.createTask.apply(global, args);
jake.currentTaskDescription = null;
return createdTask;
};
/**
@name rule
@static
@function
@description Creates a Jake Suffix Rule
`
@param {String} pattern The suffix name of the objective
@param {String} source The suffix name of the objective
@param {Array} [prereqs] Prerequisites to be run before this task
@param {Function} [action] The action to perform for this task
@param {Object} [opts]
@param {Boolean} [opts.asyc=false] Perform this task asynchronously.
If you flag a task with this option, you must call the global
`complete` method inside the task's action, for execution to proceed
to the next task.
@example
desc('This is a rule, which does not support namespace or pattern.');
rule('.o', '.c', {async: true}, function () {
var cmd = util.format('gcc -o %s %s', this.name, this.source);
jake.exec([cmd], function () {
complete();
}, {printStdout: true});
});
desc('This rule has prerequisites.');
rule('.o', '.c', ['util.h'], {async: true}, function () {
var cmd = util.format('gcc -o %s %s', this.name, this.source);
jake.exec([cmd], function () {
complete();
}, {printStdout: true});
});
desc('This is a rule with patterns.');
rule('%.o', '%.c', {async: true}, function () {
var cmd = util.format('gcc -o %s %s', this.name, this.source);
jake.exec([cmd], function () {
complete();
}, {printStdout: true});
});
desc('This is another rule with patterns.');
rule('obj/%.o', 'src/%.c', {async: true}, function () {
var cmd = util.format('gcc -o %s %s', this.name, this.source);
jake.exec([cmd], function () {
complete();
}, {printStdout: true});
});
desc('This is an example with chain rules.');
rule('%.pdf', '%.dvi', {async: true}, function () {
var cmd = util.format('dvipdfm %s',this.source);
jake.exec([cmd], function () {
complete();
}, {printStdout: true});
});
rule('%.dvi', '%.tex', {async: true}, function () {
var cmd = util.format('latex %s',this.source);
jake.exec([cmd], function () {
complete();
}, {printStdout: true});
});
desc('This rule has a namespace.');
task('default', ['debug:obj/main.o]);
namespace('debug', {async: true}, function() {
rule('obj/%.o', 'src/%.c', function () {
// ...
});
}
*/
this.rule = function () {
var args = Array.prototype.slice.call(arguments)
, arg
, pattern = args.shift()
, source = args.shift()
, prereqs = []
, action = function () {}
, opts = {}
, key = pattern.toString(); // May be a RegExp
while ((arg = args.shift())) {
if (typeof arg == 'function') {
action = arg;
}
else if (Array.isArray(arg)) {
prereqs = arg;
}
else {
opts = arg;
}
}
jake.currentNamespace.rules[key] = new jake.Rule({
pattern: pattern
, source: source
, prereqs: prereqs
, action: action
, opts: opts
, desc: jake.currentTaskDescription
, ns: jake.currentNamespace
});
jake.currentTaskDescription = null;
};
/**
@name directory
@static
@function
@description Creates a Jake DirectoryTask. Can be used as a prerequisite
for FileTasks, or for simply ensuring a directory exists for use with a
Task's action.
`
@param {String} name The name of the DiretoryTask
@example
// Creates the package directory for distribution
directory('pkg');
*/
this.directory = function (name) {
var args = Array.prototype.slice.call(arguments)
, createdTask;
args.unshift('directory');
createdTask = jake.createTask.apply(global, args);
jake.currentTaskDescription = null;
return createdTask;
};
/**
@name file
@static
@function
@description Creates a Jake FileTask.
`
@param {String} name The name of the FileTask
@param {Array} [prereqs] Prerequisites to be run before this task
@param {Function} [action] The action to create this file, if it doesn't
exist already.
@param {Object} [opts]
@param {Array} [opts.asyc=false] Perform this task asynchronously.
If you flag a task with this option, you must call the global
`complete` method inside the task's action, for execution to proceed
to the next task.
*/
this.file = function (name, prereqs, action, opts) {
var args = Array.prototype.slice.call(arguments)
, createdTask;
args.unshift('file');
createdTask = jake.createTask.apply(global, args);
jake.currentTaskDescription = null;
return createdTask;
};
/**
@name desc
@static
@function
@description Creates a description for a Jake Task (or FileTask,
DirectoryTask). When invoked, the description that iscreated will
be associated with whatever Task is created next.
`
@param {String} description The description for the Task
*/
this.desc = function (description) {
jake.currentTaskDescription = description;
};
/**
@name namespace
@static
@function
@description Creates a namespace which allows logical grouping
of tasks, and prevents name-collisions with task-names. Namespaces
can be nested inside of other namespaces.
`
@param {String} name The name of the namespace
@param {Function} scope The enclosing scope for the namespaced tasks
@example
namespace('doc', function () {
task('generate', ['doc:clobber'], function () {
// Generate some docs
});
task('clobber', function () {
// Clobber the doc directory first
});
});
*/
this.namespace = function (name, closure) {
var curr = jake.currentNamespace
, ns = curr.childNamespaces[name] || new jake.Namespace(name, curr);
curr.childNamespaces[name] = ns;
jake.currentNamespace = ns;
closure();
jake.currentNamespace = curr;
jake.currentTaskDescription = null;
return ns;
};
/**
@name complete
@static
@function
@description Complets an asynchronous task, allowing Jake's
execution to proceed to the next task
`
@example
task('generate', ['doc:clobber'], function () {
exec('./generate_docs.sh', function (err, stdout, stderr) {
if (err || stderr) {
fail(err || stderr);
}
else {
console.log(stdout);
complete();
}
});
}, {async: true});
*/
this.complete = function (val) {
var current = jake._invocationChain.pop();
if (current) {
current.complete(val);
}
};
/**
@name fail
@static
@function
@description Causes Jake execution to abort with an error.
Allows passing an optional error code, which will be used to
set the exit-code of exiting process.
`
@param {Error|String} err The error to thow when aborting execution.
If this argument is an Error object, it will simply be thrown. If
a String, it will be used as the error-message. (If it is a multi-line
String, the first line will be used as the Error message, and the
remaining lines will be used as the error-stack.)
@example
task('createTests, function () {
if (!fs.existsSync('./tests')) {
fail('Test directory does not exist.');
}
else {
// Do some testing stuff ...
}
});
*/
this.fail = function (err, code) {
var msg
, errObj;
if (code) {
jake.errorCode = code;
}
if (err) {
if (typeof err == 'string') {
// Use the initial or only line of the error as the error-message
// If there was a multi-line error, use the rest as the stack
msg = err.split('/n');
errObj = new Error(msg.shift());
if (msg.length) {
errObj.stack = msg.join('\n');
}
throw errObj;
}
else if (err instanceof Error) {
throw err;
}
else {
throw new Error(err.toString());
}
}
else {
throw new Error();
}
};
this.packageTask = function (name, version, definition) {
return new jake.PackageTask(name, version, definition);
};
this.npmPublishTask = function (name, definition) {
return new jake.NpmPublishTask(name, definition);
};
this.watchTask = function (taskNames, definition) {
return new jake.WatchTask(taskNames, definition);
};
this.testTask = function (name, definition) {
return new jake.TestTask(name, definition);
};
})();
module.exports = api;
jake-0.7.4/lib/file_list.js 0000664 0000000 0000000 00000017616 12240565131 0015524 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var fs = require('fs')
, path = require('path')
, minimatch = require('minimatch')
, utils = require('utilities')
, globSync;
globSync = function (pat) {
var dirname = jake.basedir(pat)
, files
, matches;
try {
files = jake.readdirR(dirname).map(function(file){
return file.replace(/\\/g, '/');
});
}
// Bail if path doesn't exist -- assume no files
catch(e) {}
if (files) {
pat = path.normalize(pat);
// Hack, Minimatch doesn't support backslashes in the pattern
// https://github.com/isaacs/minimatch/issues/7
pat = pat.replace(/\\/g, '/');
matches = minimatch.match(files, pat, {});
}
return matches || [];
};
// Constants
// ---------------
// List of all the builtin Array methods we want to override
var ARRAY_METHODS = Object.getOwnPropertyNames(Array.prototype)
// Array methods that return a copy instead of affecting the original
, SPECIAL_RETURN = {
'concat': true
, 'slice': true
, 'filter': true
, 'map': true
}
// Default file-patterns we want to ignore
, DEFAULT_IGNORE_PATTERNS = [
/(^|[\/\\])CVS([\/\\]|$)/
, /(^|[\/\\])\.svn([\/\\]|$)/
, /(^|[\/\\])\.git([\/\\]|$)/
, /\.bak$/
, /~$/
]
// Ignore core files
, DEFAULT_IGNORE_FUNCS = [
function (name) {
var isDir = false
, stats;
try {
stats = fs.statSync(name);
isDir = stats.isDirectory();
}
catch(e) {}
return (/(^|[\/\\])core$/).test(name) && !isDir;
}
];
var FileList = function () {
var self = this
, wrap;
// List of glob-patterns or specific filenames
this.pendingAdd = [];
// Switched to false after lazy-eval of files
this.pending = true;
// Used to calculate exclusions from the list of files
this.excludes = {
pats: DEFAULT_IGNORE_PATTERNS.slice()
, funcs: DEFAULT_IGNORE_FUNCS.slice()
, regex: null
};
this.items = [];
// Wrap the array methods with the delegates
wrap = function (prop) {
var arr;
self[prop] = function () {
if (self.pending) {
self.resolve();
}
if (typeof self.items[prop] == 'function') {
// Special method that return a copy
if (SPECIAL_RETURN[prop]) {
arr = self.items[prop].apply(self.items, arguments);
return FileList.clone(self, arr);
}
else {
return self.items[prop].apply(self.items, arguments);
}
}
else {
return self.items[prop];
}
};
};
for (var i = 0, ii = ARRAY_METHODS.length; i < ii; i++) {
wrap(ARRAY_METHODS[i]);
}
// Include whatever files got passed to the constructor
this.include.apply(this, arguments);
// Fix constructor linkage
this.constructor = FileList;
};
FileList.prototype = new (function () {
var globPattern = /[*?\[\{]/;
var _addMatching = function (pat) {
var matches = globSync(pat);
this.items = this.items.concat(matches);
}
, _resolveAdd = function (name) {
if (globPattern.test(name)) {
_addMatching.call(this, name);
}
else {
this.push(name);
}
}
, _calculateExcludeRe = function () {
var pats = this.excludes.pats
, pat
, excl = []
, matches = [];
for (var i = 0, ii = pats.length; i < ii; i++) {
pat = pats[i];
if (typeof pat == 'string') {
// Glob, look up files
if (/[*?]/.test(pat)) {
matches = globSync(pat);
matches = matches.map(function (m) {
return utils.string.escapeRegExpChars(m);
});
excl = excl.concat(matches);
}
// String for regex
else {
excl.push(utils.string.escapeRegExpChars(pat));
}
}
// Regex, grab the string-representation
else if (pat instanceof RegExp) {
excl.push(pat.toString().replace(/^\/|\/$/g, ''));
}
}
if (excl.length) {
this.excludes.regex = new RegExp('(' + excl.join(')|(') + ')');
}
else {
this.excludes.regex = /^$/;
}
}
, _resolveExclude = function () {
var self = this;
_calculateExcludeRe.call(this);
// No `reject` method, so use reverse-filter
this.items = this.items.filter(function (name) {
return !self.shouldExclude(name);
});
};
/**
* Includes file-patterns in the FileList. Should be called with one or more
* pattern for finding file to include in the list. Arguments should be strings
* for either a glob-pattern or a specific file-name, or an array of them
*/
this.include = function () {
var args = Array.isArray(arguments[0]) ? arguments[0] : arguments;
for (var i = 0, ii = args.length; i < ii; i++) {
this.pendingAdd.push(args[i]);
}
return this;
};
/**
* Indicates whether a particular file would be filtered out by the current
* exclusion rules for this FileList.
* @param {String} name The filename to check
* @return {Boolean} Whether or not the file should be excluded
*/
this.shouldExclude = function (name) {
if (!this.excludes.regex) {
_calculateExcludeRe.call(this);
}
var excl = this.excludes;
return excl.regex.test(name) || excl.funcs.some(function (f) {
return !!f(name);
});
};
/**
* Excludes file-patterns from the FileList. Should be called with one or more
* pattern for finding file to include in the list. Arguments can be:
* 1. Strings for either a glob-pattern or a specific file-name
* 2. Regular expression literals
* 3. Functions to be run on the filename that return a true/false
*/
this.exclude = function () {
var args = Array.isArray(arguments[0]) ? arguments[0] : arguments
, arg;
for (var i = 0, ii = args.length; i < ii; i++) {
arg = args[i];
if (typeof arg == 'function' && !(arg instanceof RegExp)) {
this.excludes.funcs.push(arg);
}
else {
this.excludes.pats.push(arg);
}
}
if (!this.pending) {
_resolveExclude.call(this);
}
return this;
};
/**
* Populates the FileList from the include/exclude rules with a list of
* actual files
*/
this.resolve = function () {
var name;
if (this.pending) {
this.pending = false;
while ((name = this.pendingAdd.shift())) {
_resolveAdd.call(this, name);
}
_resolveExclude.call(this);
}
return this;
};
/**
* Convert to a plain-jane array
*/
this.toArray = function () {
// Call slice to ensure lazy-resolution before slicing items
var ret = this.slice().items.slice();
return ret;
};
/**
* Get rid of any current exclusion rules
*/
this.clearExclude = function () {
this.excludes = {
pats: []
, funcs: []
, regex: null
};
return this;
};
})();
// Static method, used to create copy returned by special
// array methods
FileList.clone = function (list, items) {
var clone = new FileList();
if (items) {
clone.items = items;
}
clone.pendingAdd = list.pendingAdd;
clone.pending = list.pending;
for (var p in list.excludes) {
clone.excludes[p] = list.excludes[p];
}
return clone;
};
exports.FileList = FileList;
jake-0.7.4/lib/jake.js 0000664 0000000 0000000 00000021301 12240565131 0014446 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var EventEmitter = require('events').EventEmitter;
// And so it begins
global.jake = new EventEmitter();
var fs = require('fs')
, path = require('path')
, taskNs = require('./task')
, Task = taskNs.Task
, FileTask = taskNs.FileTask
, DirectoryTask = taskNs.DirectoryTask
, Rule = require('./rule').Rule
, Namespace = require('./namespace').Namespace
, api = require('./api')
, utils = require('./utils')
, Program = require('./program').Program
, Loader = require('./loader').Loader
, pkg = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString());
var MAX_RULE_RECURSION_LEVEL = 16;
var Invocation = function (taskName, args) {
this.taskName = taskName;
this.args = args;
};
// Globalize jake and top-level API methods (e.g., `task`, `desc`)
utils.mixin(global, api);
// Copy utils onto base jake
utils.mixin(jake, utils);
// File utils should be aliased directly on base jake as well
utils.mixin(jake, utils.file);
utils.mixin(jake, new (function () {
this._invocationChain = [];
// Private variables
// =================
// Local reference for scopage
var self = this;
// Public properties
// =================
this.version = pkg.version;
// Used when Jake exits with a specific error-code
this.errorCode = undefined;
// Loads Jakefiles/jakelibdirs
this.loader = new Loader();
// Name/value map of all the various tasks defined in a Jakefile.
// Non-namespaced tasks are placed into 'default.'
this.defaultNamespace = new Namespace('default', null);
// 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 = this.defaultNamespace;
// Saves the description created by a 'desc' call that prefaces a
// 'task' call that defines a task.
this.currentTaskDescription = null;
this.program = new Program();
this.FileList = require('./file_list').FileList;
this.PackageTask = require('./package_task').PackageTask;
this.NpmPublishTask = require('./npm_publish_task').NpmPublishTask;
this.WatchTask = require('./watch_task').WatchTask;
this.TestTask = require('./test_task').TestTask;
this.Task = Task;
this.FileTask = FileTask;
this.DirectoryTask = DirectoryTask;
this.Namespace = Namespace;
this.Rule = Rule;
this.parseAllTasks = function () {
var _parseNs = function (name, ns) {
var nsTasks = ns.tasks
, task
, nsNamespaces = ns.childNamespaces
, fullName;
// Iterate through the tasks in each namespace
for (var q in nsTasks) {
task = nsTasks[q];
// Prefix namespaced tasks
fullName = name == 'default' ? q : name + ':' + q;
// Save with 'taskname' or 'namespace:taskname' key
task.fullName = fullName;
jake.Task[fullName] = task;
}
for (var p in nsNamespaces) {
fullName = (name == 'default') ? p : name + ':' + p;
_parseNs(fullName, nsNamespaces[p]);
}
};
_parseNs('default', jake.defaultNamespace);
};
/**
* Displays the list of descriptions avaliable for tasks defined in
* a Jakefile
*/
this.showAllTaskDescriptions = function (f) {
var p
, maxTaskNameLength = 0
, task
, str = ''
, padding
, name
, descr
, filter = typeof f == 'string' ? f : null;
for (p in jake.Task) {
task = jake.Task[p];
// Record the length of the longest task name -- used for
// pretty alignment of the task descriptions
maxTaskNameLength = p.length > maxTaskNameLength ?
p.length : maxTaskNameLength;
}
// Print out each entry with descriptions neatly aligned
for (p in jake.Task) {
if (filter && p.indexOf(filter) == -1) {
continue;
}
task = jake.Task[p];
name = '\033[32m' + p + '\033[39m ';
// Create padding-string with calculated length
padding = (new Array(maxTaskNameLength - p.length + 2)).join(' ');
descr = task.description;
if (descr) {
descr = '\033[90m # ' + descr + '\033[39m \033[37m \033[39m';
console.log('jake ' + name + padding + descr);
}
}
};
this.createTask = function () {
var args = Array.prototype.slice.call(arguments)
, arg
, obj
, task
, type
, name
, action
, opts = {}
, prereqs = [];
type = args.shift();
// name, [deps], [action]
// Name (string) + deps (array) format
if (typeof args[0] == 'string') {
name = args.shift();
if (Array.isArray(args[0])) {
prereqs = args.shift();
}
}
// name:deps, [action]
// Legacy object-literal syntax, e.g.: {'name': ['depA', 'depB']}
else {
obj = args.shift();
for (var p in obj) {
prereqs = prereqs.concat(obj[p]);
name = p;
}
}
// Optional opts/callback or callback/opts
while ((arg = args.shift())) {
if (typeof arg == 'function') {
action = arg;
}
else {
opts = arg;
}
}
task = jake.currentNamespace.resolveTask(name);
if (task && !action) {
// Task already exists and no action, just update prereqs, and return it.
task.prereqs = task.prereqs.concat(prereqs);
return task;
}
switch (type) {
case 'directory':
action = function () {
jake.mkdirP(name);
};
task = new DirectoryTask(name, prereqs, action, opts);
break;
case 'file':
task = new FileTask(name, prereqs, action, opts);
break;
default:
task = new Task(name, prereqs, action, opts);
}
if (jake.currentTaskDescription) {
task.description = jake.currentTaskDescription;
jake.currentTaskDescription = null;
}
jake.currentNamespace.tasks[name] = task;
task.namespace = jake.currentNamespace;
// FIXME: Should only need to add a new entry for the current
// task-definition, not reparse the entire structure
jake.parseAllTasks();
return task;
};
this.attemptRule = function (name, ns, level) {
var prereqRule
, prereq;
if (level > MAX_RULE_RECURSION_LEVEL) {
return null;
}
// Check Rule
prereqRule = ns.matchRule(name);
if (prereqRule) {
prereq = prereqRule.createTask(name, level);
}
return prereq || null;
};
this.createPlaceholderFileTask = function (name, namespace) {
var nsPath = ''
, filePath = name.split(':').pop() // Strip any namespace
, parts
, fileTaskName
, task
, stats;
if (namespace) {
if (typeof namespace == 'string') {
nsPath = namespace;
}
else {
nsPath = namespace.path;
}
}
parts = nsPath.length ? nsPath.split(':') : [];
parts.push(filePath);
fileTaskName = parts.join(':');
task = jake.Task[fileTaskName];
// If there's not already an existing dummy FileTask for it,
// create one
if (!task) {
// Create a dummy FileTask only if file actually exists
if (fs.existsSync(filePath)) {
stats = fs.statSync(filePath);
task = new jake.FileTask(filePath);
task.fullName = fileTaskName;
task.modTime = stats.mtime;
task.dummy = true;
// Put this dummy Task in the global Tasks list so
// modTime will be eval'd correctly
jake.Task[fileTaskName] = task;
}
}
return task || null;
};
this.init = function () {
var self = this;
process.addListener('uncaughtException', function (err) {
self.program.handleErr(err);
});
};
this.run = function () {
var args = Array.prototype.slice.call(arguments)
, program = this.program
, loader = this.loader
, preempt
, opts;
program.parseArgs(args);
program.init();
preempt = program.firstPreemptiveOption();
if (preempt) {
preempt();
}
else {
opts = program.opts;
// Load Jakefile and jakelibdir files
loader.loadFile(opts.jakefile);
loader.loadDirectory(opts.jakelibdir);
program.run();
}
};
})());
module.exports = jake;
jake-0.7.4/lib/loader.js 0000664 0000000 0000000 00000005062 12240565131 0015010 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var path = require('path')
, fs = require('fs')
, existsSync = typeof fs.existsSync == 'function' ?
fs.existsSync : path.existsSync
, utils = require('utilities')
, CoffeeScript
, Loader;
Loader = function () {
var JAKEFILE_PAT = /\.jake(\.js|\.coffee)?$/;
var _requireCoffee = function () {
try {
return require('coffee-script');
}
catch (e) {
fail('CoffeeScript is missing! Try `npm install coffee-script`');
}
};
this.loadFile = function (file) {
var jakefile = file ?
file.replace(/\.js$/, '').replace(/\.coffee$/, '') : 'Jakefile'
, fileSpecified = !!file
// Dear God, why?
, isCoffee = false
// Warning, recursive
, exists;
exists = function () {
var cwd = process.cwd();
if (existsSync(jakefile) || existsSync(jakefile + '.js') ||
existsSync(jakefile + '.coffee')) {
return true;
}
if (!fileSpecified) {
process.chdir("..");
if (cwd === process.cwd()) {
return false;
}
return exists();
}
};
if (!exists()) {
fail('No Jakefile. Specify a valid path with -f/--jakefile, ' +
'or place one in the current directory.');
}
isCoffee = existsSync(jakefile + '.coffee');
if (isCoffee) {
CoffeeScript = _requireCoffee();
}
require(utils.file.absolutize(jakefile));
};
this.loadDirectory = function (d) {
var dirname = d || 'jakelib'
, dirlist;
dirname = utils.file.absolutize(dirname);
if (existsSync(dirname)) {
dirlist = fs.readdirSync(dirname);
dirlist.forEach(function (filePath) {
if (JAKEFILE_PAT.test(filePath)) {
if (/\.coffee$/.test(filePath)) {
CoffeeScript = _requireCoffee();
}
require(path.join(dirname, filePath));
}
});
}
};
};
module.exports.Loader = Loader;
jake-0.7.4/lib/namespace.js 0000664 0000000 0000000 00000003242 12240565131 0015474 0 ustar 00root root 0000000 0000000
var Namespace = function (name, parentNamespace) {
this.name = name;
this.parentNamespace = parentNamespace;
this.childNamespaces = {};
this.tasks = {};
this.rules = {};
this.path = this.getPath();
};
Namespace.prototype = new (function () {
this.resolveTask = function(relativeName) {
var parts = relativeName.split(':')
, name = parts.pop()
, ns = this.resolveNamespace(parts.join(':'));
return (ns && ns.tasks[name]) ||
(this.parentNamespace &&
this.parentNamespace.resolveTask(relativeName));
};
this.resolveNamespace = function(relativeName) {
var parts = relativeName.split(':')
, ns;
if (!relativeName) {
return this;
}
ns = this;
for (var i = 0, ii = parts.length; ns && i < ii; i++) {
ns = ns.childNamespaces[parts[i]];
}
return (ns || (this.parentNamespace &&
this.parentNamespace.resolveNamespace(relativeName)));
};
this.matchRule = function(relativeName) {
var parts = relativeName.split(':')
, name = parts.pop()
, ns = this.resolveNamespace(parts.join(':'))
, rules = ns ? ns.rules : []
, r
, match;
for (var p in rules) {
r = rules[p];
if (r.match(relativeName)) {
match = r;
}
}
return (ns && match) ||
(this.parentNamespace &&
this.parentNamespace.matchRule(relativeName));
};
this.getPath = function () {
var parts = []
, next = this;
while (!!next) {
parts.push(next.name);
next = next.parentNamespace;
}
parts.pop(); // Remove 'default'
return parts.reverse().join(':');
};
})();
module.exports.Namespace = Namespace;
jake-0.7.4/lib/npm_publish_task.js 0000664 0000000 0000000 00000015050 12240565131 0017102 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var fs = require('fs')
, exec = require('child_process').exec
, utils = require('utilities')
, currDir = process.cwd()
, FileList = require('./file_list').FileList;
var NpmPublishTask = function () {
var args = Array.prototype.slice.call(arguments)
, arg
, definition
, createDef = function (arg) {
return function () {
this.packageFiles.include(arg);
};
};
this.name = args.shift();
while ((arg = args.pop())) {
if (typeof arg == 'function') {
definition = arg;
}
else if (Array.isArray(arg) || typeof arg == 'string') {
definition = createDef(arg);
}
}
this.packageFiles = new FileList();
this.publishCmd = 'npm publish %filename';
if (typeof definition == 'function') {
definition.call(this);
}
this.define();
};
NpmPublishTask.prototype = new (function () {
var _currentBranch = null;
var getPackage = function () {
var pkg = JSON.parse(fs.readFileSync(process.cwd() + '/package.json').toString());
return pkg;
}
, getPackageVersionNumber = function () {
return getPackage().version;
};
this.define = function () {
var self = this;
namespace('npm', function () {
task('fetchTags', {async: true}, function () {
// Make sure local tags are up to date
var cmds = [
'git fetch --tags'
];
jake.exec(cmds, function () {
console.log('Fetched remote tags.');
complete();
});
});
task('getCurrentBranch', {async: true}, function () {
// Figure out what branch to push to
exec('git symbolic-ref --short HEAD',
function (err, stdout, stderr) {
if (err) {
fail(err);
}
if (stderr) {
fail(new Error(stderr));
}
if (!stdout) {
fail(new Error('No current Git branch found'));
}
_currentBranch = utils.string.trim(stdout);
console.log('On branch ' + _currentBranch);
complete();
});
});
task('version', {async: true}, function () {
// Only bump, push, and tag if the Git repo is clean
exec('git status --porcelain --untracked-files=no',
function (err, stdout, stderr) {
var cmds
, path
, pkg
, version
, arr
, patch
, message;
if (err) {
fail(err);
}
if (stderr) {
fail(new Error(stderr));
}
if (stdout.length) {
fail(new Error('Git repository is not clean.'));
}
// Grab the current version-string
path = process.cwd() + '/package.json';
pkg = getPackage();
version = pkg.version;
// Increment the patch-number for the version
arr = version.split('.');
patch = parseInt(arr.pop(), 10) + 1;
arr.push(patch);
version = arr.join('.');
// New package-version
pkg.version = version;
// Commit-message
message = 'Version ' + version;
// Update package.json with the new version-info
fs.writeFileSync(path, JSON.stringify(pkg, true, 2));
cmds = [
'git commit package.json -m "' + message + '"'
, 'git push origin ' + _currentBranch
, 'git tag -a v' + version + ' -m "' + message + '"'
, 'git push --tags'
];
jake.exec(cmds, function () {
var version = getPackageVersionNumber();
console.log('Bumped version number to v' + version + '.');
complete();
});
});
});
task('definePackage', function () {
var version = getPackageVersionNumber()
, t;
t = new jake.PackageTask(self.name, 'v' + version, function () {
// Replace the PackageTask's FileList with the NPMPublishTask's FileList
this.packageFiles = self.packageFiles;
this.needTarGz = true;
});
});
task('package', {async: true}, function () {
var definePack = jake.Task['npm:definePackage']
, pack = jake.Task.package
, version = getPackageVersionNumber();
// May have already been run
definePack.reenable(true);
definePack.addListener('complete', function () {
pack.addListener('complete', function () {
console.log('Created package for ' + self.name + ' v' + version);
complete();
});
pack.invoke();
});
definePack.invoke();
});
task('publish', {async: true}, function () {
var version = getPackageVersionNumber()
, cmds
, filename
, cmd;
console.log('Publishing ' + self.name + ' v' + version);
filename = 'pkg/' + self.name + '-v' + version + '.tar.gz'
cmd = self.publishCmd.replace(/%filename/gi, filename);
cmds = [
cmd
];
// Hackity hack -- NPM publish sometimes returns errror like:
// Error sending version data\nnpm ERR!
// Error: forbidden 0.2.4 is modified, should match modified time
setTimeout(function () {
jake.exec(cmds, function () {
console.log('Published to NPM');
complete();
}, {stdout: true});
}, 5000);
});
task('cleanup', function () {
var clobber = jake.Task.clobber;
clobber.reenable(true);
clobber.invoke();
console.log('Cleaned up package');
});
});
desc('Bump version-number, package, and publish to NPM.');
task('publish', ['npm:fetchTags', 'npm:getCurrentBranch', 'npm:version',
'npm:package', 'npm:publish', 'npm:cleanup'], function () {});
jake.Task['npm:definePackage'].invoke();
};
})();
jake.NpmPublishTask = NpmPublishTask;
exports.NpmPublishTask = NpmPublishTask;
jake-0.7.4/lib/package_task.js 0000664 0000000 0000000 00000023171 12240565131 0016160 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var path = require('path')
, fs = require('fs')
, exec = require('child_process').exec
, FileList = require('./file_list').FileList;
/**
@name jake
@namespace jake
*/
/**
@name jake.PackageTask
@constructor
@description Instantiating a PackageTask creates a number of Jake
Tasks that make packaging and distributing your software easy.
@param {String} name The name of the project
@param {String} version The current project version (will be
appended to the project-name in the package-archive
@param {Function} definition Defines the contents of the package,
and format of the package-archive. Will be executed on the instantiated
PackageTask (i.e., 'this', will be the PackageTask instance),
to set the various instance-propertiess.
@example
var t = new jake.PackageTask('rous', 'v' + version, function () {
var files = [
'Capfile'
, 'Jakefile'
, 'README.md'
, 'package.json'
, 'app/*'
, 'bin/*'
, 'config/*'
, 'lib/*'
, 'node_modules/*'
];
this.packageFiles.include(files);
this.packageFiles.exclude('node_modules/foobar');
this.needTarGz = true;
});
*/
var PackageTask = function (name, version, definition) {
/**
@name jake.PackageTask#name
@public
@type {String}
@description The name of the project
*/
this.name = name;
/**
@name jake.PackageTask#version
@public
@type {String}
@description The project version-string
*/
this.version = version;
/**
@name jake.PackageTask#version
@public
@type {String='pkg'}
@description The directory-name to use for packaging the software
*/
this.packageDir = 'pkg';
/**
@name jake.PackageTask#packageFiles
@public
@type {jake.FileList}
@description The list of files and directories to include in the
package-archive
*/
this.packageFiles = new FileList();
/**
@name jake.PackageTask#needTar
@public
@type {Boolean=false}
@description If set to true, uses the `tar` utility to create
a gzip .tgz archive of the package
*/
this.needTar = false;
/**
@name jake.PackageTask#needTar
@public
@type {Boolean=false}
@description If set to true, uses the `tar` utility to create
a gzip .tar.gz archive of the package
*/
this.needTarGz = false;
/**
@name jake.PackageTask#needTarBz2
@public
@type {Boolean=false}
@description If set to true, uses the `tar` utility to create
a bzip2 .bz2 archive of the package
*/
this.needTarBz2 = false;
/**
@name jake.PackageTask#needJar
@public
@type {Boolean=false}
@description If set to true, uses the `jar` utility to create
a .jar archive of the package
*/
this.needJar = false;
/**
@name jake.PackageTask#needZip
@public
@type {Boolean=false}
@description If set to true, uses the `zip` utility to create
a .zip archive of the package
*/
this.needZip = false;
/**
@name jake.PackageTask#manifestFile
@public
@type {String=null}
@description Can be set to point the `jar` utility at a manifest
file to use in a .jar archive. If unset, one will be automatically
created by the `jar` utility. This path should be relative to the
root of the package directory (this.packageDir above, likely 'pkg')
*/
this.manifestFile = null;
/**
@name jake.PackageTask#tarCommand
@public
@type {String='tar'}
@description The shell-command to use for creating tar archives.
*/
this.tarCommand = 'tar';
/**
@name jake.PackageTask#jarCommand
@public
@type {String='jar'}
@description The shell-command to use for creating jar archives.
*/
this.jarCommand = 'jar';
/**
@name jake.PackageTask#zipCommand
@public
@type {String='zip'}
@description The shell-command to use for creating zip archives.
*/
this.zipCommand = 'zip';
/**
@name jake.PackageTask#archiveChangeDir
@public
@type {String=null}
@description Equivalent to the '-C' command for the `tar` and `jar`
commands. ("Change to this directory before adding files.")
*/
this.archiveChangeDir = null;
/**
@name jake.PackageTask#archiveContentDir
@public
@type {String=null}
@description Specifies the files and directories to include in the
package-archive. If unset, this will default to the main package
directory -- i.e., name + version.
*/
this.archiveContentDir = null;
if (typeof definition == 'function') {
definition.call(this);
}
this.define();
};
PackageTask.prototype = new (function () {
var _compressOpts = {
Tar: {
ext: '.tgz'
, flags: 'cvzf'
, cmd: 'tar'
}
, TarGz: {
ext: '.tar.gz'
, flags: 'cvzf'
, cmd: 'tar'
}
, TarBz2: {
ext: '.tar.bz2'
, flags: 'cvjf'
, cmd: 'tar'
}
, Jar: {
ext: '.jar'
, flags: 'cf'
, cmd: 'jar'
}
, Zip: {
ext: '.zip'
, flags: 'r'
, cmd: 'zip'
}
};
this.define = function () {
var self = this
, packageDirPath = this.packageDirPath()
, compressTaskArr = [];
desc('Build the package for distribution');
task('package', ['clobberPackage', 'buildPackage']);
// Backward-compat alias
task('repackage', ['package']);
task('clobberPackage', function () {
jake.rmRf(self.packageDir, {silent: true});
});
desc('Remove the package');
task('clobber', ['clobberPackage']);
var doCommand = function (p) {
var filename = path.resolve(self.packageDir + '/' + self.packageName() +
_compressOpts[p].ext);
compressTaskArr.push(filename);
file(filename, [packageDirPath], function () {
var cmd
, opts = _compressOpts[p]
// Directory to move to when doing the compression-task
// Changes in the case of zip for emulating -C option
, chdir = self.packageDir
// Save the current dir so it's possible to pop back up
// after compressing
, currDir = process.cwd();
cmd = self[opts.cmd + 'Command'];
cmd += ' -' + opts.flags;
if (opts.cmd == 'jar' && self.manifestFile) {
cmd += 'm';
}
// The name of the archive to create -- use full path
// so compression can be performed from a different dir
// if needed
cmd += ' ' + filename;
if (opts.cmd == 'jar' && self.manifestFile) {
cmd += ' ' + self.manifestFile;
}
// Where to perform the compression -- -C option isn't
// supported in zip, so actually do process.chdir for this
if (self.archiveChangeDir) {
if (opts.cmd == 'zip') {
chdir = path.join(chdir, self.archiveChangeDir);
}
else {
cmd += ' -C ' + self.archiveChangeDir;
}
}
// Where to stick the archive
if (self.archiveContentDir) {
cmd += ' ' + self.archiveContentDir;
}
else {
cmd += ' ' + self.packageName();
}
// Move into the desired dir (usually packageDir) to compress
// Return back up to the current dir after the exec
process.chdir(chdir);
exec(cmd, function (err, stdout, stderr) {
if (err) { throw err; }
// Return back up to the starting directory (see above,
// before exec)
process.chdir(currDir);
complete();
});
}, {async: true});
};
for (var p in _compressOpts) {
if (this['need' + p]) {
doCommand(p);
}
}
task('buildPackage', compressTaskArr, function () {});
directory(this.packageDir);
file(packageDirPath, this.packageFiles, function () {
var fileList = [];
self.packageFiles.forEach(function (name) {
var f = path.join(self.packageDirPath(), name)
, fDir = path.dirname(f)
, stats;
jake.mkdirP(fDir, {silent: true});
// Add both files and directories
fileList.push({
from: name
, to: f
});
});
var _copyFile = function () {
var cmd
, file = fileList.pop()
, stat;
if (file) {
stat = fs.statSync(file.from);
// Target is a directory, just create it
if (stat.isDirectory()) {
jake.mkdirP(file.to, {silent: true});
_copyFile();
}
// Otherwise copy the file
else {
jake.cpR(file.from, file.to, {silent: true});
_copyFile();
}
}
else {
complete();
}
};
_copyFile();
}, {async: true});
};
this.packageName = function () {
if (this.version) {
return this.name + '-' + this.version;
}
else {
return this.name;
}
};
this.packageDirPath = function () {
return this.packageDir + '/' + this.packageName();
};
})();
jake.PackageTask = PackageTask;
exports.PackageTask = PackageTask;
jake-0.7.4/lib/parseargs.js 0000664 0000000 0000000 00000007120 12240565131 0015526 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var parseargs = {};
/**
* @constructor
* Parses a list of command-line args into a key/value object of
* options and an array of positional commands.
* @ param {Array} opts A list of options in the following format:
* [{full: 'foo', abbr: 'f'}, {full: 'bar', abbr: 'b'}]]
*/
parseargs.Parser = function (opts) {
// A key/value object of matching options parsed out of the args
this.opts = {};
this.taskNames = null;
this.envVars = null;
// Data structures used for parsing
this.reg = [];
this.shortOpts = {};
this.longOpts = {};
var item;
for (var i = 0, ii = opts.length; i < ii; i++) {
item = opts[i];
this.shortOpts[item.abbr] = item;
this.longOpts[item.full] = item;
}
this.reg = opts;
};
parseargs.Parser.prototype = new function () {
var _trueOrNextVal = function (argParts, args) {
if (argParts[1]) {
return argParts[1];
}
else {
return (!args[0] || (args[0].indexOf('-') === 0)) ?
true : args.shift();
}
};
/**
* Parses an array of arguments into options and positional commands
* @param {Array} args The command-line args to parse
*/
this.parse = function (args) {
var cmds = []
, cmd
, envVars = {}
, opts = {}
, arg
, argItem
, argParts
, cmdItems
, taskNames = []
, preempt;
while (args.length) {
arg = args.shift();
if (arg.indexOf('-') === 0) {
arg = arg.replace(/^--/, '').replace(/^-/, '');
argParts = arg.split('=');
argItem = this.longOpts[argParts[0]] || this.shortOpts[argParts[0]];
if (argItem) {
// First-encountered preemptive opt takes precedence -- no further opts
// or possibility of ambiguity, so just look for a value, or set to
// true and then bail
if (argItem.preempts) {
opts[argItem.full] = _trueOrNextVal(argParts, args);
preempt = true;
break;
}
// If the opt requires a value, see if we can get a value from the
// next arg, or infer true from no-arg -- if it's followed by another
// opt, throw an error
if (argItem.expectValue) {
opts[argItem.full] = _trueOrNextVal(argParts, args);
if (!opts[argItem.full]) {
throw new Error(argItem.full + ' option expects a value.');
}
}
else {
opts[argItem.full] = true;
}
}
}
else {
cmds.unshift(arg);
}
}
if (!preempt) {
// Parse out any env-vars and task-name
while (!!(cmd = cmds.pop())) {
cmdItems = cmd.split('=');
if (cmdItems.length > 1) {
envVars[cmdItems[0]] = cmdItems[1];
}
else {
taskNames.push(cmd);
}
}
}
return {
opts: opts
, envVars: envVars
, taskNames: taskNames
};
};
};
module.exports = parseargs;
jake-0.7.4/lib/program.js 0000664 0000000 0000000 00000014145 12240565131 0015213 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var fs = require('fs')
, parseargs = require('./parseargs')
, utils = require('./utils')
, Program
, optsReg
, preempts
, usage
, die;
optsReg = [
{ full: 'jakefile'
, abbr: 'f'
, preempts: false
, expectValue: true
}
, { full: 'quiet'
, abbr: 'q'
, preempts: false
, expectValue: false
}
, { full: 'directory'
, abbr: 'C'
, preempts: false
, expectValue: true
}
, { full: 'always-make'
, abbr: 'B'
, preempts: false
, expectValue: false
}
, { full: 'tasks'
, abbr: 'T'
, preempts: true
}
// Alias ls
, { full: 'tasks'
, abbr: 'ls'
, preempts: true
}
, { full: 'trace'
, abbr: 't'
, preempts: false
, expectValue: false
}
, { full: 'help'
, abbr: 'h'
, preempts: true
}
, { full: 'version'
, abbr: 'V'
, preempts: true
}
// Alias lowercase v
, { full: 'version'
, abbr: 'v'
, preempts: true
}
, { full: 'jakelibdir'
, abbr: 'J'
, preempts: false
, expectValue: true
}
];
preempts = {
version: function () {
die(jake.version);
}
, help: function () {
die(usage);
}
};
usage = ''
+ 'Jake JavaScript build tool\n'
+ '********************************************************************************\n'
+ 'If no flags are given, Jake looks for a Jakefile or Jakefile.js in the current directory.\n'
+ '********************************************************************************\n'
+ '{Usage}: jake [options ...] [env variables ...] target\n'
+ '\n'
+ '{Options}:\n'
+ ' -f, --jakefile FILE Use FILE as the Jakefile.\n'
+ ' -C, --directory DIRECTORY Change to DIRECTORY before running tasks.\n'
+ ' -q, --quiet Do not log messages to standard output.\n'
+ ' -B, --always-make Unconditionally make all targets.\n'
+ ' -T/ls, --tasks Display the tasks (matching optional PATTERN) with descriptions, then exit.\n'
+ ' -J, --jakelibdir JAKELIBDIR Auto-import any .jake files in JAKELIBDIR. (default is \'jakelib\')\n'
+ ' -t, --trace Enable full backtrace.\n'
+ ' -h, --help Display this help message.\n'
+ ' -V/v, --version Display the Jake version.\n'
+ '';
Program = function () {
this.opts = {};
this.taskNames = null;
this.taskArgs = null;
this.envVars = null;
};
Program.prototype = new (function () {
this.handleErr = function (err) {
var msg;
if (jake.listeners('error').length) {
jake.emit('error', err);
return;
}
utils.logger.error('jake aborted.');
if (this.opts.trace && err.stack) {
utils.logger.error(err.stack);
}
else {
if (err.stack) {
msg = err.stack.split('\n').slice(0, 3).join('\n');
utils.logger.error(msg);
utils.logger.error('(See full trace by running task with --trace)');
}
else {
utils.logger.error(err.message);
}
}
process.stdout.write('', function() {
process.stderr.write('', function() {
process.exit(jake.errorCode || 1);
});
});
};
this.parseArgs = function (args) {
var result = (new parseargs.Parser(optsReg)).parse(args);
this.setOpts(result.opts);
this.setTaskNames(result.taskNames);
this.setEnvVars(result.envVars);
};
this.setOpts = function (options) {
var opts = options || {};
utils.mixin(this.opts, opts);
};
this.setTaskNames = function (names) {
if (names && !Array.isArray(names)) {
throw new Error('Task names must be an array');
}
this.taskNames = (names && names.length) ? names : ['default'];
};
this.setEnvVars = function (vars) {
this.envVars = vars || null;
};
this.firstPreemptiveOption = function () {
var opts = this.opts;
for (var p in opts) {
if (preempts[p]) {
return preempts[p];
}
}
return false;
};
this.init = function (configuration) {
var self = this
, config = configuration || {};
if (config.options) {
this.setOpts(config.options);
}
if (config.taskNames) {
this.setTaskNames(config.taskNames);
}
if (config.envVars) {
this.setEnvVars(config.envVars);
}
process.addListener('uncaughtException', function (err) {
self.handleErr(err);
});
if (this.envVars) {
utils.mixin(process.env, this.envVars);
}
};
this.run = function () {
var rootTask
, taskNames
, dirname
, opts = this.opts;
// Run with `jake -T`, just show descriptions
if (opts.tasks) {
return jake.showAllTaskDescriptions(opts.tasks);
}
taskNames = this.taskNames;
if (!(Array.isArray(taskNames) && taskNames.length)) {
throw new Error('Please pass jake.runTasks an array of task-names');
}
// Set working dir
dirname = opts.directory;
if (dirname) {
if (utils.file.existsSync(dirname) &&
fs.statSync(dirname).isDirectory()) {
process.chdir(dirname);
}
else {
throw new Error(dirname + ' is not a valid directory path');
}
}
task('__root__', taskNames, function () {});
rootTask = jake.Task.__root__;
rootTask.once('complete', function () {
jake.emit('complete');
});
jake.emit('start');
rootTask.invoke();
};
})();
die = function (msg) {
console.log(msg);
process.stdout.write('', function() {
process.stderr.write('', function() {
process.exit();
});
});
};
module.exports.Program = Program;
jake-0.7.4/lib/rule.js 0000664 0000000 0000000 00000017663 12240565131 0014523 0 ustar 00root root 0000000 0000000 var path = require('path')
, fs = require('fs')
, Matcher
, rule = {}
, Rule;
// Define a helper object with some utility functions
Matcher = new (function () {
// Split a task to two parts, name space and task name.
// For example, given 'foo:bin/a%.c', return an object with
// - 'ns' : foo
// - 'name' : bin/a%.c
this.split = function(task) {
var parts = task.split(':')
, name = parts.pop()
, ns = this.resolveNS( parts );
return {
'name' : name,
'ns' : ns
};
};
// Return the namespace based on an array of names.
// For example, given ['foo', 'baz' ], return the namespace
//
// default -> foo -> baz
//
// where default is the global root namespace
// and -> means child namespace.
this.resolveNS = function(parts) {
var ns = jake.defaultNamespace;
for(var i = 0, l = parts.length; ns && i < l; i++) {
ns = ns.childNamespaces[parts[i]];
}
return ns;
};
// Given a pattern p, say 'foo:bin/a%.c'
// Return an object with
// - 'ns' : foo
// - 'dir' : bin
// - 'prefix' : a
// - 'suffix' : .c
this.resolve = function(p) {
var task = this.split(p),
name = task.name,
ns = task.ns;
var split = path.basename(name).split('%');
return {
ns: ns
, dir: path.dirname(name)
, prefix: split[0]
, suffix: split[1]
};
};
// Test whether string a is a suffix of string b
this.stringEndWith = function (a,b) {
var l;
return (l = b.lastIndexOf(a)) == -1 ? false : l + a.length == b.length;
};
// Replace the suffix a of the string s with b.
// Note that, it is assumed a is a suffix of s.
this.stringReplaceSuffix = function (s, a, b) {
return s.slice(0,s.lastIndexOf(a)) + b;
};
// Test wether the a prerequisite matchs the pattern.
// The arg 'pattern' does not have namespace as prefix.
// For example, the following tests are true
//
// pattern | name
// bin/%.o | bin/main.o
// bin/%.o | foo:bin/main.o
//
// The following tests are false (trivally)
//
// pattern | name
// bin/%.o | foobin/main.o
// bin/%.o | bin/main.oo
this.match = function(pattern, name) {
var p
, task
, ns
, obj
, filename;
if (pattern instanceof RegExp) {
return pattern.test(name);
}
else if (pattern.indexOf('%') == -1) {
// No Pattern. No Folder. No Namespace.
// A Simple Suffix Rule. Just test suffix
return this.stringEndWith(pattern, name);
}
else {
// Resolve the dir, prefix and suffix of pattern
p = this.resolve(pattern);
// Resolve the namespace and task-name
task = this.split(name);
name = task.name;
ns = task.ns;
// Set the objective as the task-name
obj = name;
// Namespace is already matched.
// Check dir
if (path.dirname(obj) != p.dir) {
return false;
}
filename = path.basename(obj);
// Check file name length
if ((p.prefix.length + p.suffix.length + 1) > filename.length) {
// Length does not match.
return false;
}
// Check prefix
if (filename.indexOf(p.prefix) !== 0) {
return false;
}
// Check suffix
if (!this.stringEndWith(p.suffix, filename)) {
return false;
}
// OK. Find a match.
return true;
}
};
// Generate the source based on
// - name name for the synthesized task
// - pattern pattern for the objective
// - source pattern for the source
//
// Return the source with properties
// - dep the prerequisite of source
// (with the namespace)
//
// - file the file name of source
// (without the namespace)
//
// For example, given
//
// - name foo:bin/main.o
// - pattern bin/%.o
// - source src/%.c
//
// return 'foo:src/main.c',
//
this.getSource = function(name, pattern, source) {
var dep
, pat
, match
, file
, src;
// Regex pattern -- use to look up the extension
if (pattern instanceof RegExp) {
match = pattern.exec(name);
if (match) {
if (typeof source == 'function') {
src = source(name);
}
else {
src = this.stringReplaceSuffix(name, match[0], source);
}
}
}
// Assume string
else {
// Simple string suffix replacement
if (pattern.indexOf('%') == -1) {
if (typeof source == 'function') {
src = source(name);
}
else {
src = this.stringReplaceSuffix(name, pattern, source);
}
}
// Percent-based substitution
else {
pat = pattern.replace('%', '(.*?)');
pat = new RegExp(pat);
match = pat.exec(name);
if (match) {
if (typeof source == 'function') {
src = source(name);
}
else {
file = match[1];
file = source.replace('%', file);
dep = match[0];
src = name.replace(dep, file);
}
}
}
}
return src;
};
})();
Rule = function (opts) {
this.pattern = opts.pattern;
this.source = opts.source;
this.prereqs = opts.prereqs;
this.action = opts.action;
this.opts = opts.opts;
this.desc = opts.desc;
this.ns = opts.ns;
};
Rule.prototype = new (function () {
// Create a file task based on this rule for the specified
// task-name
// ======
// FIXME: Right now this just throws away any passed-in args
// for the synthsized task (taskArgs param)
// ======
this.createTask = function (fullName, level) {
var self = this
, pattern
, source
, action
, opts
, prereqs
, parts
, valid
, name
, nsPath
, ns
, src
, tNs
, createdTask;
parts = fullName.split(':');
name = parts.pop();
nsPath = parts.join(':');
ns = this.ns.resolveNamespace(nsPath);
pattern = this.pattern;
source = this.source;
if (typeof source == 'string') {
src = Matcher.getSource(name, pattern, source);
}
else {
src = source(name);
}
parts.push(src);
src = parts.join(':');
// Generate the prerequisite for the matching task.
// It is the original prerequisites plus the prerequisite
// representing source file, i.e.,
//
// rule( '%.o', '%.c', ['some.h'] ...
//
// If the objective is main.o, then new task should be
//
// file( 'main.o', ['main.c', 'some.h' ] ...
prereqs = this.prereqs;
prereqs.unshift(src);
// Prereq should be:
// 1. an existing task
// 2. an existing file on disk
// 3. a valid rule (i.e., not at too deep a level)
valid = prereqs.some(function (p) {
var ns = self.ns;
return ns.resolveTask(p) ||
fs.existsSync(p) ||
jake.attemptRule(p, ns, level + 1);
});
// If any of the prereqs aren't valid, the rule isn't valid
if (!valid) {
return null;
}
// Otherwise, hunky-dory, finish creating the task for the rule
else {
// Create the action for the task
action = function () {
var task = this;
self.action.apply(task);
};
opts = this.opts;
// Insert the file task into Jake
//
// Since createTask function stores the task as a child task
// of currentNamespace. Here we temporariliy switch the namespace.
// FIXME: Should allow optional ns passed in instead of this hack
tNs = jake.currentNamespace;
jake.currentNamespace = ns;
createdTask = jake.createTask('file', name, prereqs, action, opts);
createdTask.source = src.split(':').pop();
jake.currentNamespace = tNs;
return createdTask;
}
};
this.match = function (name) {
return Matcher.match(this.pattern, name);
};
})();
rule.Rule = Rule;
rule.Matcher = Matcher;
module.exports = rule;
jake-0.7.4/lib/task/ 0000775 0000000 0000000 00000000000 12240565131 0014143 5 ustar 00root root 0000000 0000000 jake-0.7.4/lib/task/directory_task.js 0000664 0000000 0000000 00000001242 12240565131 0017526 0 ustar 00root root 0000000 0000000 var DirectoryTask
, FileTask = require('./file_task').FileTask;
/**
@name jake
@namespace jake
*/
/**
@name jake.DirectoryTask
@constructor
@augments EventEmitter
@augments jake.Task
@augments jake.FileTask
@description A Jake DirectoryTask
@param {String} name The name of the directory to create.
*/
DirectoryTask = function (name) {
this.modTime = null;
// Do constructor-work only on actual instances, not when used
// for inheritance
if (arguments.length) {
this.init.apply(this, arguments);
}
};
DirectoryTask.prototype = new FileTask();
DirectoryTask.prototype.constructor = DirectoryTask;
exports.DirectoryTask = DirectoryTask;
jake-0.7.4/lib/task/file_task.js 0000664 0000000 0000000 00000007271 12240565131 0016451 0 ustar 00root root 0000000 0000000 var fs = require('fs')
, Task = require('./task').Task
, FileTask
, FileBase
, DirectoryTask
, utils = require('../utils');
FileBase = new (function () {
var isFileOrDirectory = function (t) {
return (t instanceof FileTask ||
t instanceof DirectoryTask);
}
, isFile = function (t) {
return (t instanceof FileTask && !(t instanceof DirectoryTask));
};
this.isNeeded = function () {
var runAction = false
, prereqs = this.prereqs
, prereqName
, prereqTask;
// No repeatsies
if (this.done) {
return false;
}
// The always-make override
else if (jake.program.opts['always-make']) {
// Run if there actually is an action
if (typeof this.action == 'function') {
return true;
}
else {
return false;
}
}
// Default case
else {
// We need either an existing file, or an action to create one.
// First try grabbing the actual mod-time of the file
try {
this.updateModTime();
}
// Then fall back to looking for an action
catch(e) {
if (typeof this.action == 'function') {
return true;
}
else {
throw new Error('File-task ' + this.fullName + ' has no ' +
'existing file, and no action to create one.');
}
}
// Compare mod-time of all the prereqs with its mod-time
// If any prereqs are newer, need to run the action to update
if (prereqs && prereqs.length) {
for (var i = 0, ii = prereqs.length; i < ii; i++) {
prereqName = prereqs[i];
prereqTask = this.namespace.resolveTask(prereqName) ||
jake.createPlaceholderFileTask(prereqName, this.namespace);
// Run the action if:
// 1. The prereq is a normal task (not file/dir)
// 2. The prereq is a file-task with a mod-date more recent than
// the one for this file/dir
if (prereqTask) {
if (!isFileOrDirectory(prereqTask) ||
(isFile(prereqTask) && prereqTask.modTime > this.modTime)) {
return true;
}
}
}
}
// File/dir has no prereqs, and exists -- no need to run
else {
return false;
}
}
};
this.updateModTime = function () {
var stats = fs.statSync(this.name);
this.modTime = stats.mtime;
};
this.complete = function () {
if (!this.dummy) {
this.updateModTime();
}
this._currentPrereqIndex = 0;
this.done = true;
this.emit('complete');
};
})();
/**
@name jake
@namespace jake
*/
/**
@name jake.FileTask
@constructor
@augments EventEmitter
@augments jake.Task
@description A Jake FileTask
@param {String} name The name of the Task
@param {Array} [prereqs] Prerequisites to be run before this task
@param {Function} [action] The action to perform to create this file
@param {Object} [opts]
@param {Array} [opts.asyc=false] Perform this task asynchronously.
If you flag a task with this option, you must call the global
`complete` method inside the task's action, for execution to proceed
to the next task.
*/
FileTask = function (name, prereqs, action, opts) {
this.modTime = null;
this.dummy = false;
// Do constructor-work only on actual instances, not when used
// for inheritance
if (arguments.length) {
this.init.apply(this, arguments);
}
};
FileTask.prototype = new Task();
FileTask.prototype.constructor = FileTask;
utils.mixin(FileTask.prototype, FileBase);
exports.FileTask = FileTask;
// DirectoryTask is a subclass of FileTask, depends on it
// being defined
DirectoryTask = require('./directory_task').DirectoryTask;
jake-0.7.4/lib/task/index.js 0000664 0000000 0000000 00000000354 12240565131 0015612 0 ustar 00root root 0000000 0000000
var Task = require('./task').Task
, FileTask = require('./file_task').FileTask
, DirectoryTask = require('./directory_task').DirectoryTask;
exports.Task = Task;
exports.FileTask = FileTask;
exports.DirectoryTask = DirectoryTask;
jake-0.7.4/lib/task/task.js 0000664 0000000 0000000 00000013232 12240565131 0015444 0 ustar 00root root 0000000 0000000 var util = require('util') // Native Node util module
, path = require('path')
, EventEmitter = require('events').EventEmitter
, Task
, TaskBase
, utils = require('../utils')
, rule = require('../rule');
var UNDEFINED_VALUE;
/**
@name jake
@namespace jake
*/
/**
@name jake.Task
@constructor
@augments EventEmitter
@description A Jake Task
@param {String} name The name of the Task
@param {Array} [prereqs] Prerequisites to be run before this task
@param {Function} [action] The action to perform for this task
@param {Object} [opts]
@param {Array} [opts.asyc=false] Perform this task asynchronously.
If you flag a task with this option, you must call the global
`complete` method inside the task's action, for execution to proceed
to the next task.
*/
Task = function () {
// Do constructor-work only on actual instances, not when used
// for inheritance
if (arguments.length) {
this.init.apply(this, arguments);
}
};
util.inherits(Task, EventEmitter);
TaskBase = new (function () {
// Parse any positional args attached to the task-name
var parsePrereqName = function (name) {
var taskArr = name.split('[')
, taskName = taskArr[0]
, taskArgs = [];
if (taskArr[1]) {
taskArgs = taskArr[1].replace(/\]$/, '');
taskArgs = taskArgs.split(',');
}
return {
name: taskName
, args: taskArgs
};
};
/**
@name jake.Task#event:complete
@event
*/
this.init = function (name, prereqs, action, options) {
var opts = options || {};
this._currentPrereqIndex = 0;
this.name = name;
this.prereqs = prereqs;
this.action = action;
this.async = false;
this.done = false;
this.fullName = null;
this.description = null;
this.args = [];
this.value = UNDEFINED_VALUE;
this.namespace = null;
// Support legacy async-flag -- if not explicitly passed or falsy, will
// be set to empty-object
if (typeof opts == 'boolean' && opts === true) {
this.async = true;
}
else {
if (opts.async) {
this.async = true;
}
}
};
/**
@name jake.Task#invoke
@function
@description Runs prerequisites, then this task. If the task has already
been run, will not run the task again.
*/
this.invoke = function () {
jake._invocationChain.push(this);
this.args = Array.prototype.slice.call(arguments);
this.runPrereqs();
};
/**
@name jake.Task#reenable
@function
@description Runs this task, without running any prerequisites. If the task
has already been run, it will still run it again.
*/
this.execute = function () {
jake._invocationChain.push(this);
this.reenable();
this.run();
};
this.runPrereqs = function () {
if (this.prereqs && this.prereqs.length) {
this.nextPrereq();
}
else {
this.run();
}
};
this.nextPrereq = function () {
var self = this
, index = this._currentPrereqIndex
, name = this.prereqs[index]
, prereq
, parsed
, filePath
, stats;
if (name) {
parsed = parsePrereqName(name);
prereq = this.namespace.resolveTask(parsed.name) ||
jake.attemptRule(name, this.namespace, 0) ||
jake.createPlaceholderFileTask(name, this.namespace);
if (!prereq) {
throw new Error('Unknown task "' + name + '"');
}
// Do when done
if (prereq.done) {
self.handlePrereqComplete(prereq);
} else {
prereq.once('complete', function () {
self.handlePrereqComplete(prereq);
});
prereq.invoke.apply(prereq, parsed.args);
}
}
};
this.reenable = function (deep) {
var prereqs
, prereq;
this.done = false;
this.value = UNDEFINED_VALUE;
if (deep && this.prereqs) {
prereqs = this.prereqs;
for (var i = 0, ii = prereqs.length; i < ii; i++) {
prereq = jake.Task[prereqs[i]];
if (prereq) {
prereq.reenable(deep);
}
}
}
};
this.handlePrereqComplete = function (prereq) {
var self = this;
this._currentPrereqIndex++;
if (this._currentPrereqIndex < this.prereqs.length) {
setTimeout(function () {
self.nextPrereq();
}, 0);
}
else {
this.run();
}
};
this.isNeeded = function () {
if (this.done || typeof this.action != 'function') {
return false;
}
return true;
};
this.run = function () {
var runAction = this.isNeeded()
, val;
if (runAction) {
this.emit('start');
try {
val = this.action.apply(this, this.args);
if (typeof val == 'object' && typeof val.then == 'function') {
this.async = true;
val.then(
function(result) {
setTimeout(function() {
complete(result);
},
0);
},
function(err) {
setTimeout(function() {
fail(err);
},
0);
});
}
}
catch (e) {
this.emit('error', e);
return; // Bail out, not complete
}
}
else {
this.emit('skip');
}
if (!(runAction && this.async)) {
complete(val);
}
};
this.complete = function (val) {
this._currentPrereqIndex = 0;
this.done = true;
// If 'complete' getting called because task has been
// run already, value will not be passed -- leave in place
if (typeof val != 'undefined') {
this.value = val;
}
this.emit('complete', this.value);
};
})();
utils.mixin(Task.prototype, TaskBase);
exports.Task = Task;
jake-0.7.4/lib/test_task.js 0000664 0000000 0000000 00000016305 12240565131 0015545 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var path = require('path')
, fs = require('fs')
, exec = require('child_process').exec
, currDir = process.cwd();
/**
@name jake
@namespace jake
*/
/**
@name jake.TestTask
@constructor
@description Instantiating a TestTask creates a number of Jake
Tasks that make running tests for your software easy.
@param {String} name The name of the project
@param {Function} definition Defines the list of files containing the tests,
and the name of the namespace/task for running them. Will be executed on the
instantiated TestTask (i.e., 'this', will be the TestTask instance), to set
the various instance-propertiess.
@example
var t = new jake.TestTask('bij-js', function () {
this.testName = 'testSpecial';
this.testFiles.include('test/**');
});
*/
var TestTask = function (name, definition) {
var self = this;
/**
@name jake.TestTask#testNam
@public
@type {String}
@description The name of the namespace to place the tests in, and
the top-level task for running tests. Defaults to "test"
*/
this.testName = 'test';
/**
@name jake.TestTask#testFiles
@public
@type {jake.FileList}
@description The list of files containing tests to load
*/
this.testFiles = new jake.FileList();
/**
@name jake.TestTask#showDescription
@public
@type {Boolean}
@description Show the created task when doing Jake -T
*/
this.showDescription = true;
if (typeof definition == 'function') {
definition.call(this);
}
if (this.showDescription) {
desc('Run the tests for ' + name);
}
task(this.testName, [self.testName + ':run'], function () {});
namespace(self.testName, function () {
task('run', {async: true}, function (pat) {
var p = pat || '.*'
, re
, testFiles;
// Don't nest; make a top-level namespace. Don't want
// re-calling from inside to nest infinitely
jake.currentNamespace = jake.defaultNamespace;
re = new RegExp(pat);
testFiles = self.testFiles.toArray().filter(function (f) {
return (re).test(f);
});
// Create a namespace for all the testing tasks to live in
namespace(self.testName + 'Exec', function () {
// Each test will be a prereq for the dummy top-level task
var prereqs = []
// Continuation to pass to the async tests, wrapping `continune`
, next = function () {
complete();
}
// Create the task for this test-function
, createTask = function (name, action) {
// If the test-function is defined with a continuation
// param, flag the task as async
var isAsync = !!action.length;
// Define the actual namespaced task with the name, the
// wrapped action, and the correc async-flag
task(name, createAction(name, action), {
async: isAsync
});
}
// Used as the action for the defined task for each test.
, createAction = function (n, a) {
// A wrapped function that passes in the `next` function
// for any tasks that run asynchronously
return function () {
var cb
, msg;
if (a.length) {
cb = next;
}
if (!(n == 'before' || n == 'after' ||
/_beforeEach$/.test(n) || /_afterEach$/.test(n))) {
if (n.toLowerCase().indexOf('test') === 0) {
msg = n;
}
else {
msg = 'test ' + n;
}
jake.logger.log(n);
}
// 'this' will be the task when action is run
return a.call(this, cb);
};
}
// Dummy top-level task for everything to be prereqs for
, topLevel;
// Pull in each test-file, and iterate over any exported
// test-functions. Register each test-function as a prereq task
testFiles.forEach(function (file) {
var exp = require(path.join(currDir, file))
, name
, action
, isAsync;
// Create a namespace for each filename, so test-name collisions
// won't be a problem
namespace(file, function () {
var testPrefix = self.testName + 'Exec:' + file + ':'
, testName;
// Dummy task for displaying file banner
testName = '*** Running ' + file + ' ***';
prereqs.push(testPrefix + testName);
createTask(testName, function () {});
// 'before' setup
if (typeof exp.before == 'function') {
prereqs.push(testPrefix + 'before');
// Create the task
createTask('before', exp.before);
}
// Walk each exported function, and create a task for each
for (var p in exp) {
if (p == 'before' || p == 'after' ||
p == 'beforeEach' || p == 'afterEach') {
continue;
}
if (typeof exp.beforeEach == 'function') {
prereqs.push(testPrefix + p + '_beforeEach');
// Create the task
createTask(p + '_beforeEach', exp.beforeEach);
}
// Add the namespace:name of this test to the list of prereqs
// for the dummy top-level task
prereqs.push(testPrefix + p);
// Create the task
createTask(p, exp[p]);
if (typeof exp.afterEach == 'function') {
prereqs.push(testPrefix + p + '_afterEach');
// Create the task
createTask(p + '_afterEach', exp.afterEach);
}
}
// 'after' teardown
if (typeof exp.after == 'function') {
prereqs.push(testPrefix + 'after');
// Create the task
createTask('after', exp.after);
}
});
});
// Create the dummy top-level task. When calling a task internally
// with `invoke` that is async (or has async prereqs), have to listen
// for the 'complete' event to know when it's done
topLevel = task('__top__', prereqs);
topLevel.addListener('complete', function () {
jake.logger.log('All tests ran successfully');
complete();
});
topLevel.invoke(); // Do the thing!
});
});
});
};
jake.TestTask = TestTask;
exports.TestTask = TestTask;
jake-0.7.4/lib/utils/ 0000775 0000000 0000000 00000000000 12240565131 0014341 5 ustar 00root root 0000000 0000000 jake-0.7.4/lib/utils/index.js 0000664 0000000 0000000 00000013744 12240565131 0016017 0 ustar 00root root 0000000 0000000 /*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var util = require('util') // Native Node util module
, exec = require('child_process').exec
, spawn = require('child_process').spawn
, EventEmitter = require('events').EventEmitter
, utils = require('utilities')
, logger = require('./logger')
, Exec;
var parseArgs = function (argumentsObj) {
var args
, arg
, cmds
, callback
, opts = {
interactive: false
, printStdout: false
, printStderr: false
, breakOnError: true
};
args = Array.prototype.slice.call(argumentsObj);
cmds = args.shift();
// Arrayize if passed a single string command
if (typeof cmds == 'string') {
cmds = [cmds];
}
// Make a copy if it's an actual list
else {
cmds = cmds.slice();
}
// Get optional callback or opts
while((arg = args.shift())) {
if (typeof arg == 'function') {
callback = arg;
}
else if (typeof arg == 'object') {
utils.mixin(opts, arg);
}
}
// Backward-compat shim
if (typeof opts.stdout != 'undefined') {
opts.printStdout = opts.stdout;
delete opts.stdout;
}
if (typeof opts.stderr != 'undefined') {
opts.printStderr = opts.stderr;
delete opts.stderr;
}
return {
cmds: cmds
, opts: opts
, callback: callback
};
};
/**
@name jake
@namespace jake
*/
utils.mixin(utils, new (function () {
/**
@name jake.exec
@static
@function
@description Executes shell-commands asynchronously with an optional
final callback.
`
@param {String[]} cmds The list of shell-commands to execute
@param {Object} [opts]
@param {Boolean} [opts.printStdout=false] Print stdout from each command
@param {Boolean} [opts.printStderr=false] Print stderr from each command
@param {Boolean} [opts.breakOnError=true] Stop further execution on
the first error.
@param {Function} [callback] Callback to run after executing the
commands
@example
var cmds = [
'echo "showing directories"'
, 'ls -al | grep ^d'
, 'echo "moving up a directory"'
, 'cd ../'
]
, callback = function () {
console.log('Finished running commands.');
}
jake.exec(cmds, {stdout: true}, callback);
*/
this.exec = function (a, b, c) {
var parsed = parseArgs(arguments)
, cmds = parsed.cmds
, opts = parsed.opts
, callback = parsed.callback;
var ex = new Exec(cmds, opts, callback);
if (!opts.interactive) {
if (opts.printStdout) {
ex.addListener('stdout', function (data) {
process.stdout.write(data);
});
}
if (opts.printStderr) {
ex.addListener('stderr', function (data) {
process.stderr.write(data);
});
}
}
ex.addListener('error', function (msg, code) {
if (opts.breakOnError) {
fail(msg, code);
}
});
ex.run();
return ex;
};
this.createExec = function (a, b, c) {
return new Exec(a, b, c);
};
})());
Exec = function () {
var parsed = parseArgs(arguments)
, cmds = parsed.cmds
, opts = parsed.opts
, callback = parsed.callback;
this._cmds = cmds;
this._callback = callback;
this._config = opts;
};
util.inherits(Exec, EventEmitter);
utils.mixin(Exec.prototype, new (function () {
var _run = function () {
var self = this
, sh
, cmd
, args
, next = this._cmds.shift()
, config = this._config
, errData = '';
// Keep running as long as there are commands in the array
if (next) {
this.emit('cmdStart', next);
// Ganking part of Node's child_process.exec to get cmdline args parsed
cmd = '/bin/sh';
args = ['-c', next];
if (process.platform == 'win32') {
cmd = 'cmd';
args = ['/c', next];
}
if (config.interactive) {
sh = spawn(cmd, args, { stdio: 'inherit' });
}
else {
sh = spawn(cmd, args, { stdio: [process.stdin, 'pipe', 'pipe'] });
// Out
sh.stdout.on('data', function (data) {
self.emit('stdout', data);
});
// Err
sh.stderr.on('data', function (data) {
var d = data.toString();
self.emit('stderr', data);
// Accumulate the error-data so we can use it as the
// stack if the process exits with an error
errData += d;
});
}
// Exit, handle err or run next
sh.on('exit', function (code) {
var msg;
if (code !== 0) {
msg = errData || 'Process exited with error.';
msg = utils.string.trim(msg);
self.emit('error', msg, code);
}
if (code === 0 || !config.breakOnError) {
self.emit('cmdEnd', next);
_run.call(self);
}
});
}
else {
self.emit('end');
if (typeof self._callback == 'function') {
self._callback();
}
}
};
this.append = function (cmd) {
this._cmds.push(cmd);
};
this.run = function () {
_run.call(this);
};
})());
utils.Exec = Exec;
utils.logger = logger;
module.exports = utils;
jake-0.7.4/lib/utils/logger.js 0000664 0000000 0000000 00000000770 12240565131 0016162 0 ustar 00root root 0000000 0000000 var util = require('util');
var logger = new (function () {
var _output = function (type, out) {
var quiet = typeof jake != 'undefined' && jake.program &&
jake.program.opts && jake.program.opts.quiet
, msg;
if (!quiet) {
msg = typeof out == 'string' ? out : util.inspect(out);
console[type](msg);
}
};
this.log = function (out) {
_output('log', out);
};
this.error = function (out) {
_output('error', out);
};
})();
module.exports = logger;
jake-0.7.4/lib/watch_task.js 0000664 0000000 0000000 00000002216 12240565131 0015670 0 ustar 00root root 0000000 0000000 FileList = require('./file_list').FileList;
var WatchTask = function (taskNames, definition) {
var self = this;
this.watchTasks = Array.isArray(taskNames) ? taskNames : [taskNames];
this.watchFiles = new FileList();
this.rootTask = task('watchTasks', this.watchTasks);
this.watchFiles.include(WatchTask.DEFAULT_INCLUDE_FILES);
this.watchFiles.exclude(WatchTask.DEFAULT_EXCLUDE_FILES);
if (typeof definition == 'function') {
definition.call(this);
}
desc('Runs these tasks: ' + this.watchTasks.join(', '));
task('watch', function () {
console.log('WatchTask started for: ' + self.watchTasks.join(', '));
jake.watch('.', {includePattern: /.+/}, function (filePath) {
var shouldRun = self.watchFiles.toArray().some(function (item) {
return item == filePath;
});
if (shouldRun) {
self.rootTask.reenable(true);
self.rootTask.invoke();
}
});
});
};
WatchTask.DEFAULT_INCLUDE_FILES = [
'./**/*.js'
, './**/*.coffee'
, './**/*.css'
, './**/*.less'
, './**/*.scss'
];
WatchTask.DEFAULT_EXCLUDE_FILES = [
'node_modules/**'
, '.git/**'
];
exports.WatchTask = WatchTask;
jake-0.7.4/package.json 0000664 0000000 0000000 00000001110 12240565131 0014712 0 ustar 00root root 0000000 0000000 {
"name": "jake",
"description": "JavaScript build tool, similar to Make or Rake",
"keywords": [
"build",
"cli",
"make",
"rake"
],
"version": "0.7.4",
"author": "Matthew Eernisse (http://fleegix.org)",
"bin": {
"jake": "./bin/cli.js"
},
"main": "./lib/jake.js",
"repository": {
"type": "git",
"url": "git://github.com/mde/jake.git"
},
"preferGlobal": true,
"dependencies": {
"minimatch": "0.2.x",
"utilities": "0.0.x"
},
"devDependencies": {
"q": "0.9.x"
},
"engines": {
"node": "*"
}
} jake-0.7.4/test/ 0000775 0000000 0000000 00000000000 12240565131 0013412 5 ustar 00root root 0000000 0000000 jake-0.7.4/test/Jakefile 0000664 0000000 0000000 00000014777 12240565131 0015067 0 ustar 00root root 0000000 0000000 var exec = require('child_process').exec
, fs = require('fs')
, Q = require('q');
desc('The default task.');
task('default', function () {
console.log('default task');
});
desc('No action.');
task({'noAction': ['default']});
desc('No action, no prereqs.');
task('noActionNoPrereqs');
desc('Task that throws');
task('throwy', function () {
var errorListener = function (err) {
process.stderr.write('Emitted: ');
process.stderr.write(err.toString());
jake.removeListener('error', errorListener);
};
jake.on('error', errorListener);
throw new Error('I am bad');
});
desc('Accepts args and env vars.');
task('argsEnvVars', function () {
var res = {
args: arguments
, env: {
foo: process.env.foo
, baz: process.env.baz
}
};
console.log(JSON.stringify(res));
});
namespace('foo', function () {
desc('The foo:bar task.');
task('bar', function () {
if (arguments.length) {
console.log('foo:bar[' +
Array.prototype.join.call(arguments, ',') +
'] task');
}
else {
console.log('foo:bar task');
}
});
desc('The foo:baz task, calls foo:bar as a prerequisite.');
task('baz', ['foo:bar'], function () {
console.log('foo:baz task');
});
desc('The foo:qux task, calls foo:bar with cmdline args as a prerequisite.');
task('qux', ['foo:bar[asdf,qwer]'], function () {
console.log('foo:qux task');
});
desc('The foo:frang task, calls foo:bar with passed args as a prerequisite.');
task('frang', function () {
var task = jake.Task['foo:bar'];
// Do args pass-through
task.invoke.apply(task, arguments);
console.log('foo:frang task');
});
desc('The foo:zoobie task, has no prerequisites.');
task('zoobie', function () {
console.log('foo:zoobie task');
});
desc('The foo:voom task, has no prerequisites.');
task('voom', function () {
console.log('foo:voom task');
});
desc('The foo:asdf task, has the same prereq twice.');
task('asdf', ['foo:bar', 'foo:baz'], function () {
console.log('foo:asdf task');
});
});
namespace('bar', function () {
desc('The bar:foo task, has no prerequisites, is async.');
task('foo', function () {
console.log('bar:foo task');
complete();
}, {async: true});
desc('The bar:promise task is a promised based async task.');
task('promise', function() {
return Q()
.then(function() {
console.log('bar:promise task');
return 123654;
});
});
desc('The bar:dependOnpromise task waits for a promise based async test');
task('dependOnpromise', ['promise'], function() {
console.log('bar:dependOnpromise task saw value', jake.Task["bar:promise"].value);
});
desc('The bar:brokenPromise task is a failing promised based async task.');
task('brokenPromise', function() {
return Q()
.then(function() {
throw new Error("nom nom nom");
});
});
desc('The bar:bar task, has the async bar:foo task as a prerequisite.');
task('bar', ['bar:foo'], function () {
console.log('bar:bar task');
});
});
namespace('hoge', function () {
desc('The hoge:hoge task, has no prerequisites.');
task('hoge', function () {
console.log('hoge:hoge task');
});
desc('The hoge:piyo task, has no prerequisites.');
task('piyo', function () {
console.log('hoge:piyo task');
});
desc('The hoge:fuga task, has hoge:hoge and hoge:piyo as prerequisites.');
task('fuga', ['hoge:hoge', 'hoge:piyo'], function () {
console.log('hoge:fuga task');
});
desc('The hoge:charan task, has hoge:fuga as a prerequisite.');
task('charan', ['hoge:fuga'], function () {
console.log('hoge:charan task');
});
desc('The hoge:gero task, has hoge:fuga as a prerequisite.');
task('gero', ['hoge:fuga'], function () {
console.log('hoge:gero task');
});
desc('The hoge:kira task, has hoge:charan and hoge:gero as prerequisites.');
task('kira', ['hoge:charan', 'hoge:gero'], function () {
console.log('hoge:kira task');
});
});
namespace('fileTest', function () {
directory('foo');
desc('File task, concatenating two files together');
file({'foo/concat.txt': ['fileTest:foo', 'fileTest:foo/src1.txt', 'fileTest:foo/src2.txt']}, function () {
console.log('fileTest:foo/concat.txt task');
var data1 = fs.readFileSync('foo/src1.txt');
var data2 = fs.readFileSync('foo/src2.txt');
fs.writeFileSync('foo/concat.txt', data1 + data2);
});
desc('File task, async creation with child_process.exec');
file('foo/src1.txt', function () {
fs.writeFile('foo/src1.txt', 'src1', function (err) {
if (err) {
throw err;
}
console.log('fileTest:foo/src1.txt task');
complete();
});
}, {async: true});
desc('File task, sync creation with writeFileSync');
file('foo/src2.txt', ['default'], function () {
fs.writeFileSync('foo/src2.txt', 'src2');
console.log('fileTest:foo/src2.txt task');
});
desc('File task, do not run unless the prereq file changes');
file('foo/from-src1.txt', ['fileTest:foo', 'fileTest:foo/src1.txt'], function () {
var data = fs.readFileSync('foo/src1.txt');
fs.writeFileSync('foo/from-src1.txt', data);
console.log('fileTest:foo/from-src1.txt task');
}, {async: true});
desc('File task, run if the prereq file changes');
task('touch-prereq', function() {
fs.writeFileSync('foo/prereq.txt', 'UPDATED');
})
desc('File task, has a preexisting file (with no associated task) as a prereq');
file('foo/from-prereq.txt', ['fileTest:foo', 'foo/prereq.txt'], function () {
var data = fs.readFileSync('foo/prereq.txt');
fs.writeFileSync('foo/from-prereq.txt', data);
console.log('fileTest:foo/from-prereq.txt task');
});
directory('foo/bar/baz');
desc('Write a file in a nested subdirectory');
file('foo/bar/baz/bamf.txt', ['foo/bar/baz'], function () {
fs.writeFileSync('foo/bar/baz/bamf.txt', 'w00t');
});
});
task('blammo');
// Define task
task('voom', ['blammo'], function () {
console.log(this.prereqs.length);
});
// Modify, add a prereq
task('voom', ['noActionNoPrereqs']);
namespace('vronk', function () {
task('groo', function () {
var t = jake.Task['vronk:zong'];
t.addListener('error', function (e) {
console.log(e.message);
});
t.invoke();
});
task('zong', function () {
throw new Error('OMFGZONG');
});
});
// define namespace
namespace('one', function() {
task('one', function() {
console.log('one:one');
});
});
// modify namespace (add task)
namespace('one', function() {
task('two', ['one:one'], function() {
console.log('one:two');
});
});
jake-0.7.4/test/Jakefile.rule 0000664 0000000 0000000 00000015760 12240565131 0016026 0 ustar 00root root 0000000 0000000 var exec = require('child_process').exec
, fs = require('fs')
, util = require('util')
, utils = require('utilities');
task('default', ['tmp']);
directory('tmpsrc');
directory('tmpbin');
////////////////////////////////////////////////////////////
// Simple Suffix Rule
file('tmp', ['tmp_init', 'tmp_dep1.o', 'tmp_dep2.o'], function (params) {
console.log('tmp task');
var data1 = fs.readFileSync('tmp_dep1.o');
var data2 = fs.readFileSync('tmp_dep2.o');
fs.writeFileSync('tmp', data1 + data2);
});
rule('.o', '.c', function() {
var cmd = util.format('cp %s %s', this.source, this.name);
console.log(cmd + ' task');
jake.exec([cmd], function () {
complete();
});
}, { async : true });
file('tmp_dep1.c', function () {
fs.writeFile('tmp_dep1.c', 'src_1', function (err) {
if (err) {
throw err;
}
console.log('tmp_dep1.c task');
complete();
});
}, {async: true});
// note that tmp_dep2.o depends on tmp_dep2.c, which is a
// static file.
task('tmp_init', function () {
fs.writeFile('tmp_dep2.c', 'src_2', function (err) {
if (err) {
throw err;
}
console.log('tmp_dep2.c task');
complete();
});
}, {async: true});
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Pattern Rule
file('tmp_p', ['tmp_init', 'tmp_dep1.oo', 'tmp_dep2.oo'], function (params) {
console.log('tmp pattern task');
var data1 = fs.readFileSync('tmp_dep1.oo');
var data2 = fs.readFileSync('tmp_dep2.oo');
fs.writeFileSync('tmp_p', data1 + data2 + ' pattern');
});
rule('%.oo', '%.c', function() {
var cmd = util.format('cp %s %s', this.source, this.name);
console.log(cmd + ' task');
jake.exec([cmd], function () {
complete();
});
}, { async : true });
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Pattern Rule with Folder
// i.e. rule('tmpbin/%.oo', 'tmpsrc/%.c', ...
file('tmp_pf', [
'tmp_src_init'
, 'tmpbin'
, 'tmpbin/tmp_dep1.oo'
, 'tmpbin/tmp_dep2.oo' ], function (params) {
console.log('tmp pattern folder task');
var data1 = fs.readFileSync('tmpbin/tmp_dep1.oo');
var data2 = fs.readFileSync('tmpbin/tmp_dep2.oo');
fs.writeFileSync('tmp_pf', data1 + data2 + ' pattern folder');
});
rule('tmpbin/%.oo', 'tmpsrc/%.c', function() {
var cmd = util.format('cp %s %s', this.source, this.name);
console.log(cmd + ' task');
jake.exec([cmd], function () {
complete();
});
}, { async : true });
file('tmpsrc/tmp_dep2.c',['tmpsrc'], function () {
fs.writeFile('tmpsrc/tmp_dep2.c', 'src/src_2', function (err) {
if (err) {
throw err;
}
console.log('tmpsrc/tmp_dep2.c task');
complete();
});
}, {async: true});
// Create static files in folder tmpsrc.
task('tmp_src_init', ['tmpsrc'], function () {
fs.writeFile('tmpsrc/tmp_dep1.c', 'src/src_1', function (err) {
if (err) {
throw err;
}
console.log('tmpsrc/tmp_dep1.c task');
complete();
});
}, {async: true});
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Namespace Test. This is a Mixed Test.
// Test for
// - rules belonging to different namespace.
// - rules with folder and pattern
task('tmp_ns', [
'tmpbin'
, 'rule:init'
, 'tmpbin/tmp_dep2.oo' // *** This relies on a rule defined before.
, 'rule:tmpbin/dep1.oo'
, 'rule:tmpbin/file2.oo' ], function () {
console.log('tmp pattern folder namespace task');
var data1 = fs.readFileSync('tmpbin/dep1.oo');
var data2 = fs.readFileSync('tmpbin/tmp_dep2.oo');
var data3 = fs.readFileSync('tmpbin/file2.oo');
fs.writeFileSync('tmp_ns', data1 + data2 + data3 + ' pattern folder namespace');
});
namespace('rule', function() {
task('init', ['tmpsrc'], function () {
fs.writeFile('tmpsrc/file2.c', 'src/src_3', function (err) {
if (err) {
throw err;
}
console.log('tmpsrc/file2.c init task');
complete();
});
}, {async: true});
file('tmpsrc/dep1.c',['tmpsrc'], function () {
fs.writeFile('tmpsrc/dep1.c', 'src/src_1', function (err) {
if (err) {
throw err;
}
console.log('tmpsrc/dep1.c task');
complete();
});
}, {async: true});
rule('tmpbin/%.oo', 'tmpsrc/%.c', function() {
var cmd = util.format('cp %s %s', this.source, this.name);
console.log(cmd + ' ns task');
jake.exec([cmd], function () {
complete();
});
}, { async : true });
});
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Chain rule
// rule('tmpbin/%.pdf', 'tmpbin/%.dvi', function() { ...
// rule('tmpbin/%.dvi', 'tmpsrc/%.tex', ['tmpbin'], function() { ...
task('tmp_cr', [
'chainrule:init'
, 'chainrule:tmpbin/file1.pdf'
, 'chainrule:tmpbin/file2.pdf' ], function () {
console.log('tmp chainrule namespace task');
var data1 = fs.readFileSync('tmpbin/file1.pdf');
var data2 = fs.readFileSync('tmpbin/file2.pdf');
fs.writeFileSync('tmp_cr', data1 + data2 + ' chainrule namespace');
});
namespace('chainrule', function() {
task('init', ['tmpsrc', 'tmpbin'], function () {
fs.writeFileSync('tmpsrc/file1.tex', 'tex1 ');
fs.writeFileSync('tmpsrc/file2.tex', 'tex2 ');
console.log('chainrule init task');
});
rule('tmpbin/%.pdf', 'tmpbin/%.dvi', function() {
var cmd = util.format('cp %s %s', this.source, this.name);
console.log(cmd + ' dvi->pdf task');
jake.exec([cmd], function () {
complete();
});
}, { async : true });
rule('tmpbin/%.dvi', 'tmpsrc/%.tex', ['tmpbin'], function() {
var cmd = util.format('cp %s %s', this.source, this.name);
console.log(cmd + ' tex->dvi task');
jake.exec([cmd], function () {
complete();
});
}, { async : true });
});
////////////////////////////////////////////////////////////
namespace('precedence', function () {
task('test', ['foo.html'], function () {
console.log('ran test');
});
rule('.html', '.txt', function () {
console.log('created html');
var data = fs.readFileSync(this.source);
fs.writeFileSync(this.name, data.toString());
});
});
namespace('regexPattern', function () {
task('test', ['foo.html'], function () {
console.log('ran test');
});
rule(/\.html$/, '.txt', function () {
console.log('created html');
var data = fs.readFileSync(this.source);
fs.writeFileSync(this.name, data.toString());
});
});
namespace('sourceFunction', function () {
var srcFunc = function (taskName) {
return taskName.replace(/\.[^.]+$/, '.txt');
};
task('test', ['foo.html'], function () {
console.log('ran test');
});
rule('.html', srcFunc, function () {
console.log('created html');
var data = fs.readFileSync(this.source);
fs.writeFileSync(this.name, data.toString());
});
});
////////////////////////////////////////////////////////////
task('clean', function () {
utils.file.rmRf('./foo');
utils.file.rmRf('./tmp');
});
jake-0.7.4/test/exec.js 0000664 0000000 0000000 00000005611 12240565131 0014677 0 ustar 00root root 0000000 0000000 var assert = require('assert')
, h = require('./helpers')
, utils = require('../lib/utils');
utils.mixin(utils, utils);
var tests = {
'before': function () {
process.chdir('./test');
}
, 'after': function () {
process.chdir('../');
}
, 'test basic exec': function (next) {
var ex = utils.createExec('ls', function () {})
, evts = { // Events should fire in this order
cmdStart: [0, null]
, stdout: [1, null]
, cmdEnd: [2, null]
, end: [3, null]
}
, incr = 0; // Increment with each event to check order
assert.ok(ex instanceof utils.Exec);
var addListenerAndIncrement = function (p) {
ex.addListener(p, function () {
evts[p][1] = incr;
incr++;
});
};
// Make sure basic events fire and fire in the right order
for (var p in evts) {
addListenerAndIncrement(p);
}
ex.run();
ex.addListener('end', function () {
for (var p in evts) {
assert.equal(evts[p][0], evts[p][1]);
}
next();
});
}
, 'test an exec failure': function (next) {
var ex = utils.createExec('false', function () {});
ex.addListener('error', function (msg, code) {
assert.equal(1, code);
next();
});
ex.run();
}
, 'test exec stdout events': function (next) {
var ex = utils.createExec('echo "foo"', function () {});
ex.addListener('stdout', function (data) {
assert.equal("foo", h.trim(data.toString()));
next();
});
ex.run();
}
, 'test exec stderr events': function (next) {
var ex = utils.createExec('echo "foo" 1>&2', function () {});
ex.addListener('stderr', function (data) {
assert.equal("foo", h.trim(data.toString()));
next();
});
ex.run();
}
, 'test piping results into next command': function (next) {
var ex = utils.createExec('ls', function () {})
, data
, appended = false;
ex.addListener('stdout', function (d) {
data += h.trim(d.toString());
});
// After the first command ends, get the accumulated result,
// and use it to build a new command to append to the queue.
// Grep through the result for files that end in .js
ex.addListener('cmdEnd', function () {
// Only append after the first cmd, avoid infinite loop
if (appended) {
return;
}
appended = true;
// Take the original output and build the new command
ex.append('echo "' + data + '" | grep "\\.js$"');
// Clear out data
data = '';
});
// And the end, the stdout data has been cleared once, and the new
// data should be the result of the second command
ex.addListener('end', function () {
// Should be a list of files ending in .js
data = data.split('\n');
data.forEach(function (d) {
assert.ok(/\.js$/.test(d));
});
next();
});
ex.run();
}
};
module.exports = tests;
jake-0.7.4/test/file_list.js 0000664 0000000 0000000 00000001070 12240565131 0015720 0 ustar 00root root 0000000 0000000 var fs = require("fs");
var assert = require("assert");
module.exports["path separator can be used by exclude"] = function(next) {
jake.mkdirP("./test/tmp/one/two/three");
jake.mkdirP("./test/tmp/one/exclude");
fs.writeFileSync("./test/tmp/one/two/three/file.txt", "hello");
fs.writeFileSync("./test/tmp/one/exclude/file.txt", "world");
var fileList = new jake.FileList();
fileList.include("test/tmp/one/**/*.txt");
assert.equal(fileList.toArray().length, 2);
fileList.exclude("tmp/one/exclude");
assert.equal(fileList.toArray().length, 1);
next();
}; jake-0.7.4/test/file_task.js 0000664 0000000 0000000 00000007513 12240565131 0015717 0 ustar 00root root 0000000 0000000 var assert = require('assert')
, fs = require('fs')
, path = require('path')
, exec = require('child_process').exec
, h = require('./helpers')
, utils = require('utilities');
var cleanUpAndNext = function (callback) {
utils.file.rmRf('./foo', {
silent: true
});
callback();
};
var tests = {
'before': function (next) {
process.chdir('./test');
cleanUpAndNext(next);
}
, 'after': function () {
process.chdir('../');
}
, 'test concating two files': function (next) {
h.exec('../bin/cli.js fileTest:foo/concat.txt', function (out) {
var data;
assert.equal('fileTest:foo/src1.txt task\ndefault task\nfileTest:foo/src2.txt task\n' +
'fileTest:foo/concat.txt task', out);
// Check to see the two files got concat'd
data = fs.readFileSync(process.cwd() + '/foo/concat.txt');
assert.equal('src1src2', data.toString());
cleanUpAndNext(next);
});
}
, 'test where a file-task prereq does not change': function (next) {
h.exec('../bin/cli.js fileTest:foo/from-src1.txt', function (out) {
assert.equal('fileTest:foo/src1.txt task\nfileTest:foo/from-src1.txt task', out);
h.exec('../bin/cli.js fileTest:foo/from-src1.txt', function (out) {
// Second time should be a no-op
assert.equal('', out);
next(); // Don't clean up
});
});
}
, 'file-task where prereq file is modified': function (next) {
setTimeout(function () {
fs.writeFile('./foo/src1.txt', '', function (err, data) {
if (err) {
throw err;
}
h.exec('../bin/cli.js fileTest:foo/from-src1.txt', function (out) {
assert.equal('fileTest:foo/from-src1.txt task', out);
cleanUpAndNext(next);
});
});
}, 1000); // Wait to do the mod to ensure mod-time is different
}
, 'test where a file-task prereq does not change with --always-make': function (next) {
h.exec('../bin/cli.js fileTest:foo/from-src1.txt', function (out) {
assert.equal('fileTest:foo/src1.txt task\nfileTest:foo/from-src1.txt task',
out);
h.exec('../bin/cli.js -B fileTest:foo/from-src1.txt', function (out) {
assert.equal('fileTest:foo/src1.txt task\nfileTest:foo/from-src1.txt task',
out);
cleanUpAndNext(next);
});
});
}
, 'test a preexisting file': function (next) {
var prereqData = 'howdy';
utils.file.mkdirP('foo');
fs.writeFileSync('foo/prereq.txt', prereqData);
h.exec('../bin/cli.js fileTest:foo/from-prereq.txt', function (out) {
var data;
assert.equal('fileTest:foo/from-prereq.txt task', out);
data = fs.readFileSync(process.cwd() + '/foo/from-prereq.txt');
assert.equal(prereqData, data.toString());
h.exec('../bin/cli.js fileTest:foo/from-prereq.txt', function (out) {
// Second time should be a no-op
assert.equal('', out);
cleanUpAndNext(next);
});
});
}
, 'test a preexisting file with --always-make flag': function (next) {
var prereqData = 'howdy';
utils.file.mkdirP('foo');
fs.writeFileSync('foo/prereq.txt', prereqData);
h.exec('../bin/cli.js fileTest:foo/from-prereq.txt', function (out) {
var data;
assert.equal('fileTest:foo/from-prereq.txt task', out);
data = fs.readFileSync(process.cwd() + '/foo/from-prereq.txt');
assert.equal(prereqData, data.toString());
h.exec('../bin/cli.js -B fileTest:foo/from-prereq.txt', function (out) {
assert.equal('fileTest:foo/from-prereq.txt task', out);
cleanUpAndNext(next);
});
});
}
, 'test nested directory-task': function (next) {
h.exec('../bin/cli.js fileTest:foo/bar/baz/bamf.txt', function (out) {
var data = fs.readFileSync(process.cwd() + '/foo/bar/baz/bamf.txt');
assert.equal('w00t', data);
cleanUpAndNext(next);
});
}
};
module.exports = tests;
jake-0.7.4/test/helpers.js 0000664 0000000 0000000 00000003017 12240565131 0015413 0 ustar 00root root 0000000 0000000 var exec = require('child_process').exec;
var helpers = new (function () {
var _tests
, _names = []
, _name
, _callback
, _runner = function () {
if (!!(_name = _names.shift())) {
console.log('Running ' + _name);
_tests[_name]();
}
else {
_callback();
}
};
this.exec = function () {
var args = Array.prototype.slice.call(arguments)
, arg
, cmd = args.shift()
, opts = {}
, callback;
// Optional opts/callback or callback/opts
while ((arg = args.shift())) {
if (typeof arg == 'function') {
callback = arg;
}
else {
opts = arg;
}
}
cmd += ' --trace';
exec(cmd, function (err, stdout, stderr) {
var out = helpers.trim(stdout);
if (err) {
if (opts.breakOnError === false) {
return callback(err);
}
else {
throw err;
}
}
if (stderr) {
callback(stderr);
}
else {
callback(out);
}
});
};
this.trim = function (s) {
var str = s || '';
return str.replace(/^\s*|\s*$/g, '');
};
this.parse = function (s) {
var str = s || '';
str = helpers.trim(str);
str = str.replace(/'/g, '"');
return JSON.parse(str);
};
this.run = function (tests, callback) {
_tests = tests;
_names = Object.keys(tests);
_callback = callback;
_runner();
};
this.next = function () {
_runner();
};
})();
module.exports = helpers;
jake-0.7.4/test/namespace.js 0000664 0000000 0000000 00000002301 12240565131 0015700 0 ustar 00root root 0000000 0000000 // Load the jake global
require('../lib/jake');
var assert = require('assert')
, h = require('./helpers')
, api = require('../lib/api')
, Namespace = require('../lib/namespace').Namespace;
var tests = {
'before': function() {
process.chdir('./test');
}
, 'after': function() {
process.chdir('../');
}
, 'resolve namespace by relative name': function () {
var foo
, bar
, baz;
foo = namespace('foo', function () {
bar = namespace('bar', function () {
baz = namespace('baz', function () {
});
});
});
assert.ok(foo === baz.resolveNamespace('foo'),
'foo -> "foo"');
assert.ok(bar === baz.resolveNamespace('foo:bar'),
'bar -> "foo:bar"');
assert.ok(bar === baz.resolveNamespace('bar'),
'bar -> "bar"');
assert.ok(baz === baz.resolveNamespace('foo:bar:baz'),
'baz -> "foo:bar:baz"');
assert.ok(baz === baz.resolveNamespace('bar:baz'),
'baz -> "bar:baz"');
}
, 'test modifying a namespace by adding a new task': function(next) {
h.exec('../bin/cli.js one:two', function(out) {
assert.equal('one:one\none:two', out);
next();
});
}
};
module.exports = tests;
jake-0.7.4/test/parseargs.js 0000664 0000000 0000000 00000011073 12240565131 0015741 0 ustar 00root root 0000000 0000000 var parseargs = require('../lib/parseargs')
, assert = require('assert')
, optsReg = [
{ full: 'directory'
, abbr: 'C'
, preempts: false
, expectValue: true
}
, { full: 'jakefile'
, abbr: 'f'
, preempts: false
, expectValue: true
}
, { full: 'tasks'
, abbr: 'T'
, preempts: true
}
, { full: 'tasks'
, abbr: 'ls'
, preempts: true
}
, { full: 'trace'
, abbr: 't'
, preempts: false
, expectValue: false
}
, { full: 'help'
, abbr: 'h'
, preempts: true
}
, { full: 'version'
, abbr: 'V'
, preempts: true
}
]
, p = new parseargs.Parser(optsReg)
, z = function (s) { return s.split(' '); }
, res;
var tests = {
'test long preemptive opt and val with equal-sign, ignore further opts': function () {
res = p.parse(z('--tasks=foo --jakefile=asdf'));
assert.equal('foo', res.opts.tasks);
assert.equal(undefined, res.opts.jakefile);
}
, 'test long preemptive opt and val without equal-sign, ignore further opts': function () {
res = p.parse(z('--tasks foo --jakefile=asdf'));
assert.equal('foo', res.opts.tasks);
assert.equal(undefined, res.opts.jakefile);
}
, 'test long preemptive opt and no val, ignore further opts': function () {
res = p.parse(z('--tasks --jakefile=asdf'));
assert.equal(true, res.opts.tasks);
assert.equal(undefined, res.opts.jakefile);
}
, 'test preemptive opt with no val, should be true': function () {
res = p.parse(z('-T'));
assert.equal(true, res.opts.tasks);
}
, 'test preemptive opt with no val, should be true and ignore further opts': function () {
res = p.parse(z('-T -f'));
assert.equal(true, res.opts.tasks);
assert.equal(undefined, res.opts.jakefile);
}
, 'test preemptive opt with val, should be val': function () {
res = p.parse(z('-T zoobie -f foo/bar/baz'));
assert.equal('zoobie', res.opts.tasks);
assert.equal(undefined, res.opts.jakefile);
}
, 'test -f expects a value, -t does not (howdy is task-name)': function () {
res = p.parse(z('-f zoobie -t howdy'));
assert.equal('zoobie', res.opts.jakefile);
assert.equal(true, res.opts.trace);
assert.equal('howdy', res.taskNames[0]);
}
, 'test different order, -f expects a value, -t does not (howdy is task-name)': function () {
res = p.parse(z('-f zoobie howdy -t'));
assert.equal('zoobie', res.opts.jakefile);
assert.equal(true, res.opts.trace);
assert.equal('howdy', res.taskNames[0]);
}
, 'test -f expects a value, -t does not (foo=bar is env var)': function () {
res = p.parse(z('-f zoobie -t foo=bar'));
assert.equal('zoobie', res.opts.jakefile);
assert.equal(true, res.opts.trace);
assert.equal('bar', res.envVars.foo);
assert.equal(undefined, res.taskName);
}
, 'test -f expects a value, -t does not (foo=bar is env-var, task-name follows)': function () {
res = p.parse(z('-f zoobie -t howdy foo=bar'));
assert.equal('zoobie', res.opts.jakefile);
assert.equal(true, res.opts.trace);
assert.equal('bar', res.envVars.foo);
assert.equal('howdy', res.taskNames[0]);
}
, 'test -t does not expect a value, -f does (throw howdy away)': function () {
res = p.parse(z('-t howdy -f zoobie'));
assert.equal(true, res.opts.trace);
assert.equal('zoobie', res.opts.jakefile);
assert.equal(undefined, res.taskName);
}
, 'test --trace does not expect a value, -f does (throw howdy away)': function () {
res = p.parse(z('--trace howdy --jakefile zoobie'));
assert.equal(true, res.opts.trace);
assert.equal('zoobie', res.opts.jakefile);
assert.equal(undefined, res.taskName);
}
, 'test --trace does not expect a value (equal), -f does (throw howdy away)': function () {
res = p.parse(z('--trace=howdy --jakefile=zoobie'));
assert.equal(true, res.opts.trace);
assert.equal('zoobie', res.opts.jakefile);
assert.equal(undefined, res.taskName);
}
/*
, 'test task-name with positional args': function () {
res = p.parse(z('foo:bar[asdf,qwer]'));
assert.equal('asdf', p.taskArgs[0]);
assert.equal('qwer', p.taskArgs[1]);
}
, 'test opts, env vars, task-name with positional args': function () {
res = p.parse(z('-f ./tests/Jakefile -t default[asdf,qwer] foo=bar'));
assert.equal('./tests/Jakefile', res.opts.jakefile);
assert.equal(true, res.opts.trace);
assert.equal('bar', res.envVars.foo);
assert.equal('default', res.taskName);
assert.equal('asdf', p.taskArgs[0]);
assert.equal('qwer', p.taskArgs[1]);
}
*/
};
module.exports = tests;
jake-0.7.4/test/rule.js 0000664 0000000 0000000 00000015130 12240565131 0014717 0 ustar 00root root 0000000 0000000 var assert = require('assert')
, fs = require('fs')
, path = require('path')
, exec = require('child_process').exec
, h = require('./helpers')
, Matcher = require('../lib/rule').Matcher
, utils = require('utilities');
var cleanUpAndNext = function (callback) {
// Gotta add globbing to file utils rmRf
var tmpFiles = [
'tmp'
, 'tmp_ns'
, 'tmp_cr'
, 'tmp_p'
, 'tmp_pf'
, 'tmpbin'
, 'tmpsrc'
, 'tmp_dep1.c'
, 'tmp_dep1.o'
, 'tmp_dep1.oo'
, 'tmp_dep2.c'
, 'tmp_dep2.o'
, 'tmp_dep2.oo'
, 'foo'
, 'foo.html'
];
tmpFiles.forEach(function (f) {
utils.file.rmRf(f, {
silent: true
});
});
callback();
};
var tests = {
'before': function (next) {
process.chdir('./test');
cleanUpAndNext(next);
}
, 'after': function () {
process.chdir('../');
}
// - name foo:bin/main.o
// - pattern bin/%.o
// - source src/%.c
//
// return {
// 'dep' : 'foo:src/main.c',
// 'file': 'src/main.c'
// };
, 'test Matcher.getSource': function () {
var src = Matcher.getSource('foo:bin/main.o', 'bin/%.o', 'src/%.c');
assert.equal('foo:src/main.c', src);
}
, 'test rule w/o pattern': function (next) {
h.exec( '../bin/cli.js -f Jakefile.rule tmp', function (out) {
var output = [
"tmp_dep2.c task"
, "tmp_dep1.c task"
, "cp tmp_dep1.c tmp_dep1.o task"
, "cp tmp_dep2.c tmp_dep2.o task"
, "tmp task" ];
var data;
assert.equal( output.join('\n') , out);
data = fs.readFileSync(process.cwd() + '/tmp');
assert.equal('src_1src_2', data.toString());
cleanUpAndNext(next);
});
}
, 'test rule w pattern w/o folder w/o namespace': function (next) {
h.exec( '../bin/cli.js -f Jakefile.rule tmp_p', function (out) {
var output = [
"tmp_dep2.c task"
, "tmp_dep1.c task"
, "cp tmp_dep1.c tmp_dep1.oo task"
, "cp tmp_dep2.c tmp_dep2.oo task"
, "tmp pattern task" ];
var data;
assert.equal( output.join('\n') , out);
data = fs.readFileSync(process.cwd() + '/tmp_p');
assert.equal('src_1src_2 pattern', data.toString());
cleanUpAndNext(next);
});
}
, 'test rule w pattern w folder w/o namespace': function (next) {
h.exec( '../bin/cli.js -f Jakefile.rule tmp_pf', function (out) {
var output = [
"tmpsrc/tmp_dep1.c task"
, "cp tmpsrc/tmp_dep1.c tmpbin/tmp_dep1.oo task"
, "tmpsrc/tmp_dep2.c task"
, "cp tmpsrc/tmp_dep2.c tmpbin/tmp_dep2.oo task"
, "tmp pattern folder task" ];
var data;
assert.equal( output.join('\n') , out);
data = fs.readFileSync(process.cwd() + '/tmp_pf');
assert.equal('src/src_1src/src_2 pattern folder', data.toString());
cleanUpAndNext(next);
});
}
, 'test rule w pattern w folder w namespace': function (next) {
h.exec( '../bin/cli.js -f Jakefile.rule tmp_ns', function (out) {
var output = [
"tmpsrc/file2.c init task"
, "tmpsrc/tmp_dep2.c task"
, "cp tmpsrc/tmp_dep2.c tmpbin/tmp_dep2.oo task"
, "tmpsrc/dep1.c task"
, "cp tmpsrc/dep1.c tmpbin/dep1.oo ns task"
, "cp tmpsrc/file2.c tmpbin/file2.oo ns task"
, "tmp pattern folder namespace task" ];
var data;
assert.equal( output.join('\n') , out);
data = fs.readFileSync(process.cwd() + '/tmp_ns');
assert.equal('src/src_1src/src_2src/src_3 pattern folder namespace', data.toString());
cleanUpAndNext(next);
});
}
, 'test rule w chain w pattern w folder w namespace': function (next) {
h.exec( '../bin/cli.js -f Jakefile.rule tmp_cr', function (out) {
var output = [
"chainrule init task"
, "cp tmpsrc/file1.tex tmpbin/file1.dvi tex->dvi task"
, "cp tmpbin/file1.dvi tmpbin/file1.pdf dvi->pdf task"
, "cp tmpsrc/file2.tex tmpbin/file2.dvi tex->dvi task"
, "cp tmpbin/file2.dvi tmpbin/file2.pdf dvi->pdf task"
, "tmp chainrule namespace task" ];
var data;
assert.equal( output.join('\n') , out);
data = fs.readFileSync(process.cwd() + '/tmp_cr');
assert.equal('tex1 tex2 chainrule namespace', data.toString());
cleanUpAndNext(next);
});
}
};
['precedence', 'regexPattern', 'sourceFunction'].forEach(function (key) {
tests['test rule with source file not created yet (' + key + ')'] = function (next) {
utils.file.rmRf('foo.txt', {silent: true});
utils.file.rmRf('foo.html', {silent: true});
h.exec('../bin/cli.js -f Jakefile.rule ' + key + ':test', {breakOnError: false},
function (out) {
// foo.txt prereq doesn't exist yet
assert.ok(out.toString().indexOf('Unknown task "foo.html"') > -1);
next();
});
};
tests['test rule with source file now created (' + key + ')'] = function (next) {
fs.writeFileSync('foo.txt', '');
h.exec('../bin/cli.js -f Jakefile.rule ' + key + ':test', function (out) {
// Should run prereq and test task
var output = [
'created html'
, 'ran test'
];
assert.equal(output.join('\n'), out);
next();
});
};
tests['test rule with objective file now created (' + key + ')'] = function (next) {
fs.writeFileSync('foo.txt', '');
h.exec('../bin/cli.js -f Jakefile.rule ' + key + ':test', function (out) {
// Should only run test task
var output = [
'ran test'
];
assert.equal(output.join('\n'), out);
next();
});
};
tests['test rule with source file modified (' + key + ')'] = function (next) {
setTimeout(function () {
fs.writeFile('foo.txt', '', function (err, data) {
if (err) {
throw err;
}
h.exec('../bin/cli.js -f Jakefile.rule ' + key + ':test', function (out) {
// Should again run both prereq and test task
var output = [
'created html'
, 'ran test'
];
assert.equal(output.join('\n'), out);
//next();
cleanUpAndNext(next);
});
});
}, 1000); // Wait to do the touch to ensure mod-time is different
};
tests['test rule with existing objective file and no source ' +
' (should be normal file-task) (' + key + ')'] = function (next) {
// Remove just the source file
fs.writeFileSync('foo.html', '');
utils.file.rmRf('foo.txt', {silent: true});
h.exec('../bin/cli.js -f Jakefile.rule ' + key + ':test', function (out) {
// Should treat existing objective file as plain file-task,
// and just run test-task
var output = [
'ran test'
];
assert.equal(output.join('\n'), out);
cleanUpAndNext(next);
});
};
});
module.exports = tests;
jake-0.7.4/test/task_base.js 0000664 0000000 0000000 00000010254 12240565131 0015706 0 ustar 00root root 0000000 0000000 var assert = require('assert')
, h = require('./helpers');
var tests = {
'before': function () {
process.chdir('./test');
}
, 'after': function () {
process.chdir('../');
}
, 'test default task': function (next) {
h.exec('../bin/cli.js', function (out) {
assert.equal('default task', out);
h.exec('../bin/cli.js default', function (out) {
assert.equal('default task', out);
next();
});
});
}
, 'test task with no action': function (next) {
h.exec('../bin/cli.js noAction', function (out) {
assert.equal('default task', out);
next();
});
}
, 'test a task with no action and no prereqs': function (next) {
h.exec('../bin/cli.js noActionNoPrereqs', function () {
next();
});
}
, 'test passing args to a task': function (next) {
h.exec('../bin/cli.js argsEnvVars[foo,bar]', function (out) {
var parsed = h.parse(out)
, args = parsed.args;
assert.equal(args[0], 'foo');
assert.equal(args[1], 'bar');
next();
});
}
, 'test a task with environment vars': function (next) {
h.exec('../bin/cli.js argsEnvVars foo=bar baz=qux', function (out) {
var parsed = h.parse(out)
, env = parsed.env;
assert.equal(env.foo, 'bar');
assert.equal(env.baz, 'qux');
next();
});
}
, 'test passing args and using environment vars': function (next) {
h.exec('../bin/cli.js argsEnvVars[foo,bar] foo=bar baz=qux', function (out) {
var parsed = h.parse(out)
, args = parsed.args
, env = parsed.env;
assert.equal(args[0], 'foo');
assert.equal(args[1], 'bar');
assert.equal(env.foo, 'bar');
assert.equal(env.baz, 'qux');
next();
});
}
, 'test a simple prereq': function (next) {
h.exec('../bin/cli.js foo:baz', function (out) {
assert.equal('foo:bar task\nfoo:baz task', out);
next();
});
}
, 'test a duplicate prereq only runs once': function (next) {
h.exec('../bin/cli.js foo:asdf', function (out) {
assert.equal('foo:bar task\nfoo:baz task\nfoo:asdf task', out);
next();
});
}
, 'test a prereq with command-line args': function (next) {
h.exec('../bin/cli.js foo:qux', function (out) {
assert.equal('foo:bar[asdf,qwer] task\nfoo:qux task', out);
next();
});
}
, 'test a prereq with args via invoke': function (next) {
h.exec('../bin/cli.js foo:frang[zxcv,uiop]', function (out) {
assert.equal('foo:bar[zxcv,uiop] task\nfoo:frang task', out);
next();
});
}
, 'test prereq execution-order': function (next) {
h.exec('../bin/cli.js hoge:fuga', function (out) {
assert.equal('hoge:hoge task\nhoge:piyo task\nhoge:fuga task', out);
next();
});
}
, 'test basic async task': function (next) {
h.exec('../bin/cli.js bar:bar', function (out) {
assert.equal('bar:foo task\nbar:bar task', out);
next();
});
}
, 'test promise async task': function (next) {
h.exec('node ../bin/cli.js bar:dependOnpromise', function (out) {
assert.equal('bar:promise task\nbar:dependOnpromise task saw value 123654', out);
next();
});
}
, 'test failing promise async task': function (next) {
h.exec('node ../bin/cli.js bar:brokenPromise', {breakOnError:false}, function (out) {
assert.equal(1, out.code);
next();
});
}
, 'test that current-prereq index gets reset': function (next) {
h.exec('../bin/cli.js hoge:kira', function (out) {
assert.equal('hoge:hoge task\nhoge:piyo task\nhoge:fuga task\n' +
'hoge:charan task\nhoge:gero task\nhoge:kira task', out);
next();
});
}
, 'test modifying a task by adding prereq during execution': function (next) {
h.exec('../bin/cli.js voom', function (out) {
assert.equal(2, out);
next();
});
}
, 'test listening for task error-event': function (next) {
h.exec('../bin/cli.js vronk:groo', function (out) {
assert.equal('OMFGZONG', out);
next();
});
}
, 'test listening for jake error-event': function (next) {
h.exec('../bin/cli.js throwy', function (out) {
assert.equal(out, 'Emitted: Error: I am bad');
next();
});
}
};
module.exports = tests;