pax_global_header00006660000000000000000000000064123065423030014510gustar00rootroot0000000000000052 comment=9a9c112c02ad28574e332aa9fd884ff17951f45d jake-0.7.9/000077500000000000000000000000001230654230300124375ustar00rootroot00000000000000jake-0.7.9/.gitignore000066400000000000000000000001101230654230300144170ustar00rootroot00000000000000*.swp *.swo dist .idea/ tags nbproject/ node_modules tmtags *.DS_Store jake-0.7.9/.jshintignore000066400000000000000000000000151230654230300151370ustar00rootroot00000000000000node_modules jake-0.7.9/.jshintrc000066400000000000000000000004731230654230300142700ustar00rootroot00000000000000{ "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.9/.travis.yml000066400000000000000000000001221230654230300145430ustar00rootroot00000000000000language: node_js node_js: - "0.10" - "0.8" script: ./bin/cli.js test --trace jake-0.7.9/Jakefile000066400000000000000000000020241230654230300140720ustar00rootroot00000000000000var 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.9/Makefile000066400000000000000000000024471230654230300141060ustar00rootroot00000000000000# # 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.9/README.md000066400000000000000000001014331230654230300137200ustar00rootroot00000000000000### Jake -- JavaScript build tool for Node.js [![Build Status](https://travis-ci.org/mde/jake.png?branch=master)](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), the second (optional) argument is a list of prerequisite tasks to run before the tests, and the final 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', ['asdf', 'qwer'], 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. If you name your test 'beforeEach', it will run before each test. You can also name a test 'afterEach' for a test that runs 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 } , 'beforeEach': function () { // Something to do before every test } , 'afterEach': function () { // Something to do after every test } , '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.9/bin/000077500000000000000000000000001230654230300132075ustar00rootroot00000000000000jake-0.7.9/bin/cli.js000077500000000000000000000014211230654230300143150ustar00rootroot00000000000000#!/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.9/changelog.md000066400000000000000000000036101230654230300147100ustar00rootroot00000000000000### 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.9/jake000066400000000000000000000003051230654230300132720ustar00rootroot00000000000000#!/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.9/jake.bat000066400000000000000000000001051230654230300140350ustar00rootroot00000000000000@ECHO OFF @"%~dp0node.exe" "%~dp0/node_modules/jake/bin/cli.js" %* jake-0.7.9/lib/000077500000000000000000000000001230654230300132055ustar00rootroot00000000000000jake-0.7.9/lib/api.js000066400000000000000000000247141230654230300143240ustar00rootroot00000000000000/* * 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 () { var ctor = function () {} , t; ctor.prototype = jake.TestTask.prototype; t = new ctor(); jake.TestTask.apply(t, arguments); return t; }; })(); module.exports = api; jake-0.7.9/lib/file_list.js000066400000000000000000000176161230654230300155300ustar00rootroot00000000000000/* * 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.9/lib/jake.js000066400000000000000000000216461230654230300144660ustar00rootroot00000000000000/* * 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 var jakefileLoaded = loader.loadFile(opts.jakefile); var jakelibdirLoaded = loader.loadDirectory(opts.jakelibdir); if(!jakefileLoaded && !jakelibdirLoaded) { fail('No Jakefile. Specify a valid path with -f/--jakefile, ' + 'or place one in the current directory.'); } program.run(); } }; })()); module.exports = jake; jake-0.7.9/lib/loader.js000066400000000000000000000051701230654230300150140ustar00rootroot00000000000000/* * 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 , oldCwd = process.cwd(); 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()) { // Restore the working directory on failure process.chdir(oldCwd); return false; } return exists(); } }; if (!exists()) { return false; } isCoffee = existsSync(jakefile + '.coffee'); if (isCoffee) { CoffeeScript = _requireCoffee(); } require(utils.file.absolutize(jakefile)); return true; }; 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)); } }); return true; } return false; }; }; module.exports.Loader = Loader; jake-0.7.9/lib/namespace.js000066400000000000000000000032421230654230300155000ustar00rootroot00000000000000 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.9/lib/npm_publish_task.js000066400000000000000000000151001230654230300171020ustar00rootroot00000000000000/* * 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(); }, {printStdout: true, printStderr: 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.9/lib/package_task.js000066400000000000000000000231711230654230300161640ustar00rootroot00000000000000/* * 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.9/lib/parseargs.js000066400000000000000000000071201230654230300155320ustar00rootroot00000000000000/* * 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.9/lib/program.js000066400000000000000000000141451230654230300152170ustar00rootroot00000000000000/* * 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.9/lib/rule.js000066400000000000000000000176631230654230300145270ustar00rootroot00000000000000var 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.9/lib/task/000077500000000000000000000000001230654230300141475ustar00rootroot00000000000000jake-0.7.9/lib/task/directory_task.js000066400000000000000000000012421230654230300175320ustar00rootroot00000000000000var 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.9/lib/task/file_task.js000066400000000000000000000072711230654230300164550ustar00rootroot00000000000000var 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.9/lib/task/index.js000066400000000000000000000003541230654230300156160ustar00rootroot00000000000000 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.9/lib/task/task.js000066400000000000000000000132321230654230300154500ustar00rootroot00000000000000var 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.9/lib/test_task.js000066400000000000000000000167711230654230300155600ustar00rootroot00000000000000/* * 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 () { var self = this , args = Array.prototype.slice.call(arguments) , name = args.shift() , definition = args.pop() , prereqs = args.pop() || []; /** @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, prereqs, {async: true}, function () { var t = jake.Task[self.testName + ':run']; t.on('complete', function () { complete(); }); // Pass args to the namespaced test t.invoke.apply(t, arguments); }); 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.9/lib/utils/000077500000000000000000000000001230654230300143455ustar00rootroot00000000000000jake-0.7.9/lib/utils/index.js000066400000000000000000000137441230654230300160230ustar00rootroot00000000000000/* * 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.9/lib/utils/logger.js000066400000000000000000000007701230654230300161660ustar00rootroot00000000000000var 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.9/lib/watch_task.js000066400000000000000000000022161230654230300156740ustar00rootroot00000000000000FileList = 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.9/package.json000066400000000000000000000011101230654230300147160ustar00rootroot00000000000000{ "name": "jake", "description": "JavaScript build tool, similar to Make or Rake", "keywords": [ "build", "cli", "make", "rake" ], "version": "0.7.9", "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.9/test/000077500000000000000000000000001230654230300134165ustar00rootroot00000000000000jake-0.7.9/test/Jakefile000066400000000000000000000147771230654230300150730ustar00rootroot00000000000000var 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.9/test/Jakefile.rule000066400000000000000000000157601230654230300160320ustar00rootroot00000000000000var 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.9/test/exec.js000066400000000000000000000056111230654230300147030ustar00rootroot00000000000000var 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.9/test/file_list.js000066400000000000000000000010701230654230300157240ustar00rootroot00000000000000var 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.9/test/file_task.js000066400000000000000000000075131230654230300157230ustar00rootroot00000000000000var 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.9/test/helpers.js000066400000000000000000000030171230654230300154170ustar00rootroot00000000000000var 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.9/test/namespace.js000066400000000000000000000023011230654230300157040ustar00rootroot00000000000000// 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.9/test/parseargs.js000066400000000000000000000110731230654230300157450ustar00rootroot00000000000000var 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.9/test/rule.js000066400000000000000000000151301230654230300147230ustar00rootroot00000000000000var 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.9/test/task_base.js000066400000000000000000000102541230654230300157120ustar00rootroot00000000000000var 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;