diff options
Diffstat (limited to 'lib/rake')
-rw-r--r-- | lib/rake/doc/README | 196 | ||||
-rw-r--r-- | lib/rake/doc/command_line_usage.rdoc | 169 | ||||
-rw-r--r-- | lib/rake/doc/glossary.rdoc | 51 | ||||
-rw-r--r-- | lib/rake/doc/rakefile.rdoc | 557 | ||||
-rw-r--r-- | lib/rake/doc/rational.rdoc | 151 |
5 files changed, 1124 insertions, 0 deletions
diff --git a/lib/rake/doc/README b/lib/rake/doc/README new file mode 100644 index 0000000000..41668dd727 --- /dev/null +++ b/lib/rake/doc/README @@ -0,0 +1,196 @@ += RAKE -- Ruby Make + +Supporting Rake version: 0.8.6 + +This package contains Rake, a simple ruby build program with +capabilities similar to make. + +Rake has the following features: + +* Rakefiles (rake's version of Makefiles) are completely defined in + standard Ruby syntax. No XML files to edit. No quirky Makefile + syntax to worry about (is that a tab or a space?) + +* Users can specify tasks with prerequisites. + +* Rake supports rule patterns to synthesize implicit tasks. + +* Flexible FileLists that act like arrays but know about manipulating + file names and paths. + +* A library of prepackaged tasks to make building rakefiles easier. For example, + tasks for building tarballs, gems and RDoc output are provided. + +* Supports parallel execution of tasks. + + +== Installation + +=== Gem Installation + +Download and install rake with the following. + + gem install rake + +=== Normal Installation + +You can download the source tarball of the latest version of Rake from + +* http://rubyforge.org/project/showfiles.php?group_id=50 + +Extract the tarball and run + + % ruby install.rb + +from its distribution directory. + +== Usage + +=== Simple Example + +First, you must write a "Rakefile" file which contains the build rules. Here's +a simple example: + + task :default => [:test] + + task :test do + ruby "test/unittest.rb" + end + +This Rakefile has two tasks: + +* A task named "test", which - upon invocation - will run a unit test file in + Ruby. +* A task named "default". This task does nothing by itself, but it has exactly + one dependency, namely the "test" task. Invoking the "default" task will + cause Rake to invoke the "test" task as well. + +Running the "rake" command without any options will cause it to run the +"default" task in the Rakefile: + + % ls + Rakefile test/ + % rake + (in /home/some_user/Projects/rake) + ruby test/unittest.rb + ....unit test output here... + +Type "rake --help" for all available options. + + +=== More Information + +* For details on Rake's command-line invocation, read + doc/command_line_usage.rdoc[http://rake.rubyforge.org/files/doc/command_line_usage_rdoc.html] +* For details on writing Rakefiles, see + doc/rakefile.rdoc[http://rake.rubyforge.org/files/doc/rakefile_rdoc.html]. +* For the original announcement of Rake, see + doc/rational.rdoc[http://rake.rubyforge.org/files/doc/rational_rdoc.html]. +* For a glossary of terms, see + doc/glossary.rdoc[http://rake.rubyforge.org/files/doc/glossary_rdoc.html]. + + +== Development + +=== Source Repository + +Rake is currently hosted at github. The github web page is +http://github.com/jimweirich/rake. The public git clone URL is + +* git://github.com/jimweirich/rake.git + +=== Running the Rake Test Suite + +If you wish to run the unit and functional tests that come with Rake: + +* Install the 'session' gem in order to run the functional tests. +* CD into the top project directory of rake. +* Type one of the following: + + rake # If you have a version of rake installed + ruby -Ilib bin/rake # If you do not have a version of rake installed. + +=== Issues and Bug Reports + +Bugs, features requests and other issues can be logged at + +* http://onestepback.org/redmine/projects/show/rake + +You will need an account to before you can post issues. Register at +http://onestepback.org/redmine/account/register. Or you can send me +an email (at jim dot weirich at gmail dot com) + + +== Online Resources + +=== Rake References + +* Rake Documentation Home: http://docs.rubyrake.org +* Rake Project Page: http://rubyforge.org/projects/rake +* Rake API Documents: http://rake.rubyforge.org +* Rake Source Code Repo: http://github.com/jimweirich/rake +* Rake Git Repo Clone URL: git://github.com/jimweirich/rake.git + +=== Presentations and Articles about Rake + +* Jim Weirich's 2003 RubyConf presentation: http://onestepback.org/articles/buildingwithrake/ +* Martin Fowler's article on Rake: http://martinfowler.com/articles/rake.html + +== Other Make Reinvisionings ... + +Rake is a late entry in the make replacement field. Here are links to +other projects with similar (and not so similar) goals. + +* http://directory.fsf.org/bras.html -- Bras, one of earliest + implementations of "make in a scripting language". +* http://www.a-a-p.org -- Make in Python +* http://www.aromatic.com/tools/jam.txt -- JAM, Java Automated Make +* http://ant.apache.org -- The Ant project +* http://ppt.perl.org/commands/make/index.html -- Make from the Perl + Power Tools implementation. +* http://search.cpan.org/search?query=PerlBuildSystem -- The Perl Build System +* http://make.rubyforge.org -- Rant, another Ruby make tool. + +== Credits + +[<b>Ryan Dlugosz</b>] For the initial conversation that sparked Rake. + +[<b>nobu.nokada@softhome.net</b>] For the initial patch for rule support. + +[<b>Tilman Sauerbeck <tilman@code-monkey.de></b>] For the recursive rule patch. + +== License + +Rake is available under an MIT-style license. + +:include: MIT-LICENSE + +== Support + +The Rake homepage is http://rake.rubyforge.org. You can find the Rake +RubyForge page at http://rubyforge.org/projects/rake. + +Feel free to submit commits or feature requests. If you send a patch, +remember to update the corresponding unit tests. If fact, I prefer +new feature to be submitted in the form of new unit tests. + +For other information, feel free to ask on the ruby-talk mailing list +(which is mirrored to comp.lang.ruby) or contact +jim dot weirich at gmail.com. + +--- + += Other stuff + +Author:: Jim Weirich <jim.weirich@gmail.com> +Requires:: Ruby 1.8.0 or later +License:: Copyright 2003-2008 by Jim Weirich. + Released under an MIT-style license. See the LICENSE file + included in the distribution. + +== Warranty + +This software is provided "as is" and without any express or +implied warranties, including, without limitation, the implied +warranties of merchantibility and fitness for a particular +purpose. diff --git a/lib/rake/doc/command_line_usage.rdoc b/lib/rake/doc/command_line_usage.rdoc new file mode 100644 index 0000000000..688e55a9af --- /dev/null +++ b/lib/rake/doc/command_line_usage.rdoc @@ -0,0 +1,169 @@ += Rake Command Line Usage + +Rake is invoked from the command line using: + + % rake [options ...] [VAR=VALUE ...] [targets ...] + +Options are: + +[<tt><em>name</em>=<em>value</em></tt>] + Set the environment variable <em>name</em> to <em>value</em> + during the execution of the <b>rake</b> command. You can access + the value by using ENV['<em>name</em>']. + +[<tt>--all</tt> (-A)] + Used in combination with the -T and -D options, will force + those options to show all the tasks, even the ones without comments. + +[<tt>--backtrace</tt>{=_output_} (-n)] + Enable a full backtrace (i.e. like --trace, but without the task + tracing details). The _output_ parameter is optional, but if + specified it controls where the backtrace output is sent. If + _output_ is <tt>stdout</tt>, then backtrace output is directed to + stardard output. If _output_ is <tt>stderr</tt>, or if it is + missing, then the backtrace output is sent to standard error. + +[<tt>--classic-namespace</tt> (-n)] + Import the Task, FileTask, and FileCreateTask into the top-level + scope to be compatible with older versions of Rake. Alternatively + you can include the line <code>require + 'rake/classic_namespace'</code> in your Rakefile to get the + classic behavior. + +[<tt>--comments</tt>] + Used in combination with the -W options to force the output to + contain commented options only. This is the reverse of + <tt>--all</tt>. + +[<tt>--describe</tt> _pattern_ (-D)] + Describe the tasks (matching optional PATTERN), then exit. + +[<tt>--dry-run</tt> (-n)] + Do a dry run. Print the tasks invoked and executed, but do not + actually execute any of the actions. + +[<tt>--execute</tt> _code_ (-e)] + Execute some Ruby code and exit. + +[<tt>--execute-print</tt> _code_ (-p)] + Execute some Ruby code, print the result, and exit. + +[<tt>--execute-continue</tt> _code_ (-E)] + Execute some Ruby code, then continue with normal task processing. + +[<tt>--help</tt> (-H)] + Display some help text and exit. + +[<tt>--jobs</tt> _number_ (-j)] + Specifies the number of active concurrent tasks used. The + suggested value is equal to the number of CPUs. The concurrent + tasks are used to execute the <tt>multitask</tt> prerequisites. + Also see the <tt>-m</tt> option which turns all tasks into + multitasks. + + Sample values: + (no -j) : unlimited concurrent tasks (standard rake behavior) + -j : 2 concurrent tasks (exact number may change) + -j 16 : 16 concurrent tasks + +[<tt>--job-stats</tt> _level_] + + Display job statistics at the completion of the run. By default, + this will display the requested number of active tasks (from the + -j options) and the maximum number of tasks in play at any given + time. + + If the optional _level_ is <tt>history</tt>, then a complete trace + of task history will be displayed on standard output. + +[<tt>--libdir</tt> _directory_ (-I)] + Add _directory_ to the list of directories searched for require. + +[<tt>--multitask</tt> (-m)] + Treat all tasks as multitasks. ('make/drake' semantics) + +[<tt>--nosearch</tt> (-N)] + Do not search for a Rakefile in parent directories. + +[<tt>--prereqs</tt> (-P)] + Display a list of all tasks and their immediate prerequisites. + +[<tt>--quiet</tt> (-q)] + Do not echo commands from FileUtils. + +[<tt>--rakefile</tt> _filename_ (-f)] + Use _filename_ as the name of the rakefile. The default rakefile + names are +rakefile+ and +Rakefile+ (with +rakefile+ taking + precedence). If the rakefile is not found in the current + directory, +rake+ will search parent directories for a match. The + directory where the Rakefile is found will become the current + directory for the actions executed in the Rakefile. + +[<tt>--rakelibdir</tt> _rakelibdir_ (-R)] + Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib') + +[<tt>--reduce-compat</tt>] + + Remove the DSL commands from the Object inheritance hierarchy and + do not define top level constants. This reduces the backwards + compatibility of Rake, but allows rake to be used with software + that would otherwise have conflicting definitions. + + *NOTE:* The next major version of Rake will only be able to be run + in "reduce-compat" mode. + +[<tt>--require</tt> _name_ (-r)] + Require _name_ before executing the Rakefile. + +[<tt>--rules</tt>] + Trace the rules resolution. + +[<tt>--silent (-s)</tt>] + Like --quiet, but also suppresses the 'in directory' announcement. + +[<tt>--suppress-backtrace _pattern_ </tt>] + Line matching the regular expression _pattern_ will be removed + from the backtrace output. Note that the --backtrace option is the + full backtrace without these lines suppressed. + +[<tt>--system</tt> (-g)] + Use the system wide (global) rakefiles. The project Rakefile is + ignored. By default, the system wide rakefiles are used only if no + project Rakefile is found. On Unix-like system, the system wide + rake files are located in $HOME/.rake. On a windows system they + are stored in $APPDATA/Rake. + +[<tt>--no-system</tt> (-G)] + Use the project level Rakefile, ignoring the system-wide (global) + rakefiles. + +[<tt>--tasks</tt> <em>pattern</em> (-T)] + Display a list of the major tasks and their comments. Comments + are defined using the "desc" command. If a pattern is given, then + only tasks matching the pattern are displayed. + +[<tt>--trace</tt>{=_output_} (-t)] + Turn on invoke/execute tracing. Also enable full backtrace on + errors. The _output_ parameter is optional, but if specified it + controls where the trace output is sent. If _output_ is + <tt>stdout</tt>, then trace output is directed to stardard output. + If _output_ is <tt>stderr</tt>, or if it is missing, then trace + output is sent to standard error. + +[<tt>--verbose</tt> (-v)] + Echo the Sys commands to standard output. + +[<tt>--version</tt> (-V)] + Display the program version and exit. + +[<tt>--where</tt> <em>pattern</em> (-W)] + Display tasks that match <em>pattern</em> and the file and line + number where the task is defined. By default this option will + display all tasks, not just the tasks that have descriptions. + +[<tt>--no-deprecation-warnings</tt> (-W)] + Do not display the deprecation warnings. + +In addition, any command line option of the form +<em>VAR</em>=<em>VALUE</em> will be added to the environment hash +<tt>ENV</tt> and may be tested in the Rakefile. diff --git a/lib/rake/doc/glossary.rdoc b/lib/rake/doc/glossary.rdoc new file mode 100644 index 0000000000..a811764091 --- /dev/null +++ b/lib/rake/doc/glossary.rdoc @@ -0,0 +1,51 @@ += Glossary + +[<b>action</b>] + Code to be executed in order to perform a task. Actions in a + rakefile are specified in a code block (usually delimited by + +do+/+end+ pairs. + +[<b>execute</b>] + When a task is executed, all of its actions are performed, in + the order they were defined. Note that unlike + <tt>invoke</tt>, <tt>execute</tt> always executes the actions + (without invoking or executing the prerequisites). + +[<b>file task</b> (FileTask)] + A file task is a task whose purpose is to create a file + (which has the same name as the task). When invoked, a file + task will only execute if one or more of the following + conditions are true. + + 1. The associated file does not exist. + 2. A prerequisite has a later time stamp than the existing file. + + Because normal Tasks always have the current time as + timestamp, a FileTask that has a normal Task prerequisite + will always execute. + +[<b>invoke</b>] + When a task is invoked, first we check to see if it has been + invoked before. if it has been, then nothing else is done. + If this is the first time its been invoked, then we invoke + each of its prerequisites. Finally, we check to see if we + need to execute the actions of this task by calling + <tt>needed?</tt>. Finally, if the task is needed, we execute + its actions. + + NOTE: Currently prerequisites are invoked even if the task is + not needed. This may change in the future. + +[<b>prerequisites</b>] + Every task has a set (possibly empty) of prerequisites. A + prerequisite P to Task T is itself a task that must be invoked + before Task T. + +[<b>rule</b>] + A rule is a recipe for synthesizing a task when no task is + explicitly defined. Rules generally synthesize file tasks. + +[<b>task</b> (Task)] + Basic unit of work in a rakefile. A task has a name, a set of + prerequisites and a list of actions to be performed. + diff --git a/lib/rake/doc/rakefile.rdoc b/lib/rake/doc/rakefile.rdoc new file mode 100644 index 0000000000..a00c9fd21e --- /dev/null +++ b/lib/rake/doc/rakefile.rdoc @@ -0,0 +1,557 @@ += Rakefile Format (as of version 0.8.7) + +First of all, there is no special format for a Rakefile. A Rakefile +contains executable Ruby code. Anything legal in a ruby script is +allowed in a Rakefile. + +Now that we understand there is no special syntax in a Rakefile, there +are some conventions that are used in a Rakefile that are a little +unusual in a typical Ruby program. Since a Rakefile is tailored to +specifying tasks and actions, the idioms used in a Rakefile are +designed to support that. + +So, what goes into a Rakefile? + +== Tasks + +Tasks are the main unit of work in a Rakefile. Tasks have a name +(usually given as a symbol or a string), a list of prerequisites (more +symbols or strings) and a list of actions (given as a block). + +=== Simple Tasks + +A task is declared by using the +task+ method. +task+ takes a single +parameter that is the name of the task. + + task :name + +=== Tasks with Prerequisites + +Any prerequisites are given as a list (enclosed in square brackets) +following the name and an arrow (=>). + + task :name => [:prereq1, :prereq2] + +<b>NOTE:</b> Although this syntax looks a little funky, it is legal +Ruby. We are constructing a hash where the key is :name and the value +for that key is the list of prerequisites. It is equivalent to the +following ... + + hash = Hash.new + hash[:name] = [:prereq1, :prereq2] + task(hash) + +=== Tasks with Actions + +Actions are defined by passing a block to the +task+ method. Any Ruby +code can be placed in the block. The block may reference the task +object via the block parameter. + + task :name => [:prereq1, :prereq2] do |t| + # actions (may reference t) + end + +=== Multiple Definitions + +A task may be specified more than once. Each specification adds its +prerequisites and actions to the existing definition. This allows one +part of a rakefile to specify the actions and a different rakefile +(perhaps separately generated) to specify the dependencies. + +For example, the following is equivalent to the single task +specification given above. + + task :name + task :name => [:prereq1] + task :name => [:prereq2] + task :name do |t| + # actions + end + +== File Tasks + +Some tasks are designed to create a file from one or more other files. +Tasks that generate these files may be skipped if the file already +exists. File tasks are used to specify file creation tasks. + +File tasks are declared using the +file+ method (instead of the +task+ +method). In addition, file tasks are usually named with a string +rather than a symbol. + +The following file task creates a executable program (named +prog+) +given two object files name <tt>a.o</tt> and <tt>b.o</tt>. The tasks +for creating <tt>a.o</tt> and <tt>b.o</tt> are not shown. + + file "prog" => ["a.o", "b.o"] do |t| + sh "cc -o #{t.name} #{t.prerequisites.join(' ')}" + end + +== Directory Tasks + +It is common to need to create directories upon demand. The ++directory+ convenience method is a short-hand for creating a FileTask +that creates the directory. For example, the following declaration +... + + directory "testdata/examples/doc" + +is equivalent to ... + + file "testdata" do |t| mkdir t.name end + file "testdata/examples" do |t| mkdir t.name end + file "testdata/examples/doc" do |t| mkdir t.name end + +The +directory+ method does not accept prerequisites or actions, but +both prerequisites and actions can be added later. For example ... + + directory "testdata" + file "testdata" => ["otherdata"] + file "testdata" do + cp Dir["standard_data/*.data"], "testdata" + end + +== Tasks with Parallel Prerequisites + +Rake allows parallel execution of prerequisites using the following syntax: + + multitask :copy_files => [:copy_src, :copy_doc, :copy_bin] do + puts "All Copies Complete" + end + +In this example, +copy_files+ is a normal rake task. Its actions are +executed whenever all of its prerequisites are done. The big +difference is that the prerequisites (+copy_src+, +copy_bin+ and ++copy_doc+) are executed in parallel. Each of the prerequisites are +run in their own Ruby thread, possibly allowing faster overall runtime. + +=== Secondary Prerequisites + +If any of the primary prerequisites of a multitask have common secondary +prerequisites, all of the primary/parallel prerequisites will wait +until the common prerequisites have been run. + +For example, if the <tt>copy_<em>xxx</em></tt> tasks have the +following prerequisites: + + task :copy_src => [:prep_for_copy] + task :copy_bin => [:prep_for_copy] + task :copy_doc => [:prep_for_copy] + +Then the +prep_for_copy+ task is run before starting all the copies in +parallel. Once +prep_for_copy+ is complete, +copy_src+, +copy_bin+, +and +copy_doc+ are all run in parallel. Note that +prep_for_copy+ is +run only once, even though it is referenced in multiple threads. + +=== Thread Safety + +The Rake internal data structures are thread-safe with respect +to the multitask parallel execution, so there is no need for the user +to do extra synchronization for Rake's benefit. However, if there are +user data structures shared between the parallel prerequisites, the +user must do whatever is necessary to prevent race conditions. + +== Tasks with Arguments + +Prior to version 0.8.0, rake was only able to handle command line +arguments of the form NAME=VALUE that were passed into Rake via the +ENV hash. Many folks had asked for some kind of simple command line +arguments, perhaps using "--" to separate regular task names from +argument values on the command line. The problem is that there was no +easy way to associate positional arguments on the command line with +different tasks. Suppose both tasks :a and :b expect a command line +argument: does the first value go with :a? What if :b is run first? +Should it then get the first command line argument. + +Rake 0.8.0 solves this problem by explicitly passing values directly +to the tasks that need them. For example, if I had a release task +that required a version number, I could say: + + rake release[0.8.2] + +And the string "0.8.2" will be passed to the :release task. Multiple +arguments can be passed by separating them with a comma, for example: + + rake name[john,doe] + +Just a few words of caution. The rake task name and its arguments +need to be a single command line argument to rake. This generally +means no spaces. If spaces are needed, then the entire rake + +argument string should be quoted. Something like this: + + rake "name[billy bob, smith]" + +(Quoting rules vary between operating systems and shells, so make sure +you consult the proper docs for your OS/shell). + +=== Tasks Arguments and the Environment + +Task argument values can also be picked up from the environment. For +example, if the "release" task expected a parameter named +"release_version", then either + + rake release[0.8.2] + +or + + RELEASE_VERSION rake release + +will work. Environment variable names must either match the task +parameter exactly, or match an all-uppercase version of the task +parameter. + +=== Tasks that Expect Parameters + +Parameters are only given to tasks that are setup to expect them. In +order to handle named parameters, the task declaration syntax for +tasks has been extended slightly. + +For example, a task that needs a first name and last name might be +declared as: + + task :name, [:first_name, :last_name] + +The first argument is still the name of the task (:name in this case). +The next two arguments are the names of the parameters expected by +:name in an array (:first_name and :last_name in the example). + +To access the values of the parameters, the block defining the task +behaviour can now accept a second parameter: + + task :name, [:first_name, :last_name] do |t, args| + puts "First name is #{args.first_name}" + puts "Last name is #{args.last_name}" + end + +The first argument of the block "t" is always bound to the current +task object. The second argument "args" is an open-struct like object +that allows access to the task arguments. Extra command line +arguments to a task are ignored. Missing command line arguments are +picked up from matching environment variables. If there are no +matching environment variables, they are given the nil value. + +If you wish to specify default values for the arguments, you can use +the with_defaults method in the task body. Here is the above example +where we specify default values for the first and last names: + + task :name, [:first_name, :last_name] do |t, args| + args.with_defaults(:first_name => "John", :last_name => "Dough") + puts "First name is #{args.first_name}" + puts "Last name is #{args.last_name}" + end + +=== Tasks that Expect Parameters and Have Prerequisites + +Tasks that use parameters have a slightly different format for +prerequisites. Use the arrow notation to indicate the prerequisites +for tasks with arguments. For example: + + task :name, [:first_name, :last_name] => [:pre_name] do |t, args| + args.with_defaults(:first_name => "John", :last_name => "Dough") + puts "First name is #{args.first_name}" + puts "Last name is #{args.last_name}" + end + +=== Deprecated Task Parameters Format + +There is an older format for declaring task parameters that omitted +the task argument array and used the :needs keyword to introduce the +dependencies. That format is still supported for compatibility, but +is not recommended for use. The older format may be dropped in future +versions of rake. + +== Accessing Task Programmatically + +Sometimes it is useful to manipulate tasks programmatically in a +Rakefile. To find a task object, use the <tt>:[]</tt> operator on the +<tt>Rake::Task</tt>. + +=== Programmatic Task Example + +For example, the following Rakefile defines two tasks. The :doit task +simply prints a simple "DONE" message. The :dont class will lookup +the doit class and remove (clear) all of its prerequisites and +actions. + + task :doit do + puts "DONE" + end + + task :dont do + Rake::Task[:doit].clear + end + +Running this example: + + $ rake doit + (in /Users/jim/working/git/rake/x) + DONE + $ rake dont doit + (in /Users/jim/working/git/rake/x) + $ + +The ability to programmatically manipulate tasks gives rake very +powerful meta-programming capabilities w.r.t. task execution, but +should be used with cation. + +== Rules + +When a file is named as a prerequisite, but does not have a file task +defined for it, Rake will attempt to synthesize a task by looking at a +list of rules supplied in the Rakefile. + +Suppose we were trying to invoke task "mycode.o", but no task is +defined for it. But the rakefile has a rule that look like this ... + + rule '.o' => ['.c'] do |t| + sh "cc #{t.source} -c -o #{t.name}" + end + +This rule will synthesize any task that ends in ".o". It has a +prerequisite a source file with an extension of ".c" must exist. If +Rake is able to find a file named "mycode.c", it will automatically +create a task that builds "mycode.o" from "mycode.c". + +If the file "mycode.c" does not exist, rake will attempt +to recursively synthesize a rule for it. + +When a task is synthesized from a rule, the +source+ attribute of the +task is set to the matching source file. This allows us to write +rules with actions that reference the source file. + +=== Advanced Rules + +Any regular expression may be used as the rule pattern. Additionally, +a proc may be used to calculate the name of the source file. This +allows for complex patterns and sources. + +The following rule is equivalent to the example above. + + rule( /\.o$/ => [ + proc {|task_name| task_name.sub(/\.[^.]+$/, '.c') } + ]) do |t| + sh "cc #{t.source} -c -o #{t.name}" + end + +<b>NOTE:</b> Because of a _quirk_ in Ruby syntax, parenthesis are +required on *rule* when the first argument is a regular expression. + +The following rule might be used for Java files ... + + rule '.java' => [ + proc { |tn| tn.sub(/\.class$/, '.java').sub(/^classes\//, 'src/') } + ] do |t| + java_compile(t.source, t.name) + end + +<b>NOTE:</b> +java_compile+ is a hypothetical method that invokes the +java compiler. + +== Importing Dependencies + +Any ruby file (including other rakefiles) can be included with a +standard Ruby +require+ command. The rules and declarations in the +required file are just added to the definitions already accumulated. + +Because the files are loaded _before_ the rake targets are evaluated, +the loaded files must be "ready to go" when the rake command is +invoked. This make generated dependency files difficult to use. By +the time rake gets around to updating the dependencies file, it is too +late to load it. + +The +Rake.import+ command addresses this by specifying a file to be +loaded _after_ the main rakefile is loaded, but _before_ any targets +on the command line are invoked. In addition, if the file name +matches an explicit task, that task is invoked before loading the +file. This allows dependency files to be generated and used in a +single rake command invocation. + +<b>NOTE:</b> Starting in Rake version 0.9.0, the top level +import+ +command is deprecated and we recommend using the scoped +"+Rake.import+" command mentioned above. Future versions of Rake will +drop support for the top level +import+ command. + +=== Example: + + require 'rake/loaders/makefile' + + file ".depends.mf" => [SRC_LIST] do |t| + sh "makedepend -f- -- #{CFLAGS} -- #{t.prerequisites} > #{t.name}" + end + + Rake.import ".depends.mf" + +If ".depends" does not exist, or is out of date w.r.t. the source +files, a new ".depends" file is generated using +makedepend+ before +loading. + +== Comments + +Standard Ruby comments (beginning with "#") can be used anywhere it is +legal in Ruby source code, including comments for tasks and rules. +However, if you wish a task to be described using the "-T" switch, +then you need to use the +desc+ command to describe the task. + +=== Example: + + desc "Create a distribution package" + task :package => [ ... ] do ... end + +The "-T" switch (or "--tasks" if you like to spell things out) will +display a list of tasks that have a description. If you use +desc+ to +describe your major tasks, you have a semi-automatic way of generating +a summary of your Rake file. + + traken$ rake -T + (in /home/.../rake) + rake clean # Remove any temporary products. + rake clobber # Remove any generated file. + rake clobber_rdoc # Remove rdoc products + rake contrib_test # Run tests for contrib_test + rake default # Default Task + rake install # Install the application + rake lines # Count lines in the main rake file + rake rdoc # Build the rdoc HTML Files + rake rerdoc # Force a rebuild of the RDOC files + rake test # Run tests + rake testall # Run all test targets + +Only tasks with descriptions will be displayed with the "-T" switch. +Use "-P" (or "--prereqs") to get a list of all tasks and their +prerequisites. + +== Namespaces + +As projects grow (and along with it, the number of tasks), it is +common for task names to begin to clash. For example, if you might +have a main program and a set of sample programs built by a single +Rakefile. By placing the tasks related to the main program in one +namespace, and the tasks for building the sample programs in a +different namespace, the task names will not will not interfere with +each other. + +For example: + + namespace "main" do + task :build do + # Build the main program + end + end + + namespace "samples" do + task :build do + # Build the sample programs + end + end + + task :build => ["main:build", "samples:build"] + +Referencing a task in a separate namespace can be achieved by +prefixing the task name with the namespace and a colon +(e.g. "main:build" refers to the :build task in the +main+ namespace). +Nested namespaces are supported, so + +Note that the name given in the +task+ command is always the unadorned +task name without any namespace prefixes. The +task+ command always +defines a task in the current namespace. + +=== FileTasks + +File task names are not scoped by the namespace command. Since the +name of a file task is the name of an actual file in the file system, +it makes little sense to include file task names in name space. +Directory tasks (created by the +directory+ command) are a type of +file task and are also not affected by namespaces. + +=== Name Resolution + +When looking up a task name, rake will start with the current +namespace and attempt to find the name there. If it fails to find a +name in the current namespace, it will search the parent namespaces +until a match is found (or an error occurs if there is no match). + +The "rake" namespace is a special implicit namespace that refers to +the toplevel names. + +If a task name begins with a "^" character, the name resolution will +start in the parent namespace. Multiple "^" characters are allowed. + +Here is an example file with multiple :run tasks and how various names +resolve in different locations. + + task :run + + namespace "one" do + task :run + + namespace "two" do + task :run + + # :run => "one:two:run" + # "two:run" => "one:two:run" + # "one:two:run" => "one:two:run" + # "one:run" => "one:run" + # "^run" => "one:run" + # "^^run" => "rake:run" (the top level task) + # "rake:run" => "rake:run" (the top level task) + end + + # :run => "one:run" + # "two:run" => "one:two:run" + # "^run" => "rake:run" + end + + # :run => "rake:run" + # "one:run" => "one:run" + # "one:two:run" => "one:two:run" + +== FileLists + +FileLists are the way Rake manages lists of files. You can treat a +FileList as an array of strings for the most part, but FileLists +support some additional operations. + +=== Creating a FileList + +Creating a file list is easy. Just give it the list of file names: + + fl = FileList['file1.rb', file2.rb'] + +Or give it a glob pattern: + + fl = FileList['*.rb'] + +== Odds and Ends + +=== do/end versus { } + +Blocks may be specified with either a +do+/+end+ pair, or with curly +braces in Ruby. We _strongly_ recommend using +do+/+end+ to specify the +actions for tasks and rules. Because the rakefile idiom tends to +leave off parentheses on the task/file/rule methods, unusual +ambiguities can arise when using curly braces. + +For example, suppose that the method +object_files+ returns a list of +object files in a project. Now we use +object_files+ as the +prerequisites in a rule specified with actions in curly braces. + + # DON'T DO THIS! + file "prog" => object_files { + # Actions are expected here (but it doesn't work)! + } + +Because curly braces have a higher precedence than +do+/+end+, the +block is associated with the +object_files+ method rather than the ++file+ method. + +This is the proper way to specify the task ... + + # THIS IS FINE + file "prog" => object_files do + # Actions go here + end + +---- + +== See + +* README.rdoc -- Main documentation for Rake. diff --git a/lib/rake/doc/rational.rdoc b/lib/rake/doc/rational.rdoc new file mode 100644 index 0000000000..0e1c33873d --- /dev/null +++ b/lib/rake/doc/rational.rdoc @@ -0,0 +1,151 @@ += Why rake? + +Ok, let me state from the beginning that I never intended to write this +code. I'm not convinced it is useful, and I'm not convinced anyone +would even be interested in it. All I can say is that Why's onion truck +must by been passing through the Ohio valley. + +What am I talking about? ... A Ruby version of Make. + +See, I can sense you cringing already, and I agree. The world certainly +doesn't need yet another reworking of the "make" program. I mean, we +already have "ant". Isn't that enough? + +It started yesterday. I was helping a coworker fix a problem in one of +the Makefiles we use in our project. Not a particularly tough problem, +but during the course of the conversation I began lamenting some of the +shortcomings of make. In particular, in one of my makefiles I wanted to +determine the name of a file dynamically and had to resort to some +simple scripting (in Ruby) to make it work. "Wouldn't it be nice if you +could just use Ruby inside a Makefile" I said. + +My coworker (a recent convert to Ruby) agreed, but wondered what it +would look like. So I sketched the following on the whiteboard... + + "What if you could specify the make tasks in Ruby, like this ..." + + task "build" do + java_compile(...args, etc ...) + end + + "The task function would register "build" as a target to be made, + and the block would be the action executed whenever the build + system determined that it was time to do the build target." + +We agreed that would be cool, but writing make from scratch would be WAY +too much work. And that was the end of that! + +... Except I couldn't get the thought out of my head. What exactly +would be needed to make the about syntax work as a make file? Hmmm, you +would need to register the tasks, you need some way of specifying +dependencies between tasks, and some way of kicking off the process. +Hey! What if we did ... and fifteen minutes later I had a working +prototype of Ruby make, complete with dependencies and actions. + +I showed the code to my coworker and we had a good laugh. It was just +about a page worth of code that reproduced an amazing amount of the +functionality of make. We were both truly stunned with the power of +Ruby. + +But it didn't do everything make did. In particular, it didn't have +timestamp based file dependencies (where a file is rebuilt if any of its +prerequisite files have a later timestamp). Obviously THAT would be a +pain to add and so Ruby Make would remain an interesting experiment. + +... Except as I walked back to my desk, I started thinking about what +file based dependencies would really need. Rats! I was hooked again, +and by adding a new class and two new methods, file/timestamp +dependencies were implemented. + +Ok, now I was really hooked. Last night (during CSI!) I massaged the +code and cleaned it up a bit. The result is a bare-bones replacement +for make in exactly 100 lines of code. + +For the curious, you can see it at ... +* doc/proto_rake.rdoc + +Oh, about the name. When I wrote the example Ruby Make task on my +whiteboard, my coworker exclaimed "Oh! I have the perfect name: Rake ... +Get it? Ruby-Make. Rake!" He said he envisioned the tasks as leaves +and Rake would clean them up ... or something like that. Anyways, the +name stuck. + +Some quick examples ... + +A simple task to delete backup files ... + + task :clean do + Dir['*~'].each {|fn| rm fn rescue nil} + end + +Note that task names are symbols (they are slightly easier to type +than quoted strings ... but you may use quoted string if you would +rather). Rake makes the methods of the FileUtils module directly +available, so we take advantage of the <tt>rm</tt> command. Also note +the use of "rescue nil" to trap and ignore errors in the <tt>rm</tt> +command. + +To run it, just type "rake clean". Rake will automatically find a +Rakefile in the current directory (or above!) and will invoke the +targets named on the command line. If there are no targets explicitly +named, rake will invoke the task "default". + +Here's another task with dependencies ... + + task :clobber => [:clean] do + rm_r "tempdir" + end + +Task :clobber depends upon task :clean, so :clean will be run before +:clobber is executed. + +Files are specified by using the "file" command. It is similar to the +task command, except that the task name represents a file, and the task +will be run only if the file doesn't exist, or if its modification time +is earlier than any of its prerequisites. + +Here is a file based dependency that will compile "hello.cc" to +"hello.o". + + file "hello.cc" + file "hello.o" => ["hello.cc"] do |t| + srcfile = t.name.sub(/\.o$/, ".cc") + sh %{g++ #{srcfile} -c -o #{t.name}} + end + +I normally specify file tasks with string (rather than symbols). Some +file names can't be represented by symbols. Plus it makes the +distinction between them more clear to the casual reader. + +Currently writing a task for each and every file in the project would be +tedious at best. I envision a set of libraries to make this job +easier. For instance, perhaps something like this ... + + require 'rake/ctools' + Dir['*.c'].each do |fn| + c_source_file(fn) + end + +where "c_source_file" will create all the tasks need to compile all the +C source files in a directory. Any number of useful libraries could be +created for rake. + +That's it. There's no documentation (other than whats in this +message). Does this sound interesting to anyone? If so, I'll continue +to clean it up and write it up and publish it on RAA. Otherwise, I'll +leave it as an interesting exercise and a tribute to the power of Ruby. + +Why /might/ rake be interesting to Ruby programmers. I don't know, +perhaps ... + +* No weird make syntax (only weird Ruby syntax :-) +* No need to edit or read XML (a la ant) +* Platform independent build scripts. +* Will run anywhere Ruby exists, so no need to have "make" installed. + If you stay away from the "sys" command and use things like + 'ftools', you can have a perfectly platform independent + build script. Also rake is only 100 lines of code, so it can + easily be packaged along with the rest of your code. + +So ... Sorry for the long rambling message. Like I said, I never +intended to write this code at all. |