Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit.

Reviewed by me.
  • Loading branch information...
commit b1e59c75b38c4a1d3ef974f4f121e239638cf4be 0 parents
@tolmasky tolmasky authored
5 bin/jake
@@ -0,0 +1,5 @@
+#!/usr/bin/env narwhal
+
+var jake = require("jake");
+
+jake.application().run();
658 lib/jake.js
@@ -0,0 +1,658 @@
+#!/usr/bin/env narwhal
+
+// Copyright 2009 280 North, Inc. (francisco@280north.com)
+// Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+var FILE = require("file"),
+ SYSTEM = require("system"),
+
+ Task = require("jake/task").Task,
+ FileTask = require("jake/filetask").FileTask,
+ FileCreationTask = require("jake/filecreationtask").FileCreationTask,
+ TaskManager = require("jake/taskmanager").TaskManager;
+
+var DEFAULT_JAKEFILES = ["jakefile", "Jakefile", "jakefile.js", "Jakefile.js", "jakefile.j", "Jakefile.j"];
+
+var Application = function()
+{
+ TaskManager.call(this);
+
+ this._name = "rake";
+ this._jakefiles = DEFAULT_JAKEFILES.slice();
+ this._jakefile = null;
+ this._options = { };
+// this._pendingImports = [];
+// this._imported = [];
+// this.loaders = { };
+// this.defaultLoader = Rake::DefaultLoader.new
+ this._originalDirectory = FILE.cwd();
+ this._topLevelTasks = [];
+/*
+ add_loader('rb', DefaultLoader.new)
+ add_loader('rf', DefaultLoader.new)
+ add_loader('rake', DefaultLoader.new)
+ @tty_output = STDOUT.tty?*/
+}
+
+Application.__proto__ = TaskManager;
+Application.prototype.__proto__ = TaskManager.prototype;
+
+// Run the Rake application. The run method performs the following three steps:
+//
+// * Initialize the command line options (+init+).
+// * Define the tasks (+loadRakefile+).
+// * Run the top level tasks (+runTasks+).
+//
+// If you wish to build a custom rake command, you should call +init+ on your
+// application. The define any tasks. Finally, call +topLevel+ to run your top
+// level tasks.
+Application.prototype.run = function()
+{
+ this.init();
+ this.loadRakefile();
+ this.topLevel();
+}
+
+// Initialize the command line parameters and app name.
+Application.prototype.init = function(/*String*/ anApplicationName)
+{
+ this._name = anApplicationName || "rake";
+// this.handleOptions();
+//FIXME: options
+ this.collectTasks();
+}
+
+// Find the rakefile and then load it and any pending imports.
+Application.prototype.loadRakefile = function()
+{
+ this.rawLoadJakefile();
+}
+
+// Run the top level tasks of a Rake application.
+Application.prototype.topLevel = function()
+{/*
+ if (options.showTasks())
+ this.displayTasksAndComments();
+
+ else if (options.showPrereqs)
+ this.displayPreq;
+ else
+*/
+ this._topLevelTasks.forEach(function(/*String*/ aTaskName)
+ {
+ this.invokeTask(aTaskName);
+ }, this);
+}
+
+/*
+ # Add a loader to handle imported files ending in the extension
+ # +ext+.
+Application.prototype.
+
+ def add_loader(ext, loader)
+ ext = ".#{ext}" unless ext =~ /^\./
+ @loaders[ext] = loader
+ end
+
+ # Application options from the command line
+ def options
+ @options ||= OpenStruct.new
+ end
+
+ # private ----------------------------------------------------------------
+*/
+
+Application.prototype.invokeTask = function(/*String*/ aTaskString)
+{
+ var result = this.parseTaskString(aTaskString),
+ task = this.lookupTask(result[0]);
+
+ task.invoke.apply(task, result[1]);
+}
+
+Application.prototype.parseTaskString = function(/*String*/ aString)
+{
+ var matches = aString.match(/^([^\[]+)(\[(.*)\])$/);
+
+ if (matches)
+ return [matches[0], matches[3].split(/\s*,\s*/)];
+
+ return [aString, []];
+}
+/*
+ # Provide standard execption handling for the given block.
+ def standard_exception_handling
+ begin
+ yield
+ rescue SystemExit => ex
+ # Exit silently with current status
+ exit(ex.status)
+ rescue SystemExit, OptionParser::InvalidOption => ex
+ # Exit silently
+ exit(1)
+ rescue Exception => ex
+ # Exit with error message
+ $stderr.puts "#{name} aborted!"
+ $stderr.puts ex.message
+ if options.trace
+ $stderr.puts ex.backtrace.join("\n")
+ else
+ $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
+ $stderr.puts "(See full trace by running task with --trace)"
+ end
+ exit(1)
+ end
+ end
+*/
+
+// True if one of the files in RAKEFILES is in the current directory.
+// If a match is found, it is copied into @rakefile.
+Application.prototype.hasJakefile = function(/*String*/ aDirectory)
+{
+ var jakefiles = this._jakefiles,
+ index = 0,
+ count = jakefiles.length;
+
+ for (; index < count; ++index)
+ {
+ var jakefile = jakefiles[index];
+
+ if (FILE.exists(FILE.join(aDirectory, jakefile)))
+ return jakefile;
+
+ else if (jakefile === "")
+ return null;
+ }
+
+ return null;
+}
+/*
+ # True if we are outputting to TTY, false otherwise
+ def tty_output?
+ @tty_output
+ end
+
+ # Override the detected TTY output state (mostly for testing)
+ def tty_output=( tty_output_state )
+ @tty_output = tty_output_state
+ end
+
+ # We will truncate output if we are outputting to a TTY or if we've been
+ # given an explicit column width to honor
+ def truncate_output?
+ tty_output? || ENV['RAKE_COLUMNS']
+ end
+
+ # Display the tasks and comments.
+ def display_tasks_and_comments
+ displayable_tasks = tasks.select { |t|
+ t.comment && t.name =~ options.show_task_pattern
+ }
+ if options.full_description
+ displayable_tasks.each do |t|
+ puts "#{name} #{t.name_with_args}"
+ t.full_comment.split("\n").each do |line|
+ puts " #{line}"
+ end
+ puts
+ end
+ else
+ width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
+ displayable_tasks.each do |t|
+ printf "#{name} %-#{width}s # %s\n",
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
+ end
+ end
+ end
+
+ def terminal_width
+ if ENV['RAKE_COLUMNS']
+ result = ENV['RAKE_COLUMNS'].to_i
+ else
+ result = unix? ? dynamic_width : 80
+ end
+ (result < 10) ? 80 : result
+ rescue
+ 80
+ end
+
+ # Calculate the dynamic width of the
+ def dynamic_width
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
+ end
+
+ def dynamic_width_stty
+ %x{stty size 2>/dev/null}.split[1].to_i
+ end
+
+ def dynamic_width_tput
+ %x{tput cols 2>/dev/null}.to_i
+ end
+
+ def unix?
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
+ end
+
+ def windows?
+ Win32.windows?
+ end
+
+ def truncate(string, width)
+ if string.length <= width
+ string
+ else
+ ( string[0, width-3] || "" ) + "..."
+ end
+ end
+
+ # Display the tasks and prerequisites
+ def display_prerequisites
+ tasks.each do |t|
+ puts "#{name} #{t.name}"
+ t.prerequisites.each { |pre| puts " #{pre}" }
+ end
+ end
+
+ # A list of all the standard options used in rake, suitable for
+ # passing to OptionParser.
+ def standard_rake_options
+ [
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
+ lambda { |value|
+ require 'rake/classic_namespace'
+ options.classic_namespace = true
+ }
+ ],
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
+ lambda { |value|
+ options.show_tasks = true
+ options.full_description = true
+ options.show_task_pattern = Regexp.new(value || '')
+ }
+ ],
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
+ lambda { |value|
+ verbose(true)
+ nowrite(true)
+ options.dryrun = true
+ options.trace = true
+ }
+ ],
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
+ lambda { |value|
+ eval(value)
+ exit
+ }
+ ],
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
+ lambda { |value|
+ puts eval(value)
+ exit
+ }
+ ],
+ ['--execute-continue', '-E CODE',
+ "Execute some Ruby code, then continue with normal task processing.",
+ lambda { |value| eval(value) }
+ ],
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
+ lambda { |value| $:.push(value) }
+ ],
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
+ lambda { |value| options.show_prereqs = true }
+ ],
+ ['--quiet', '-q', "Do not log messages to standard output.",
+ lambda { |value| verbose(false) }
+ ],
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
+ lambda { |value|
+ value ||= ''
+ @rakefiles.clear
+ @rakefiles << value
+ }
+ ],
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
+ lambda { |value| options.rakelib = value.split(':') }
+ ],
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
+ lambda { |value|
+ begin
+ require value
+ rescue LoadError => ex
+ begin
+ rake_require value
+ rescue LoadError => ex2
+ raise ex
+ end
+ end
+ }
+ ],
+ ['--rules', "Trace the rules resolution.",
+ lambda { |value| options.trace_rules = true }
+ ],
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
+ lambda { |value| options.nosearch = true }
+ ],
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
+ lambda { |value|
+ verbose(false)
+ options.silent = true
+ }
+ ],
+ ['--system', '-g',
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
+ lambda { |value| options.load_system = true }
+ ],
+ ['--no-system', '--nosystem', '-G',
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
+ lambda { |value| options.ignore_system = true }
+ ],
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
+ lambda { |value|
+ options.show_tasks = true
+ options.show_task_pattern = Regexp.new(value || '')
+ options.full_description = false
+ }
+ ],
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
+ lambda { |value|
+ options.trace = true
+ verbose(true)
+ }
+ ],
+ ['--verbose', '-v', "Log message to standard output.",
+ lambda { |value| verbose(true) }
+ ],
+ ['--version', '-V', "Display the program version.",
+ lambda { |value|
+ puts "rake, version #{RAKEVERSION}"
+ exit
+ }
+ ]
+ ]
+ end
+
+ # Read and handle the command line options.
+ def handle_options
+ options.rakelib = ['rakelib']
+
+ OptionParser.new do |opts|
+ opts.banner = "rake [-f rakefile] {options} targets..."
+ opts.separator ""
+ opts.separator "Options are ..."
+
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
+ puts opts
+ exit
+ end
+
+ standard_rake_options.each { |args| opts.on(*args) }
+ end.parse!
+
+ # If class namespaces are requested, set the global options
+ # according to the values in the options structure.
+ if options.classic_namespace
+ $show_tasks = options.show_tasks
+ $show_prereqs = options.show_prereqs
+ $trace = options.trace
+ $dryrun = options.dryrun
+ $silent = options.silent
+ end
+ end
+
+ # Similar to the regular Ruby +require+ command, but will check
+ # for *.rake files in addition to *.rb files.
+ def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
+ return false if loaded.include?(file_name)
+ paths.each do |path|
+ fn = file_name + ".rake"
+ full_path = File.join(path, fn)
+ if File.exist?(full_path)
+ load full_path
+ loaded << fn
+ return true
+ end
+ end
+ fail LoadError, "Can't find #{file_name}"
+ end
+*/
+
+Application.prototype.findJakefileLocation = function()
+{
+ var directory = FILE.cwd(),
+ filename = null;
+
+ while (!(filename = this.hasJakefile(directory)))// && !this._options.nosearch)
+ directory = FILE.join(directory, "..");
+
+ if (!filename)
+ return null;
+
+ return [filename, directory];
+}
+
+Application.prototype.rawLoadJakefile = function()
+{
+ var result = this.findJakefileLocation(),
+ jakefile = result ? result[0] : null,
+ location = result ? result[1] : null,
+ options = this._options;
+/*
+ if (!options.ignore_system && (options.load_system || !rakefile) && system_dir && FILE.directory?(system_dir)
+ if (options["silent"])
+ print("(in ");
+ puts "(in #{Dir.pwd})" unless options.silent
+ glob("#{system_dir}/*.rake") do |name|
+ add_import name
+ end
+ }
+ else*/
+ {
+ if (!jakefile)
+ throw "No Jakefile found (looking for: " + this._rakefiles.join(', ') + ")";
+
+ this._jakefile = jakefile;
+
+// file.chdir(location);
+
+ if (!options["silent"])
+ print("(in " + FILE.cwd() + ")");
+
+// $rakefile = @rakefile if options.classic_namespace
+
+ if (jakefile && jakefile.length)//expand_path?
+ require(FILE.absolute(FILE.join(location, jakefile)));
+
+/* options.rakelib.each do |rlib|
+ glob("#{rlib}/*.rake") do |name|
+ add_import name
+ end
+ */
+ }
+ //load_imports
+}
+
+/*
+ def glob(path, &block)
+ Dir[path.gsub("\\", '/')].each(&block)
+ end
+ private :glob
+*/
+/*
+ # The directory path containing the system wide rakefiles.
+ def system_dir
+ @system_dir ||=
+ begin
+ if ENV['RAKE_SYSTEM']
+ ENV['RAKE_SYSTEM']
+ elsif Win32.windows?
+ Win32.win32_system_dir
+ else
+ standard_system_dir
+ end
+ end
+ end
+
+ # The standard directory containing system wide rake files.
+ def standard_system_dir #:nodoc:
+ File.join(File.expand_path('~'), '.rake')
+ end
+ private :standard_system_dir
+*/
+
+// Collect the list of tasks on the command line. If no tasks are
+// given, return a list containing only the default task.
+// Environmental assignments are processed at this time as well.
+Application.prototype.collectTasks = function()
+{
+ this._topLevelTasks = [];
+
+ var topLevelTasks = this._topLevelTasks;
+
+ SYSTEM.args.slice(1).forEach(function(/*String*/ anArgument)
+ {
+ var matches = anArgument.match(/^(\w+)=(.*)$/);
+
+ if (matches)
+ SYSTEM.env[matches[1]] = matches[2];
+
+ else if (!anArgument.match(/^-/))
+ topLevelTasks.push(anArgument);
+ });
+
+ if (topLevelTasks.length <= 0)
+ topLevelTasks.push("default");
+}
+/*
+ # Add a file to the list of files to be imported.
+ def add_import(fn)
+ @pending_imports << fn
+ end
+
+ # Load the pending list of imported files.
+ def load_imports
+ while fn = @pending_imports.shift
+ next if @imported.member?(fn)
+ if fn_task = lookup(fn)
+ fn_task.invoke
+ end
+ ext = File.extname(fn)
+ loader = @loaders[ext] || @default_loader
+ loader.load(fn)
+ @imported << fn
+ end
+ end
+
+ # Warn about deprecated use of top level constant names.
+ def const_warning(const_name)
+ @const_warning ||= false
+ if ! @const_warning
+ $stderr.puts %{WARNING: Deprecated reference to top-level constant '#{const_name}' } +
+ %{found at: #{rakefile_location}} # '
+ $stderr.puts %{ Use --classic-namespace on rake command}
+ $stderr.puts %{ or 'require "rake/classic_namespace"' in Rakefile}
+ end
+ @const_warning = true
+ end
+
+ def rakefile_location
+ begin
+ fail
+ rescue RuntimeError => ex
+ ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
+ end
+ end
+ */
+
+// Exports
+exports.Task = Task;
+exports.FileTask = FileTask;
+exports.FileCreationTask = FileCreationTask;
+exports.TaskManager = TaskManager;
+exports.Application = Application;
+
+var application = null;
+
+exports.application = function()
+{
+ if (!application)
+ application = new Application();
+
+ return application;
+}
+
+exports.setApplication = function(/*Application*/ anApplication)
+{
+ application = anApplication;
+}
+
+exports.EARLY = new Date(-10000,1,1,0,0,0,0).getTime();
+
+exports.task = function()
+{
+ return Task.defineTask.apply(Task, arguments);
+}
+
+exports.file = function()
+{
+ return FileTask.defineTask.apply(FileTask, arguments);
+}
+
+exports.fileCreate = function()
+{
+ return FileCreationTask.defineTask.apply(FileCreationTask, arguments);
+}
+
+exports.directory = function(aDirectory)
+{
+ var oldLength = null;
+
+ while (aDirectory !== "." && aDirectory.length !== oldLength)
+ {
+ exports.fileCreate(aDirectory, function(aTask)
+ {
+ var taskName = aTask.name();
+
+ if (!FILE.exists(taskName))
+ FILE.mkdirs(taskName);
+ });
+
+ oldLength = aDirectory.length;
+ aDirectory = FILE.dirname(aDirectory);
+ }
+}
+
+exports.filedir = function()
+{
+ var fileTask = FileTask.defineTask.apply(FileTask, arguments),
+ fileDirectory = FILE.dirname(fileTask.name());
+
+ exports.directory (fileDirectory);
+ exports.file (fileTask.name(), fileDirectory);
+}
+/*
+ # Return the original directory where the Rake application was started.
+ def original_dir
+ application.original_dir
+ end
+*/
66 lib/jake/clean.js
@@ -0,0 +1,66 @@
+#!/usr/bin/env narwhal
+
+// Copyright 2009 280 North, Inc. (francisco@280north.com)
+// Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+
+// The 'jake/clean' file defines two file lists (CLEAN and CLOBBER) and
+// two jake tasks ("clean" and "clobber").
+//
+// ["clean"] Clean up the project by deleting scratch files and backup
+// files. Add files to the CLEAN file list to have the :clean
+// target handle them.
+//
+// ["clobber"] Clobber all generated and non-source files in a project.
+// The task depends on :clean, so all the clean files will
+// be deleted as well as files in the CLOBBER file list.
+// The intent of this task is to return a project to its
+// pristine, just unpacked state.
+
+var Jake = require("jake");
+
+CLEAN = [];//new Jake.FileList("**/*~", "**/*.bak", "**/core");
+/*
+CLEAN.clearExclude().exclude(function(aFilename)
+{
+ aFilename.pathmap("%f") == 'core' && File.directory?(fn)
+});*/
+
+//desc "Remove any temporary products."
+task ("clean", function()
+{
+ CLEAN.forEach(function(aFilename)
+ {
+ FILE.rmTree(aFilename);
+ });
+});
+
+CLOBBER = new Jake::FileList;
+
+//desc "Remove any generated file."
+task ("clobber", ["clean"], function()
+{
+ CLOBBER.forEach(function(aFilename)
+ {
+ FILE.rmTree(aFilename);
+ });
+});
58 lib/jake/filecreationtask.js
@@ -0,0 +1,58 @@
+#!/usr/bin/env narwhal
+
+// Copyright 2009 280 North, Inc. (francisco@280north.com)
+// Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+var FILE = require("file"),
+ Jake = require("jake"),
+ FileTask = require("jake/filetask").FileTask;
+
+// #########################################################################
+// A FileCreationTask is a file task that when used as a dependency will be
+// needed if and only if the file has not been created. Once created, it is
+// not re-triggered if any of its dependencies are newer, nor does trigger
+// any rebuilds of tasks that depend on it whenever it is updated.
+//
+var FileCreationTask = function()
+{
+ FileTask.apply(this, arguments);
+}
+
+FileCreationTask.__proto__ = FileTask;
+FileCreationTask.prototype.__proto__ = FileTask.prototype;
+
+//print("IT IS " + FileTask.defineTask + " " + FileCreationTask.defineTask);
+// Is this file task needed? Yes if it doesn't exist.
+FileCreationTask.prototype.isNeeded = function()
+{
+ return !FILE.exists(this.name());
+}
+
+// Time stamp for file creation task. This time stamp is earlier
+// than any other time stamp.
+FileCreationTask.prototype.timestamp = function()
+{
+ return Jake.EARLY;
+}
+
+// Exports
+exports.FileCreationTask = FileCreationTask;
428 lib/jake/filelist.js
@@ -0,0 +1,428 @@
+#!/usr/bin/env narwhal
+
+// Copyright 2009 280 North, Inc. (francisco@280north.com)
+// Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+
+// A FileList is essentially an array with a few helper methods defined to
+// make file manipulation a bit easier.
+//
+// FileLists are lazy. When given a list of glob patterns for possible files
+// to be included in the file list, instead of searching the file structures
+// to find the files, a FileList holds the pattern for latter use.
+//
+// This allows us to define a number of FileList to match any number of
+// files, but only search out the actual files when then FileList itself is
+// actually used. The key is that the first time an element of the
+// FileList/Array is requested, the pending patterns are resolved into a real
+// list of file names.
+//
+
+function isRegExp(anObject)
+{
+ return typeof(anObject) === "function") && anObject.constructor.toString().match(/regexp/i) !== null
+}
+
+// Create a file list from the globbable patterns given.
+//
+// Example:
+// file_list = new FileList('lib/**/*.rb', 'test/test*.rb')
+//
+function FileList()
+{
+ this._pending_add = []
+ this._pending = false
+ this._excludePatterns = DEFAULT_IGNORE_PATTERNS.slice();
+ this._excludeProcs = DEFAULT_IGNORE_PROCS.slice();
+ this._excludeRe = nil
+ this._items = []
+ this.include.apply(this, arguments);
+}
+ # == Method Delegation
+ #
+ # The lazy evaluation magic of FileLists happens by implementing all the
+ # array specific methods to call +resolve+ before delegating the heavy
+ # lifting to an embedded array object (@items).
+ #
+ # In addition, there are two kinds of delegation calls. The regular kind
+ # delegates to the @items array and returns the result directly. Well,
+ # almost directly. It checks if the returned value is the @items object
+ # itself, and if so will return the FileList object instead.
+ #
+ # The second kind of delegation call is used in methods that normally
+ # return a new Array object. We want to capture the return value of these
+ # methods and wrap them in a new FileList object. We enumerate these
+ # methods in the +SPECIAL_RETURN+ list below.
+
+ # List of array methods (that are not in +Object+) that need to be
+ # delegated.
+ ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
+
+ # List of additional methods that must be delegated.
+ MUST_DEFINE = %w[to_a inspect]
+
+ # List of methods that should not be delegated here (we define special
+ # versions of them explicitly below).
+ MUST_NOT_DEFINE = %w[to_a to_ary partition *]
+
+ # List of delegated methods that return new array values which need
+ # wrapping.
+ SPECIAL_RETURN = %w[
+ map collect sort sort_by select find_all reject grep
+ compact flatten uniq values_at
+ + - & |
+ ]
+
+ DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
+
+ # Now do the delegation.
+ DELEGATING_METHODS.each_with_index do |sym, i|
+ if SPECIAL_RETURN.include?(sym)
+ ln = __LINE__+1
+ class_eval %{
+ def #{sym}(*args, &block)
+ resolve
+ result = @items.send(:#{sym}, *args, &block)
+ FileList.new.import(result)
+ end
+ }, __FILE__, ln
+ else
+ ln = __LINE__+1
+ class_eval %{
+ def #{sym}(*args, &block)
+ resolve
+ result = @items.send(:#{sym}, *args, &block)
+ result.object_id == @items.object_id ? self : result
+ end
+ }, __FILE__, ln
+ end
+ end
+
+
+
+// Add file names defined by glob patterns to the file list. If an array
+// is given, add each element of the array.
+//
+// Example:
+// file_list.include("*.java", "*.cfg")
+// file_list.include %w( math.c lib.h *.o )
+//
+FileList.prototype.include = function()
+{
+ // TODO: check for pending
+ Array.prototype.forEach.apply(arguments, function(/*Object*/ anArgument)
+ {
+ if (Array.isArray(anArgument))
+ this.include.apply(this, anArgument);
+ else if (typeof anArgument.toArray === "function")
+ this.include.apply(this, anArgument.toArray());
+ else
+ this._pendingAdd.push(aFilename);
+ }, this);
+
+ this._pending = true;
+
+ return this;
+}
+
+FileList.prototype.add = FileList.prototype.include;
+
+// Register a list of file name patterns that should be excluded from the
+// list. Patterns may be regular expressions, glob patterns or regular
+// strings. In addition, a block given to exclude will remove entries that
+// return true when given to the block.
+//
+// Note that glob patterns are expanded against the file system. If a file
+// is explicitly added to a file list, but does not exist in the file
+// system, then an glob pattern in the exclude list will not exclude the
+// file.
+//
+// Examples:
+// FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
+// FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c']
+//
+// If "a.c" is a file, then ...
+// FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
+//
+// If "a.c" is not a file, then ...
+// FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
+//
+FileList.prototype.exclude = function()
+{
+ Array.prototype.forEach.apply(arguments, function(argument)
+ {
+ if (typeof argument === "function")
+ this._exlcudeProcs.push(argument);
+ else
+ this._excludePatterns.push(argument);
+ }, this);
+
+ if (!this._pending)
+ this.resolveExclude();
+
+ return this;
+}
+
+FileList.prototype.clearExclude = function()
+{
+ this._excludePatterns = [];
+ this._excludeProcs = [];
+
+ if (!this._pending)
+ this.calculateExcludeRegexp();
+
+ return this;
+}
+
+FileList.prototype.toArray = function()
+{
+ this.resolve();
+ return this._items;
+}
+/*
+ # Lie about our class.
+ def is_a?(klass)
+ klass == Array || super(klass)
+ end
+ alias kind_of? is_a?
+
+ # Redefine * to return either a string or a new file list.
+ def *(other)
+ result = @items * other
+ case result
+ when Array
+ FileList.new.import(result)
+ else
+ result
+ end
+ end
+*/
+
+// Resolve all the pending adds now.
+FileList.prototype.resolve = function()
+{
+ if (this._pending)
+ {
+ this._pending = false;
+
+ this._pendingAdd.forEach(function(/*String*/ aFileName)
+ {
+ this.resolveAdd(aFilename);
+ }, this);
+
+ this._pendingAdd = [];
+
+ this.resolveExclude();
+ }
+
+ return this;
+}
+
+FileList.prototype.calculateExcludeRegexp = function()
+{
+ var ignores = [];
+
+ this._excludePatterns.forEach(function(aPattern)
+ {
+ if (isRegExp(aPattern))
+ ignores.push(aPattern)
+ /*
+ else if ()
+
+ case pat
+ when Regexp
+ ignores << pat
+ when /[*?]/
+ Dir[pat].each do |p| ignores << p end
+ else
+ ignores << Regexp.quote(pat)
+ end*/
+ end
+ if ignores.empty?
+ @exclude_re = /^$/
+ else
+ re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
+ @exclude_re = Regexp.new(re_str)
+ end
+ end
+}
+
+FileList.prototype.resolveAdd = function(/*String*/ aFilename)
+{
+
+ case fn
+ when %r{[*?\[\{]}
+ add_matching(fn)
+ else
+ self << fn
+ end
+ end
+ private :resolve_add
+
+ def resolve_exclude
+ calculate_exclude_regexp
+ reject! { |fn| exclude?(fn) }
+ self
+ end
+ private :resolve_exclude
+}
+
+ # Return a new FileList with the results of running +sub+ against each
+ # element of the oringal list.
+ #
+ # Example:
+ # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
+ #
+ def sub(pat, rep)
+ inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
+ end
+
+ # Return a new FileList with the results of running +gsub+ against each
+ # element of the original list.
+ #
+ # Example:
+ # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
+ # => ['lib\\test\\file', 'x\\y']
+ #
+ def gsub(pat, rep)
+ inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
+ end
+
+ # Same as +sub+ except that the oringal file list is modified.
+ def sub!(pat, rep)
+ each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
+ self
+ end
+
+ # Same as +gsub+ except that the original file list is modified.
+ def gsub!(pat, rep)
+ each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
+ self
+ end
+
+ # Apply the pathmap spec to each of the included file names, returning a
+ # new file list with the modified paths. (See String#pathmap for
+ # details.)
+ def pathmap(spec=nil)
+ collect { |fn| fn.pathmap(spec) }
+ end
+
+ # Return a new file list with <tt>String#ext</tt> method applied
+ # to each member of the array.
+ #
+ # This method is a shortcut for:
+ #
+ # array.collect { |item| item.ext(newext) }
+ #
+ # +ext+ is a user added method for the Array class.
+ def ext(newext='')
+ collect { |fn| fn.ext(newext) }
+ end
+
+
+ # Grep each of the files in the filelist using the given pattern. If a
+ # block is given, call the block on each matching line, passing the file
+ # name, line number, and the matching line of text. If no block is given,
+ # a standard emac style file:linenumber:line message will be printed to
+ # standard out.
+ def egrep(pattern)
+ each do |fn|
+ open(fn) do |inf|
+ count = 0
+ inf.each do |line|
+ count += 1
+ if pattern.match(line)
+ if block_given?
+ yield fn, count, line
+ else
+ puts "#{fn}:#{count}:#{line}"
+ end
+ end
+ end
+ end
+ end
+ end
+
+ # Return a new file list that only contains file names from the current
+ # file list that exist on the file system.
+ def existing
+ select { |fn| File.exist?(fn) }
+ end
+
+ # Modify the current file list so that it contains only file name that
+ # exist on the file system.
+ def existing!
+ resolve
+ @items = @items.select { |fn| File.exist?(fn) }
+ self
+ end
+
+ # FileList version of partition. Needed because the nested arrays should
+ # be FileLists in this version.
+ def partition(&block) # :nodoc:
+ resolve
+ result = @items.partition(&block)
+ [
+ FileList.new.import(result[0]),
+ FileList.new.import(result[1]),
+ ]
+ end
+
+ # Convert a FileList to a string by joining all elements with a space.
+ def to_s
+ resolve
+ self.join(' ')
+ end
+
+ # Add matching glob patterns.
+ def add_matching(pattern)
+ Dir[pattern].each do |fn|
+ self << fn unless exclude?(fn)
+ end
+ end
+ private :add_matching
+
+ # Should the given file name be excluded?
+ def exclude?(fn)
+ calculate_exclude_regexp unless @exclude_re
+ fn =~ @exclude_re || @exclude_procs.any? { |p| p.call(fn) }
+ end
+
+var DEFAULT_IGNORE_PATTERNS =
+ [
+ /(^|[\/\\])CVS([\/\\]|$)/,
+ /(^|[\/\\])\.svn([\/\\]|$)/,
+ /\.bak$/,
+ /~$/
+ ];
+
+ DEFAULT_IGNORE_PROCS =
+ [
+ proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) }
+ ]
+# @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
+
+FileList.prototype.import = function(/*Array*/ anArray)
+{
+ this._items = array
+ return this;
+}
76 lib/jake/filetask.js
@@ -0,0 +1,76 @@
+#!/usr/bin/env narwhal
+
+// Copyright 2009 280 North, Inc. (francisco@280north.com)
+// Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+var FILE = require("file"),
+ Jake = require("jake"),
+ Task = require("jake/task").Task;
+
+// A FileTask is a task that includes time based dependencies. If any of a
+// FileTask's prerequisites have a timestamp that is later than the file
+// represented by this task, then the file must be rebuilt (using the
+// supplied actions).
+var FileTask = function()
+{
+ Task.apply(this, arguments);
+}
+
+FileTask.__proto__ = Task;
+FileTask.prototype.__proto__ = Task.prototype;
+
+// Is this file task needed? Yes if it doesn't exist, or if its time stamp
+// is out of date.
+FileTask.prototype.isNeeded = function()
+{
+ return !FILE.exists(this.name()) || this.outOfDate(this.timestamp());
+}
+
+// Time stamp for file task.
+FileTask.prototype.timestamp = function()
+{
+ if (FILE.exists(this.name()))
+ return FILE.mtime(this.name());
+
+ return Jake.EARLY;
+}
+
+// Are there any prerequisites with a later time than the given time stamp?
+FileTask.prototype.outOfDate = function(aTimestamp)
+{
+ var application = this.application();
+
+ this._prerequisites.some(function(aTaskName)
+ {
+ return application.lookupTask(aTaskName).timestamp() > aTimestamp;
+ }, this);
+}
+
+// Apply the scope to the task name according to the rules for this kind
+// of task. File based tasks ignore the scope when creating the name.
+FileTask.scopeName = function(/*Array<String>*/ aScope, /*String*/ aTaskName)
+{
+ return aTaskName;
+}
+
+// EXPORTS
+exports.FileTask = FileTask;
366 lib/jake/task.js
@@ -0,0 +1,366 @@
+#!/usr/bin/env narwhal
+
+// Copyright 2009 280 North, Inc. (francisco@280north.com)
+// Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+var SYSTEM = require("system"),
+ jake = require("jake");
+
+var Task = function(/*String*/ aName, /*Application*/ anApplication)
+{
+ this._name = aName;
+ this._prerequisites = [];
+ this._actions = [];
+ this._alreadyInvoked = false;
+ //this._fullComment = null;
+ //this._comment = null;
+ //this.lock = Monitor.new
+ this._application = anApplication;
+ this._scope = anApplication.currentScope();
+ this._argumentNames = [];
+}
+
+Task.prototype.toString = function()
+{
+ return "Task (" + this.name() + ")";
+}
+
+Task.prototype.name = function()
+{
+ return this._name;
+}
+
+Task.prototype.prerequisites = function()
+{
+ return this._prerequisites;
+}
+
+Task.prototype.actions = function()
+{
+ return this._actions;
+}
+
+Task.prototype.application = function()
+{
+ return this._application;
+}
+
+Task.prototype.enhance = function(/*Array*/ prerequisites, /*Function*/ anAction)
+{
+ if (prerequisites)
+ this._prerequisites = this._prerequisites.concat(prerequisites);
+
+ if (anAction)
+ this._actions.push(anAction);
+}
+
+Task.prototype.reenable = function()
+{
+ this._alreadyInvoked = false;
+}
+
+Task.prototype.clear = function()
+{
+ this.clearPrerequisites();
+ this.clearActions();
+}
+
+Task.prototype.clearPrerequisites = function()
+{
+ this._prerequisites = [];
+}
+
+Task.prototype.clearActions = function()
+{
+ this._actions = [];
+}
+
+Task.prototype.invoke = function()
+{
+ var taskArguments = new TaskArguments(this._argumentNames, Array.prototype.slice.apply(arguments));
+
+ this.invokeWithCallChain(taskArguments, InvocationChain.EMPTY);
+}
+
+// Same as invoke, but explicitly pass a call chain to detect
+// circular dependencies.
+Task.prototype.invokeWithCallChain = function(taskArguments, anInvocationChain)
+{
+ var newChain = InvocationChain.append(this, anInvocationChain);
+// @lock.synchronize do
+// if application.options.trace
+// puts "** Invoke #{name} #{format_trace_flags}"
+// end
+
+ if (this._alreadyInvoked)
+ return;
+
+ this._alreadyInvoked = true;
+
+ this.invokePrerequisites(taskArguments, newChain);
+
+ if (this.isNeeded())
+ this.execute(taskArguments);
+}
+
+// Invoke all the prerequisites of a task.
+Task.prototype.invokePrerequisites = function(taskArguments, invocationChain)
+{
+ this._prerequisites.forEach(function(/*String*/ aPrerequisiteName)
+ {
+ var prerequisite = this._application.lookupTask(aPrerequisiteName, this._scope);
+
+ prerequisiteArguments = taskArguments.newScope(prerequisite.argumentNames());
+ prerequisite.invokeWithCallChain(prerequisiteArguments, invocationChain);
+ }, this);
+}
+
+Task.prototype.setArgumentNames = function(argumentNames)
+{
+ this._argumentNames = argumentNames;
+}
+
+Task.prototype.argumentNames = function()
+{
+ return this._argumentNames;
+}
+
+// Execute the actions associated with this task.
+Task.prototype.execute = function(taskArguments)
+{
+ taskArguments = taskArguments || EMPTY_TASK_ARGS;
+
+// if application.options.dryrun
+// puts "** Execute (dry run) #{name}"
+// return
+// end
+// if application.options.trace
+// puts "** Execute #{name}"
+// end
+
+// application.enhance_with_matching_rule(name) if @actions.empty?
+
+ this._actions.forEach(function(anAction)
+ {
+ anAction(this);
+ //anAction(This, args)
+ }, this);
+}
+
+Task.prototype.isNeeded = function()
+{
+ return true;
+}
+
+Task.prototype.timestamp = function()
+{
+ if (this._prerequisites.length <= 0)
+ return new Date().getTime();
+
+ return Math.max.apply(null, this._prerequisites.map(function(/*String*/ aPrerequisiteName)
+ {
+ return this._application.lookupTask(aPrerequisiteName).timestamp();
+ }, this));
+}
+
+Task.clear = function()
+{
+ Jake.application.clear();
+}
+
+Task.tasks = function()
+{
+ return Jake.application.tasks();
+}
+
+Task.taskNamed = function(/*String*/ aTaskName)
+{
+ return Jake.application.taskWithName(aTaskName);
+}
+
+Task.taskWithNameIsDefined = function(/*String*/ aTaskName)
+{
+ return !!Jake.application.lookupTaskWithName(aTaskName);
+}
+
+Task.defineTask = function()
+{
+ var args = [this];
+ application = jake.application();
+
+ // Can't simply use concat because we don't want to flatten inner arrays.
+ Array.prototype.forEach.call(arguments, function(object)
+ {
+ args.push(object);
+ });
+
+ return application.defineTask.apply(application, args);
+}
+
+Task.scopeName = function(/*Array<String>*/ aScope, /*String*/ aTaskName)
+{
+ return aScope.concat(aTaskName).join(':');
+}
+/*
+ # Track the last comment made in the Rakefile.
+ attr_accessor :last_description
+ alias :last_comment :last_description # Backwards compatibility
+*/
+
+var TaskArguments = function(/*Array<String>*/ names, /*Array<Object>*/ values, /*TaskArguments*/ aParent)
+{
+ this._names = names.slice();
+ this._parent = aParent;
+ this._hash = { };
+
+ this._names.forEach(function(/*String*/ aName, /*Number*/ anIndex)
+ {
+ if (values[anIndex] !== undefined)
+ this._hash[aName] = values[anIndex];
+ }, this);
+}
+
+TaskArguments.prototype.newScope = function(/*Array<String>*/ names)
+{
+ var values = names.map(function(/*String*/ aName)
+ {
+ return this.lookup(aName);
+ }, this);
+
+ return new TaskArguments(names, values, this);
+}
+
+TaskArguments.prototype.withDefaults = function(/*Object*/ defaults)
+{
+ var hash = this._hash;
+
+ for (key in defaults)
+ if (defaults.hasOwnProperty(key) && !hash.hasOwnProperty(key))
+ hash[key] = defaults[key];
+}
+
+TaskArguments.prototype.forEach = function(/*Function*/ aFunction, /*Object*/ thisObject)
+{
+ if (!aFunction)
+ return;
+
+ var hash = this._hash;
+
+ if (typeof thisObject === "undefined")
+ thisObject = aFunction;
+
+ for (key in hash)
+ aFunction.apply(thisObject, [key, hash[key]]);
+}
+
+TaskArguments.prototype.toHash = function()
+{
+ return this._hash;
+}
+
+TaskArguments.prototype.toString = function()
+{
+ return this._hash;
+}
+
+TaskArguments.prototype.lookup = function(/*String*/ aName)
+{
+ var hash = this._hash;
+
+ if (hash.hasOwnProperty(aName))
+ return hash[Name];
+
+ var env = SYSTEM.env;
+
+ if (env.hasOwnProperty(aName))
+ return env[aName];
+
+ var upperCaseName = aName.toUpperCase();
+
+ if (env.hasOwnProperty(upperCaseName))
+ return env[upperCaseName];
+
+ if (this._parent)
+ return this._parent.lookup(aName);
+
+ return null;
+}
+
+var EMPTY_TASK_ARGS = new TaskArguments([], []);
+
+var InvocationChain = function(aValue, /*InvocationChain*/ aTail)
+{
+ this._value = aValue;
+ this._tail = aTail;
+}
+
+InvocationChain.prototype.isMember = function(/*Object*/ anObject)
+{
+ return this._value == anObject || this._tail.isMember(anObject);
+}
+
+InvocationChain.prototype.append = function(/*Object*/ anObject)
+{
+ if (this.isMember(anObject))
+ throw "Circular dependency detected: " + this + " => " + this._value;
+
+ return new InvocationChain(this._value, this);
+}
+
+InvocationChain.prototype.toString = function()
+{
+ return this.prefix() + this._value;
+}
+
+InvocationChain.append = function(aValue, /*InvocationChain*/ aChain)
+{
+ return aChain.append(aValue);
+}
+
+InvocationChain.prototype.prefix = function()
+{
+ return this._tail + " => ";
+}
+
+var EmptyInvocationChain = function()
+{
+}
+
+EmptyInvocationChain.prototype.isMember = function(/*Object*/ anObject)
+{
+ return false;
+}
+
+EmptyInvocationChain.prototype.append = function(/*Object*/ aValue)
+{
+ return new InvocationChain(aValue, this);
+}
+
+EmptyInvocationChain.prototype.toString = function()
+{
+ return "TOP";
+}
+
+InvocationChain.EMPTY = new EmptyInvocationChain;
+
+// EXPORTS
+exports.Task = Task;
292 lib/jake/taskmanager.js
@@ -0,0 +1,292 @@
+#!/usr/bin/env narwhal
+
+// Copyright 2009 280 North, Inc. (francisco@280north.com)
+// Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Jim Weirich (jim.weirich@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+var FILE = require("file"),
+ FileTask = require("jake/filetask").FileTask;
+
+TaskManager = function()
+{
+ this._tasks = { };
+ this._rules = [];
+ this._scope = [];
+// @last_description = nil
+}
+/*
+ def create_rule(*args, &block)
+ pattern, arg_names, deps = resolve_args(args)
+ pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
+ @rules << [pattern, deps, block]
+ end
+*/
+
+TaskManager.prototype.defineTask = function(aTaskClass, aTaskName)
+{
+ if (arguments.length < 1)
+ throw "No class passed to Task.defineTask";
+
+ if (arguments.length < 2)
+ throw "No name passed to Task.defineTask";
+
+ aTaskName = aTaskClass.scopeName(this._scope, aTaskName);
+
+ var task = this.intern(aTaskClass, aTaskName),
+ result = this.resolveArguments(Array.prototype.slice.apply(arguments, [2]));
+
+ task.setArgumentNames(result[0]);
+//task.add_description(@last_description)
+//@last_description = nil
+ task.enhance(result[1], result[2]);
+
+ return task;
+}
+
+// Lookup a task. Return an existing task if found, otherwise
+// create a task of the current type.
+TaskManager.prototype.intern = function(/*Function*/ aTaskClass, /*String*/ aTaskName)
+{
+ var task = this._tasks[aTaskName];
+
+ if (!task)
+ {
+ task = new aTaskClass(aTaskName, this);
+ this._tasks[aTaskName] = task;
+ }
+
+ return task;
+}
+
+TaskManager.prototype.lookupTask = function(/*String*/ aTaskName, /*Array<String>*/ scopes)
+{
+ var task = this.lookup(aTaskName, scopes) || /* enhance_with_matching_rule(task_name) or ||*/ this.synthesizeFileTask(aTaskName);
+
+ if (!task)
+ throw "Don't know how to build task '" + aTaskName + "'";
+
+ return task;
+}
+
+TaskManager.prototype.synthesizeFileTask = function(/*String*/ aTaskName)
+{
+ if (!FILE.exists(aTaskName))
+ return null;
+
+ return this.defineTask(FileTask, aTaskName);
+}
+
+// Resolve the arguments for a task/rule. Returns a triplet of
+// [task_name, arg_name_list, prerequisites].
+//
+// The patterns recognized by this argument resolving function are:
+//
+// task(taskName, action)
+// task(taskName, [dependency])
+// task(taskName, [dependency], action)
+// task(taskName, [argumentName], [dependency], action)
+//
+TaskManager.prototype.resolveArguments = function(args)
+{
+ var action = null;
+
+ if (args.length && (typeof args[args.length - 1] === "function"))
+ action = args.pop();
+
+ var dependencies = [];
+
+ if (args.length)
+ dependencies = args.pop();
+
+ var argumentNames = [];
+
+ if (args.length)
+ argumentNames = args.pop();
+
+ return [argumentNames, dependencies, action];
+}
+
+TaskManager.prototype.tasks = function()
+{
+ var tasks = Object.keys(this._tasks);
+
+ tasks.sort(function(lhs, rhs)
+ {
+ if (lhs < rhs)
+ return -1;
+
+ else if (lhs > rhs)
+ return 1;
+
+ return 0;
+ } );
+
+ return tasks;
+}
+
+// List of all the tasks defined in the given scope (and its
+// sub-scopes).
+TaskManager.prototype.tasksInScope = function(/*Array<String>*/ aScope)
+{
+ var prefix = aScope.join(":"),
+ regexp = new Regexp("^" + prefix + ":");
+
+ return this._tasks.filter(function(/*Task*/ aTask)
+ {
+ return !!aTask.name().match(regexp);
+ });
+}
+
+// Clear all tasks in this application.
+TaskManager.prototype.clear = function()
+{
+ this._tasks = [];
+ this._rules = [];
+}
+
+// Lookup a task, using scope and the scope hints in the task name.
+// This method performs straight lookups without trying to
+// synthesize file tasks or rules. Special scope names (e.g. '^')
+// are recognized. If no scope argument is supplied, use the
+// current scope. Return nil if the task cannot be found.
+TaskManager.prototype.lookup = function(/*String*/ aTaskName, /*Array<String>*/ initialScope)
+{
+ if (!initialScope)
+ initialScope = this._scope;
+
+ var scopes = initialScope,
+ matches = null;
+
+ if (aTaskName.match(/^jake:/))
+ {
+ scopes = [];
+ aTaskName = aTaskName.replace(/^jake:/, "");
+ }
+ else if (matches = aTaskName.match(/^(\^+)/))
+ {
+ scopes = initialScope.slice(0, initialScope.length - matches[1].length);
+ aTaskName = aTaskName.replace(/^(\^+)/, "");
+ }
+
+ return this.lookupInScope(aTaskName, scopes);
+}
+
+// Lookup the task name
+TaskManager.prototype.lookupInScope = function(/*String*/ aTaskName, /*Array<String>*/ aScope)
+{
+ var count = aScope.length;
+
+ while (count >= 0)
+ {
+ var task = this._tasks[aScope.slice(0, count).concat([aTaskName]).join(':')];
+
+ if (task)
+ return task;
+
+ count--;
+ }
+
+ return null;
+}
+
+// Return the list of scope names currently active in the task
+// manager.
+TaskManager.prototype.currentScope = function()
+{
+ return this._scope.slice();
+}
+/*
+ # Evaluate the block in a nested namespace named +name+. Create
+ # an anonymous namespace if +name+ is nil.
+ def in_namespace(name)
+ name ||= generate_name
+ @scope.push(name)
+ ns = NameSpace.new(self, @scope)
+ yield(ns)
+ ns
+ ensure
+ @scope.pop
+ end
+
+ private
+
+ # Generate an anonymous namespace name.
+ def generate_name
+ @seed ||= 0
+ @seed += 1
+ "_anon_#{@seed}"
+ end
+
+ def trace_rule(level, message)
+ puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
+ end
+
+ # Attempt to create a rule given the list of prerequisites.
+ def attempt_rule(task_name, extensions, block, level)
+ sources = make_sources(task_name, extensions)
+ prereqs = sources.collect { |source|
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
+ if File.exist?(source) || Rake::Task.task_defined?(source)
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
+ source
+ elsif parent = enhance_with_matching_rule(source, level+1)
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
+ parent.name
+ else
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
+ return nil
+ end
+ }
+ task = FileTask.define_task({task_name => prereqs}, &block)
+ task.sources = prereqs
+ task
+ end
+
+ # Make a list of sources from the list of file name extensions /
+ # translation procs.
+ def make_sources(task_name, extensions)
+ extensions.collect { |ext|
+ case ext
+ when /%/
+ task_name.pathmap(ext)
+ when %r{/}
+ ext
+ when /^\./
+ task_name.ext(ext)
+ when String
+ ext
+ when Proc
+ if ext.arity == 1
+ ext.call(task_name)
+ else
+ ext.call
+ end
+ else
+ fail "Don't know how to handle rule dependent: #{ext.inspect}"
+ end
+ }.flatten
+ end
+
+ end # TaskManager
+*/
+
+// EXPORTS
+exports.TaskManager = TaskManager;
6 package.json
@@ -0,0 +1,6 @@
+{
+ "name": "jake",
+ "author": "Francisco Tolmasky (http://tolmasky.com/)",
+ "description": "A build system for CommonJS, lifted from Rake",
+ "keywords": ["build", "jake", "rake", "make"]
+}
Please sign in to comment.
Something went wrong with that request. Please try again.