From 2bfa96ec848f9c8e27006776be17e2f818e58761 Mon Sep 17 00:00:00 2001 From: zzak Date: Sun, 10 Feb 2013 05:26:34 +0000 Subject: * doc/rake/, lib/rake/doc/: Move Rake rdoc files to lib/rake git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@39185 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rake/doc/rakefile.rdoc | 557 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 557 insertions(+) create mode 100644 lib/rake/doc/rakefile.rdoc (limited to 'lib/rake/doc/rakefile.rdoc') 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] + +NOTE: 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 a.o and b.o. The tasks +for creating a.o and b.o 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 copy_xxx 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 :[] operator on the +Rake::Task. + +=== 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 + +NOTE: 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 + +NOTE: +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. + +NOTE: 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. -- cgit v1.2.3