== How to write an *Rantfile* As already mentioned, an Rantfile is a Ruby script. Additionally, Rant provides methods and classes which aid you in automating your work. Centric to the processing done by Rant is the *task*. The Rantfile defines tasks and their dependencies, Rant invokes the required tasks. Also important is the Rant application. When the rant command starts, it instantiates one Rant application which will read one or more Rantfiles. The Rantfile communicates with the following list of methods with the Rant application: +task+:: Defines a task with prerequisites and an action. +file+:: A special form of task which creates one file. +desc+:: Describes the next defined task. A task with description is considered a "public" task. +gen+:: Takes a _generator_ object as first argument which usually creates one or more tasks. An example would be the +RubyPackage+ generator which produces tasks for packaging. +import+:: Imports additional code which usually provides additional generators. Example: to use the +RubyPackage+ generator you have to import "rubypackage" first. +plugin+:: Instantiate a plugin. Example: plugin :Configure instantiates the Configure plugin. +sys+:: When given argument(s), runs an external program. Otherwise you can call methods to interact with the OS on it. +source+:: Takes a filename as argument and causes Rant to read it in as Rantfile. +subdirs+:: Takes a list of subdirectories in which Rant should look for Rantfiles. === Defining a task Just call the +task+ function and give it a task name as argument. The task name may be a string or symbol: task :taskname That's it, your first task. Not very usefull, because it doesn't do anything. To associate an action with the task, add a block: task :mytask do puts "Hello, mytask running." end Put these 3 lines of code into a file called +Rantfile+ and run rant: % rant mytask Hello, mytask running. Note: you could have ommited the mytask argument to rant because it is the only task in the Rantfile. You can add a block parameter which will be a reference to the created task: task :mytask do |t| puts t.name end Running rant now: % rant mytask Add prerequisites to create relations between tasks: task :first => [:t1, :t2] do puts t.name end task :t1 do |t| puts t.name end task :t2 do |t| puts t.name end In the definition of the "first" task we told Rant that it _depends_ on task "t1" and task "t2". "t1" and "t2" are called prerequisites for "first". Try it out: % rant first t1 t2 first % rant t1 t1 % rant t2 t2 === Defining a file task You will notice that rant will run the actions for a normal task always its name is given on the commandline or it is required by another task. Often you want to create a file, e.g. a program by invoking a compiler. In this case, the action needs only to be run if the source files (prerequisites) have changed. Use a file task instead of a normal task. In this example we use the sys.touch method to test our file task. (This method works the same as the Unix touch command: Update the modification time of a file or create an empty file): file "testfile" do |t| sys.touch t.name end Now run rant: % rant touch testfile This would have been the same with a normal task. But now run rant a second time: % rant This time rant doesn't run the action, because "testfile" is up to date. Of course you can add prerequisites the same way as for a normal task. Additionally you can add filenames as prerequisites. Assuming the files "a.o" and "b.o" are in the same directory as the Rantfile: file "myprog" => %w(a.o b.o) do |t| sys %w(cc -o), t.name, t.prerequisites end Running rant: % rant cc -o myprog a.o b.o Running a second time: % rant Does nothing, myprog is up to date. Don't be irritated by the %w() syntax. It creates a list of strings. The following expressions are equivalent: ["a", "b", "c"] %w(a b c) === Adding task descriptions The +desc+ function lets you describe your tasks. A small example Rantfile: # Generate C source file ls.c with the xgen command. file "ls.c" => %w(ls1.x ls2.x) do |t| sys %w(xgen -o), t.name, t.prerequisites end desc "Build ls program." file "ls" => "ls.c" do sys "cc -o ls ls.c" end desc "Remove autogenerated files." task :clean do sys.rm_f %w(ls.c ls) end (Note that xgen is a hypothetical command ;) The --tasks (or the short form, -T) option of rant shows this descriptions: % rant -T rant ls # Build ls program. rant clean # Remove autogenerated files. Only the tasks which have a description are listed. === The +sys+ function After using the +sys+ function quite often in the examples, I should explain it a little bit. The +sys+ function can be used in two ways: The first form is with no arguments. It returns an object on which you can invoke the methods of the +FileUtils+ module that comes with ruby. Examples are: sys.rm "file1", "file2", ... # remove files sys.cp "src", "dest" # copy from "src" do "dest" sys.mkdir "dir" # create directory "dir" For a list of all available methods invoke ri: % ri FileUtils which will also show documentation for them. Additionally you have the following methos which are not in the FileUtils module: sys.ruby "arg1", "arg2", ... # invoke the ruby interpreter sys.safe_ln "src", "dest" # create a hardlink or fall back to # copying If you're tired of typing sys. all the time, you can include the +Sys+ module in your Rantfile (though it is not recommended to do this): include Sys task :clean do rm_f "myprog" end Then you can invoke these methods without the sys function. Note:: You are highly encouraged to not include Sys into your Rantfile as this introduces many methods in the global namespace, which can cause name clashes with other libraries. Of course if you have just some tasks to remove backup files in your home directory and do not use other Ruby libraries in your Rantfile, it is no problem. === Generators The *gen* function takes a generator which usually creates one or more tasks for you. Currently are two generators immediately available: +Directory+:: Create directories. +Task+:: Define custom task. === The +Directory+ generator An example usage of the +Directory+ generator would be the backup example shown in the README file: file "misc/backup/data" => %w(misc/backup data) do |t| sys.cp "data", t.name end gen Directory, "misc/backup" Now rant will create the directories "misc" and "backup" on demand. Assuming "misc/backup" doesn't exist: % rant mkdir misc mkdir misc/backup cp data misc/backup/data === The +Task+ generator The +Task+ generator allows you to determine by hand when your task action needs to be run: desc "Install with setup.rb" gen Task, :install do |t| t.needed { !File.exist? "InstalledFiles" } t.act do sys.ruby "setup.rb" end end The +act+ block of the "install" task will only be run if "InstalledFiles" doesn't exist. Of course you can add prerequisites like with any other task. === Importing additional generators The +import+ function lets you import additional generators. Currently the following are coming with Rant: +RubyTest+:: Run Test::Unit tests for your Ruby code. +RubyDoc+:: Run RDoc. +RubyPackage+:: Generate tar, zip and gem packages of your Ruby application/library. As these are all Ruby specific, please read the Ruby project howto. === The +subdirs+ command The +subdirs+ command allows you to give Rant a list of subdirectories (relative to the Rantfile) where it should look for additional Rantfiles. The tasks defined in those subdirectories can be referenced from your main Rantfile and vice versa. A small example: We are assuming the following files: myprog/ Rantfile # the main Rantfile README src/ Rantfile main.c lib.c lib.h Then the main Rantfile could look like: desc "Build myprog." file "myprog" => "src/myprog" do sys.cp "src/myprog", "myprog" end desc "Remove compiler products." task :clean => "src/clean" do sys.rm_f "myprog" end # Tell Rant to look in src for an Rantfile, # we could list more directories here. subdirs "src" And src/Rantfile: file "lib.o" => %w(lib.c lib.h) do sys "cc -c -o lib.o lib.c" end file "main.o" => "main.c" do sys "cc -c -o main.o main.c" end file "myprog" => %w(lib.o main.o) do sys "cc -o myprog main.o lib.o" end task :clean do sys.rm_f Dir["*.o"] + %w(myprog) end Note that we refer to the task in subdirectory simply by prepending the directory name and a slash to the task name. file "myprog" => "src/myprog" do This tells Rant that the "myprog" task depends on the "myprog" task defined in the Rantfile in the "src" directory. The same goes for the "clean" task. Let's examine what Rant thinks about our build specification: myprog % rant -T rant myprog # Build myprog. rant clean # Remove compiler products. Since the "myprog" task is the first in our Rantfile, we don't need to specify a task to build myprog: myprog % rant cc -c -o lib.o lib.c cc -c -o main.o main.c cc -o myprog main.o lib.o cp src/myprog myprog And to save disk space, remove generated files: myprog % rant clean rm -f lib.o main.o myprog rm -f myprog The nice thing about our buildfiles is, that we can use the src/Rantfile independent from our main Rantfile. Consider you are working on lib.c and only want to check that it compiles correctly. You can specify the task explicitely: myprog % rant src/lib.o cc -c -o lib.o lib.c But if you are working a long time only in the src/ directory it is convinient to do: myprog % cd src myprog/src % rant lib.o cc -c -o lib.o lib.c To clean only the src directory: myprog/src % rant clean rm -f lib.o main.o myprog You can find this example project in the doc/examples/myprog directory of the Rant distribution. Of course you can also have a subdirectory in src/ containing an Rantfile. Simply put a +subdirs+ command into the src/Rantfile. Rant integrates well into your file system :) If you want to refer to a task in the main Rantfile from a subdir Rantfile, put a # in front of the task name: task "a" => "#b" But note that this Rantfile won't work as a standalone buildfile, because it refers to a main Rantfile. == See also Rant Overview:: README[link:files/README.html] Ruby project howto:: doc/rubyproject.rdoc[link:files/doc/rubyproject_rdoc.html]