Rook Users' Guide
Preface
Rook is a software build tool such as Make, Ant, SCons or Cook. It is implemented in Ruby and runs any platform Ruby support. Basic command (copy, move, rename, mkdir, ...) is also implemented in Ruby and allows you to execute platform-depended command.
Rook liken build process to cooking. Input file is called 'ingredient', output is 'product', task is 'recipe', build file is 'cookbook'. Rook generates products from ingredients according to recipes. You describe products, ingredients, and recipes in cookbook.
Features:
- Impremented in pure Ruby and runs any platform which Ruby supports.
- Input file (called 'cookbook') is named 'Rookbook.yaml' or 'Rookbook.rb', which is equivarent to 'Makefile' of Make or 'build.xml' of Ant.
- Cookbook's format is YAML or Ruby script. Also you can embed Ruby script in YAML format.
Caution! Rook is currently under experimental. It means that the design and specification of Rook may change frequently.
Table of Contents
Quick guide to YAML
YAML is a text-base format to describe stuctured data. The purpose of YAML is similar to XML but YAML is more friendly for human to read and write than XML.
You should learn about YAML because cookbook ('Rookbook.yaml') is described in YAML format. This chapter will help you to learn YAML in quickly. See the following page for details.
Sequence
Sequence is a list of element.
- aaa - bbb - ccc
[ aaa, bbb, ccc ]
These are equivarent to the following Ruby code.
['aaa', 'bbb', 'ccc']
Mapping
Mapping is a map or hash of pairs of key and value.
one: 1 two: 2 three: 3
{ one: 1, two: 2, three: 3 }
These are equivarent to the following Ruby code.
{'one'=>1, 'two'=>2, 'three'=>3}
Combination of Sequence and Mapping
Sequence and mapping are nestable in each other.
The following is an example of a sequence of mappings.
- name: Foo
email: foo@mail.com
age: 30
married: yes
- name: Bar
email: bar@mail.org
gender: F
- { name: Baz, age: 19, married: no }
The above is equivarent to the following Ruby code.
[
{'name'=>'Foo', 'email'=>'foo@mail.com', 'age'=>30},
{'name'=>'Bar', 'email'=>'bar@mail.org', 'gender'=>'F'},
{'name'=>'Baz', 'age'=>19, 'married'=>false},
]
The following is an example of a mapping of sequences.
members: - Foo - Bar - Baz tasks: - write code - test code - write documents sub-projects: [ sub1, sub2 ]
The above is equivarent to the following Ruby code.
{
'members'=>['Foo', 'Bar', 'Baz'],
'tasks'=>['write code', 'test code', 'write documents'],
'sub-projects'=>['sub1, 'sub2'],
}
Scalar and Data Type
YAML determines data type of elemet automatically.
- foobar # string - 123 # integer - 3.14159 # float - true # boolean (true) - yes # boolean (true) - on # boolean (true) - false # boolean (false) - no # boolean (false) - off # boolean (false) - 2006-01-01 # date - 2006-01-01 00:00:00 # timestamp - 'true' # string - "false" # string
YAML data is represented by the combination of the followings.
- Collection (Sequecne and Mapping)
- Scalar (string, integer, float, boolean, ...)
This feature is similar to JSON. In fact, you can regard YAML's flow-style as JSON.
Block text
'|' means start of block-text. Block-text is convenient if string has several lines.
text1: |
foo
bar
baz
text2: |
foo
bar
baz
This is equivarent to the following Ruby code.
[
'text1'=> <<END,
foo
bar
baz
END
'text2'=> <<END,
foo
bar
baz
END
]
Anchor and Alias
Anchor is feature to mark data. Alias is feature to refer anchored data.
- name: foo
desc: &anchor1 description
text: &anchor2 |
blocktext
blocktext
blocktext
- name: bar
desc: *anchor1
text: *anchor2
This is equivarent to the following Ruby code.
anchor1 = "description"
anchor2 = <<END
blocktext
blocktext
blocktext
END
[
{ 'name'=>'foo',
'desc'=>anchor1,
'text'=>anchor2,
},
{ 'name'=>'bar',
'desc'=>anchor1,
'text'=>anchor2,
}
]
Comment
YAML comment starts with the '#' character. Between '#' and end of line is YAML comment.
# comment
# comment
members: # comment
- name: Foo # comment
age: 19 # comment
YAML has only line comment.
Special Characters
YAML uses several characters to describe features.
| char | feature |
|---|---|
| [ | start of inline-style sequence |
| ] | end of inline-style sequence |
| { | start of inline-style mapping |
| } | end of inline-style mapping |
| - | sequence element in block-style |
| ? | mapping element in block-style |
| : | separator of key and value in block-style and flow-style |
| * | alias |
| & | anchor |
| ! | data type |
| # | comment |
| | | text in block-style |
| > | text in block-style |
Notice that these characters may cause YAML parse error if you don't quote them with single quotation or double quotation.
## OK - foo *bar* baz ## NG (because '*foo' means alias in YAML) - *foo* bar baz ## OK - '*foo* bar baz'
See YAML Cookbook or YAML specification for details.
Cookbook
Recipes
Cookbook should contains some recipes.
- 'product:' part represents filename which is generated by the recipe.
- 'desc:' part represents the description about the recipe. Description is showed when command-line option '-l' specified.
- 'ingreds:' part represents filenames which are required to generate product. The part takes sequence of ingredient names.
- 'method*:' part represents how to generate the product from the ingredients. The part is described in Ruby code (tail asterisk '*' means that "this is Ruby code").
In the 'method*:' part, some helper functions provided by Rook are available. For exaple, function 'sys()' invokes OS-depend command. See References for details about helper functions.
The following is an example of recipe definitions in cookbook.
recipes:
- product: hello
desc: generates hello command
ingreds: [ hello.o ]
method*: |
sys "gcc -g -o hello hello.o"
- product: hello.o
desc: compile 'hello.c' and 'hello.h'
ingreds: [ hello.c, hello.h ]
method*: |
sys "gcc -g -c hello.c"
The following is an example of invoking rook command. Command-line option '-l' shows recipes which have 'desc:' part. It means that recipes which have 'desc:' part are public recipes. Command-line option '-L' shows all recipes.
$ rook -l properites: recipes: hello : generates hello command hello.o : compile 'hello.c' and 'hello.h' $ rook hello ### ** hello.o rook$ gcc -g -c hello.c ### * hello rook$ gcc -g -o hello hello.o
One of the aim of Rook is fusion of 'declarative' and 'procedural'. Attributes of recipe are described in declarative format (=YAML) and steps how to generate product are described in procedural format (=Ruby).
The benefits of this style are:
- Easy to read and write for human. Readability and Writeability is very important.
- Powerful to write complex logics. You can get power of Ruby in YAML.
Ruby is the language which is easy to define DSL(Domain Specific Language). But not all the language have the ability to define DSL as easily as Ruby. Embedding script code in YAML solve the problem.
@product and @ingreds
Product and ingredient names are referable as instance variable in recipe's 'method*:' part.
- Variable '@product' represents product.
- Variable '@ingreds' represents ingredients.
- Variable '@ingred' represents the first item of ingredients (= @ingreds[0]).
@product and @ingredsrecipes:
- product: hello
desc: generates hello command
ingreds: [ hello.o ]
method*: |
sys "gcc -g -o #{@product} #{@ingred}"
# or sys "gcc -g -o #{@product} #{@ingreds[0]}"
- product: hello.o
desc: compile 'hello.c' and 'hello.h'
ingreds: [ hello.c, hello.h ]
method*: |
sys "gcc -g -c #{@ingred}"
# or sys "gcc -g -c #{@ingreds[0]}"
$ rook hello ### ** hello.o rook$ gcc -g -c hello.c ### * hello rook$ gcc -g -o hello hello.o
Specific recipe and generic recipe
Specific recipe is a recipe which is combined to a certain file. Product name of specific recipe is a concrete file name.
Generic recipe is a recipe which is combined to a pattern of file name. Product name of generic recipe is a pattern with metacharacter or regular expression.
Rook converts file name pattern into regular expression. For example, file name pattern '*.o' is coverted to regular expression '/^(.*)\.o$/', and '*.??.txt' to '/^(.*)\.(.)(.)\.txt$/'. Matched strings with metacharacter ('*' or '?') are accessable by '$(1)', '$(2)', ... in recipe definition and instance variable '@m' in 'method*:' part.
recipes:
## specific recipe
- product: hello
desc: generates hello command
ingreds: [ hello.o ]
method*: |
sys "gcc -g -o #{@product} #{@ingred}"
## generic recipe
- product: *.o # or /^(.*)\.o$/
desc: compile '*.c' and '*.h'
ingreds: [ $(1).c, $(1).h ]
method*: |
sys "gcc -g -c $(1).c"
# or "gcc -g -c #{@m[1]}.c"
# or "gcc -g -c #{@ingred}"
$ rook -l properites: recipes: hello : generates hello command *.o : compile '*.c' and '*.h' $ rook hello ### ** hello.o rook$ gcc -g -c hello.c ### * hello rook$ gcc -g -o hello hello.o
It is able to specify regular expression instead of filename pattern.
For example, /^(.*)\.o$/ is available as product instead of *.o.
Grouping in regular expression is referable by $(1), $(2), ... in the same way.
Specific recipe is prior to generic recipe. For example, recipe 'hello.o' is used and recipe '*.o' is not used to generate 'hello.o' when target product is 'hello.o' in the following example.
recipes:
- product: hello.o
ingreds: [ hello.c ]
method*: |
sys "gcc -g -O2 -o #{@product} #{@ingred}"
- product: *.o
ingreds: [ *.c, *.h ]
method*: |
sys "gcc -g -o #{@product} #{@ingred}"
Toppings
There may be a case that ingredient file exists or not. For example, product 'foo.o' depends on 'foo.c' and 'foo.h', while product 'bar.o' depends only on 'bar.c'.
In the case, you can use 'toppings:' part which resolve the problem.
For example when 'toppings: [hello.h]' is specified in recipe definition, Rook detect dependency as following.
- If file 'hello.h' exists, product 'hello.o' depends on ingredients 'hello.c' and 'hello.h'.
- If file 'hello.h' doesn't exist, product 'hello.o' depends on only 'hello.c'.
'toppings:' part is useful especially when used with generic recipes.
recipes:
## specific recipe
- product: hello
desc: generates hello command
ingreds: [ hello.o ]
method*: |
sys "gcc -g -o #{@product} #{@ingred}"
## generic recipe
- product: *.o
desc: compile '*.c' and '*.h'
ingreds: [ $(1).c ]
toppings: [ $(1).h ]
method*: |
sys "gcc -g -c #{@ingred}" # or "gcc -g -c $(1).c"
$ rook hello ### ** hello.o rook$ gcc -g -c hello.c ### * hello rook$ gcc -g -o hello hello.o
Symbolic Recipes
Symbolic recipe is a recipe which is not combined with any files. Product name of symbolic recipe starts with colon (':').
For example, symbolic recipe :clean is a recipe to delete '*.o' files and is not combined to file ':clean'.
Also symbolic recipe :all is a recipe to call recipe of 'hello' and is not combined to file ':all'.
recipes:
## specific recipe
- product: hello
desc: generates hello command
ingreds: [ hello.o ]
method*: |
sys "gcc -g -O2 -o #{@product} #{@ingred}"
## generic recipe
- product: *.o
desc: compile '*.c' and '*.h'
ingreds: [ $(1).c ]
toppings: [ $(1).h ]
method*: |
sys "gcc -g -O2 -c #{@ingred}" # or "gcc -g -c $(1).c"
## symbolic recipes
- product: :clean
method*: |
rm_f '*.o' # remove '*.o' files
- product: :all
ingreds: [ hello ]
$ rook -l properites: recipes: hello : generates hello command *.o : compile '*.c' and '*.h' :clean : remove all byproducts and itermediates :all : cook all $ rook :all ### *** hello.o rook$ gcc -g -O2 -c hello.c ### ** hello rook$ gcc -g -O2 -o hello hello.o ### * :all $ ls Rookbook.yaml hello hello.c hello.h hello.o $ rook :clean ### * :clean rook$ rm -f *.o $ ls kookbook.yaml hello hello.c hello.h
Rook have several well-known symbolic product. Symbolic recipes which product is contained in the following list are got public automatically.
- :all
- create all products
- :clean
- remove by-products
- :clear
- remove all products and by-products
- :deploy
- deploy products
- :config
- configure
- :setup
- setup
- :install
- install products
- :test
- do test
Properties
Property is a pair of name and value. You can set properties in cookbook.
You can get property value with '$(name)' notation in property definition and recipe definition. And properties are accessable as instance variable in 'method*' part of recipe definition.
Property name should match to regular expression '/^[_a-zA-Z]\w*$/'. Notice that period ('.') or hyphen ('-') are not available as property name character.
properties:
- cc: gcc
- c_flags: -g -O2
- basename: hello
recipes:
## specific recipe
- product: $(basename)
desc: generates $(basename) command
ingreds: [ $(basename).o ]
method*: |
sys "#{@cc} #{@c_flags} -o #{@product} #{@ingred}"
## generic recipe
- product: *.o
desc: compile '*.c' and '*.h'
ingreds: [ $(1).c ]
toppings: [ $(1).h ]
method*: |
sys "#{@cc} #{@c_flags} -c #{@ingred}" # or "#{@cc} #{@c_flags} -c $(1).c"
## symbolic recipes
- product: :clean
method*: |
rm_f '*.o'; # remove '*.o' files
- product: :all
ingreds: [ $basename ]
$ rook hello ### ** hello.o rook$ gcc -g -O2 -c hello.c ### * hello rook$ gcc -g -O2 -o hello hello.o
Properties are shown when command-line option '-l' is specified.
And it is able to overwrite properties in command-line option '--propname=propvalue'.
Properties specified in command-line are prior to that in Rookbook.
$ rook -l properites: cc = "gcc" c_flags = "-g -O2" basename = "hello" recipes: hello : generates hello command *.o : compile '*.c' and '*.h' :clean : remove all byproducts and itermediates :all : cook all $ rook -l --basename=foobar properites: basename = "foobar" cc = "gcc" c_flags = "-g -O2" recipes: foobar : generates foobar command *.o : compile '*.c' and '*.h' :clean : remove all byproducts and itermediates :all : cook all
Property file is another way to specify properties. If you have create property file 'Rookbook.props' or 'Properties.yaml', rook command automatically read it and set properties.
basename: hello c_flags: -g -O2
Property value can be specified in both YAML format and Ruby code. If you append '*' in tail of property name, Rook regard it's value as Ruby code. Property value is to be refered by '$(propname)' in YAML and by '$propname' in Ruby code.
properties:
- srcdir: src/main/java
- junit_jar: lib/junit.jar
- path_elems: [ '.', $(srcdir), $(junit_jar) ]
- classpath*: @path_elems.join(File::PATH_SEPARATOR)
recipes:
- product: :echo
method*: |
p @classpath #=> ".:src/main/java:lib/junit.jar"
Parameters
Parameters are similar to properties but have the following differences.
- Not able to override parameter value in command-line option nor property file.
- Not shown with command-line option '-l' (but shown with '-L').
You can consider properties as 'global variables' or 'public variable', and parameters as 'local variables' or 'private variable'.
Similar to properties, parameters value can be refered by '$(paramname)' in YAML and by 'paramname' (local variable) in Ruby code.
There is a special parameter 'rook_product'. If the parameter is set, rook regard it as the default target product when target product is not specified in command-line.
properties:
- cc: gcc
- c_flags: -g -O2
parameters:
- basename: hello
- rook_product: $(basename) # default product when no product
# specified in command-line
recipes:
## specific recipe
- product: $(basename)
desc: generates $(basename) command
ingreds: [ $(basename).o ]
method*: |
sys "#{@cc} #{@c_flags} -o #{basename} #{basename}.c"
# or sys "$cc #{@c_flags} -o #{@product} #{@ingred}"
## generic recipe
- product: *.o
desc: compile '*.c' and '*.h'
ingreds: [ $(1).c ]
toppings: [ $(1).h ]
method*: |
sys "#{@cc} #{@c_flags} -c #{@ingred}"
## symbolic recipes
- product: :clean
method*: |
rm_f '*.o' # remove '*.o' files
- product: :all
ingreds: [ $(basename) ]
$ rook -L properites: cc = "gcc" c_flags = "-g -O2" parameters: basename = "hello" rook_product = "hello" recipes: hello : generates hello command *.o : compile '*.c' and '*.h' :clean : remove all byproducts and itermediates :all : cook all $ rook ### ** hello.o rook$ gcc -g -O2 -c hello.c ### * hello rook$ gcc -g -O2 -o hello hello.c
Material
There is an exception in any case. Assume that you have a file 'optparse.o' which is supplied by other developer and no source. Rook will try to find 'optparse.c' and failed in the result.
Using 'materials:' part, you can tell Rook that 'optparse.o' is not a product .
properties:
- cc: gcc
- c_flags: -g -O2
parameters:
- basename: hello
- rook_product: $(basename)
materials:
- optparse.o
recipes:
## specific recipe
- product: $(basename)
desc: generates $(basename) command
ingreds: [ $(basename).o, optparse.o ]
method*: |
sys "#{@cc} #{@c_flags} -o #{@product} #{@ingred} optparse.o"
## generic recipe
- product: *.o
desc: compile '*.c' and '*.h'
ingreds: [ $(1).c ]
toppings: [ $(1).h ]
method*: |
sys "#{@cc} #{@c_flags} -c #{@ingred}" # or "#{@cc} #{@c_flags} -c $(1).c"
## symbolic recipes
- product: :clean
method*: |
rm_f '*.o'
- product: :all
ingreds: [ $(basename) ]
$ rook ### ** hello.o rook$ gcc -g -O2 -c hello.c ### * hello rook$ gcc -g -O2 -o hello hello.o optparse.o
Ruby DSL style Cookbook
Rook allows you to describe cookbook not only in YAML format but also Ruby DSL style.
- Properties are declared by
property()method. - Parameters are represented by local variables, or
parameter()method. - Recipes are defined by
recipe()method. - Method of recipe is described by block of
recipe()method. - Materials are declared by
material()method. - Description of recipe is specified by
desc()method in the before of the recipe.
## properties
property :cc, 'gcc'
property :c_flags, '-g -O2'
## parameters
basename = 'hello'
parameter :rook_product, basename
## materials
material 'optparse.o'
## recipes
desc "generated #{basename} command"
recipe basename, "#{basename}.c", "#{basename}.o" do |r|
sys "#{@cc} #{@c_flags} -o #{@product} #{@ingred} optparse.o"
end
desc "compile '*.c' and '*.h'"
recipe '*.o', "$(1).c", :toppings=>["$(1).h"] do |r|
sys "#{@cc} #{@c_flags} -c #{@ingred}"
# notice that "$(1).c" is not available in block!
end
recipe :clean do |r|
rm_f '*.o'
end
recipe :all, basename
Default name cookbook in Rook is Rookbook.yaml or Rookbook.rb. If suffix of cookbook is '.yaml' or '.yml' then rook loads cookbook in YAML mode, else if suffix is '.rb' then rook loads cookbook as Ruby script.
$ rook -l ### ** hello.o rook$ gcc -g -O2 -c hello.c ### * hello rook$ gcc -g -O2 -o hello hello.c optparse.o
Why rook supports both YAML style and Ruby DSL style? Because it is for choosability. YAML style is declarative and Ruby DSL style is procedural. Each approach have each advantages. You can choose which style to describe cookbook.
Other features
Invoke Recipes Forcedly
Command-line option '-F' invokes recipes forcedly, it means that timestamp of files are ignored.
recipes:
- product: hello
ingreds: [ hello.o ]
method*: |
sys "gcc -o #{@product} #{@ingred}"
- product: hello.o
ingreds: [ hello.c, hello.h ]
method*: |
sys "gcc -c #{@ingred}"
## create 'hello' $ rook hello ### ** hello.o rook$ gcc -c hello.c ### * hello rook$ gcc -o hello hello.o ## 'hello' is newer than 'hello.c' and 'hello.h' $ rook hello ## create 'hello' forcedly $ rook -F hello ### ** hello.o rook$ gcc -c hello.c ### * hello rook$ gcc -o hello hello.o
Validation check for Cookbook
Command-line option '-c' do validation checks for cookbook. It checks schema check of cookbook and Ruby syntax check.
property: # 'properties:' is correct
- cc: gcc
- c_flags: -g -O2
recipes:
- product: hello
method*: |
sys "#{@cc} #{@c_flags} hello.c"
mv 'a.out', 'hello'
$ rook -c validation error: Rookbook11.yaml:1: [/property] key 'property:' is undefined.
properties:
- cc: gcc
- c_flags: -g -O2
recipes:
- product: hello
method*: |
sys "#{@cc} #{@c_flags} hello.c"
rm_f 'a.out', 'hello', # extra comma
$ rook -c Rookbook12.yaml:9: parse error, unexpected $
Nested Array
You can specify not only filenames but also array of filenames as ingredient on 'ingreds:', 'toppings:', 'byprods:' and 'coprods:'. In addition, nested arrays are also available. These arrays are flattened by rook.
parameters:
- project: hello
- src_files*: Dir.glob('lib/*.rb') # array
- text_files: [README, ChangeLog, COPYING] # array (sequence)
- all_files: [$(src_files), $(text_files)] # nested array
recipes:
- product: :inspect
ingreds: [ package.xml, $(all_files) ] # nested array will be flatten
method*: |
# all_files is a nested sequence
puts "*** all_files (nested)"
p all_files
# $ingreds is flattened
puts "*** @ingreds (flattened)"
p @ingreds
$ rook :inspect ### * :inspect *** all_files (nested) [["lib/bar.rb", "lib/baz.rb", "lib/foo.rb"], ["README", "ChangeLog", "COPYING"]] *** @ingreds (flattened) ["package.xml", "lib/bar.rb", "lib/baz.rb", "lib/foo.rb", "README", "ChangeLog", "COPYING"]
Preparation
It is able to load external Ruby scripts and to define functions with 'preparation*:' section. You can write any Ruby code in 'preparation*:' section (tail asterisk '*' means that 'this is Ruby code').
'preparation*:' section is evaluated by rook before evaluating 'properties:', 'parameters:', and 'recipes:' sections. Functions you defined in 'preparation*:' section are available in 'properties:', 'parameters:', or 'recipe:' section.
preparation*: |
require 'my-util' # load external Ruby script
def my_join_path(list) # define original function
s = ''
list.each do |item|
if item && !item.empty?
s << File::PATH_SEPARATOR unless s.empty?
s << item
end
end
return s
end
parameters:
- path_elems: [ '.', src/main/java, null, '', lib/junit.jar ]
- classpath*: my_join_path(path_elems)
Recipe Listing
Command-line option '-l' shows list of properties and recipes which has description and '-L' shows list of properties, parameters, and all of recipes.
properties:
- cc: gcc
- c_flags: -g -O2
parameters:
- basename: hello
- kook_product: $(basename)
recipes:
# this will be shown with '-l' option because it has 'desc:'
- product: hello
desc: create hello command
# this will not be shown with '-l' option because no 'desc:'
- product: *.o
#desc: no comment
$ rook -l properites: cc = "gcc" c_flags = "-g -O2" recipes: hello : create hello command $ rook -L properites: cc = "gcc" c_flags = "-g -O2" parameters: basename = "hello" kook_product = "hello" recipes: hello : create hello command *.o :
Rook set description of symbolic recipe automatically if product name is ':all', ':clean', ':clear', or ':test'. This means that these symbolic recipes are listed automatically even when you omit recipe description.
recipes:
# the following are shown regardless no-description.
- product: :all
#desc: no description
- product: :clean
#desc: no description
- product: :clear
#desc: no description
- product: :test
#desc: no description
$ rook -l properites: recipes: :all : cook all :clean : remove all byproducts and itermediates :clear : remove all byproducts, intermediates, and products :test : do test
Recipe-local Parameters
Recipe can have it's own local parameters. Recipe local parameters cannot be refered by other recipes.
This is convenient to override property or parameter specified in cookbook, and to share method definition with other recipe by YAML anchor and alias.
parameters:
- c_flags: -g -Wall
recipes:
- product: *.o
ingreds: [ $(1).c ]
toppings: [ $(1).h ]
method*: &gcc |
sys "gcc #{c_flags} -c #{@ingred}"
- product: foo.o
ingreds: [ foo.c ]
params:
- c_flags: $(c_flags) -O2
method*: *gcc
$ rook -n hello.o ### * hello.o rook$ gcc -g -Wall -c hello.c $ rook -n foo.o ### * foo.o rook$ gcc -g -Wall -O2 -c foo.c
By-Products and Co-Products
'byprods:' and 'coprods:' sections represent by-products and co-products of the recipe. Currently, these section have no feature than description.
Instance variables @byprods and @coprods in 'method*:' section represents by-products and co-products of the recipe. Instance variable @byprod is equivarent to @byprods[0] and @coprod is equivarent to @coprods[0].
recipes:
- product: *.dvi
ingreds: [ $(1).tex ]
byprods: [ $(1).aux, $(1).log ]
method*: |
sys "latex #{@ingred}"
rm_f @byprods
References
Filesystem Functions
The following functions are available in 'method*:' part of recipes.
- sys cmmand-string
-
Execute command-string. If command status is not zero then exception is raised.
sys "gcc hello.c"
- sys! command-string
- Execute command-string. Command statuis is ignored even if it is zero.
- echo string
-
Echo string. Newline is printed.
echo "OK."
- echo_n string
-
Echo string. Newline is not printed.
echo_n "Enter your name: "
- chdir dir, &block
-
Change directory and do block.
chdir "lib" do cp_pr "**/*.rb", dir end
- cd dir, &block
- Equivarent to chdir.
- chmod mode, path[, path2, ...]
-
Change permission of path.
chmod 0644, "lib/**/*.rb" chmod 0755, "bin/*"
- chmod_R mode, path[, path2, ...]
-
Change permission of path recursively.
chmod_R 0644, "lib"
- chown user, group, path[, path2, ...]
-
Change owner of path. If user or group is nil then no changed.
chown nobody, nil, "lib/**/*.rb"
- chown_R owner, group, path[, path2, ...]
-
Change owner of path recursively. If user or group is nil then no changed.
chown nobody, nil, "lib"
- mkdir path
-
Make directory.
mkdir "lib"
- mkdir_p path
-
Make directory. If parent directory is not exist then it is created automatically.
mkdir_p "foo/bar/baz"
- rm path[, path2, ...]
-
Remove files.
rm '*.html', '*.txt'
- rm_r path[, path2, ...]
-
Remove files or directories recursively.
rm_r '*'
- rm_f path[, path2, ...]
-
Remove files forcedly. No errors reported even if path doesn't exist.
rm_f '*.html', '*.txt'
- rm_rf path[, path2, ...]
-
Remove files or directories forcedly.
No errors reported even if path doesn't exist.
rm_rf '*'
- touch path[, path2, ...]
-
Touch files or directories. If path doesn't exist then empty file is created.
touch '*.c'
- cp file1, file2
-
Copy file1 to file2.
cp 'foo.txt', 'bar.txt'
- cp file, file2, ..., dir
-
Copy file to dir.
cp '*.txt', '*.html', 'dir'
- cp_r path1, path2
-
Copy path1 to path2 recursively.
cp_r 'dir1', 'dir2'
- cp_r path, path2, ..., dir
-
Copy path to dir recursively. Directory dir must exist.
cp_r 'lib', 'doc', 'test', 'dir'
- cp_p file1, file2
-
Copy file1 to file2. Timestams is preserved.
cp_p 'foo.txt', 'bar.txt'
- cp_p file, file2, ..., dir
-
Copy file to dir. Timestams is preserved. Directory dir must exist.
cp_p '*.txt', '*.html', 'dir'
- cp_pr path1, path2
-
Copy path1 to path2 recursively. Timestams is preserved.
cp_pr 'lib', 'lib.bkup'
- cp_pr path, path2, ..., dir
-
Copy path to dir recursively. Directory dir must exist. Timestams is preserved.
cp_pr 'lib/**/*.rb', 'test/**/*.rb', 'tmpdir'
- cp_a path, path2, ..., dir
- Copy path to dir recursively. Timestams is preserved.
- mv file1, file2
-
Rename file1 to file2.
mv 'foo.txt', 'bar.txt'
- mv path, path2, ..., dir
-
Move path to dir.
mv 'lib/*.rb', 'test/*.rb', 'tmpdir'
- ln file1, file2
-
Create hard-link.
ln 'file1.txt', 'file2.txt'
- ln_s path, path2
-
Create symblic-link.
ln_s 'dir1', 'dir2' ln_s '*.txt', 'dir'
- ln_sf path, path2
-
Create symblic-link forcedly.
ln_sf 'dir1', 'dir2' ln_sf '*.txt', 'dir'
- store path, path2, ..., dir
-
Copy path (files or directories) to dir with keeping path-structure.
store "lib/**/*.rb", "doc/**/*.html", "dir"
- store_p path, path2, ..., dir
-
Copy path (files or directories) to dir with keeping path-structure.
Timestamp is preserved.
store_p "lib/**/*.rb", "doc/**/*.html", "dir"
- store_a path, path2, ..., dir
-
Copy path (files or directories) to dir with keeping path-structure.
Timestamp is preserved.
store_a "lib/**/*.rb", "doc/**/*.html", "dir"
- edit path, path2, ... { |content [,filename]| ... }
-
Edit file content.
If path is directory then it is ignored.
edit "lib/**/*.rb", "doc/**/*.html" do |content| content.gsub!(/\$Release\$/, '1.0.0') content.gsub!(/\$Copyright\$/, 'copyright(c) 2006 kuwata-lab.com') end
- zip archive, path1, path2, ...
-
Create zip archive file. Requires rubyzip library.
zip 'file.zip', 'file1.txt', 'file2.txt'
- zip_r archive, path1, path2, ...
-
Create zip archive file recursively. Requires rubyzip library.
zip_r 'file.zip', 'lib', 'doc', 'test'
- unzip archive
-
Unzip archive file. Requires rubyzip library.
unzip 'file.zip'
- tar_cf archive, path1, path2, ...
-
Create tar archive. Requies minitar library.
tar_cf 'file.tar', 'lib', 'doc', 'test'
- tar_xf archive
-
Extract tar archive. Requies minitar library.
tar_xf 'file.tar'
- tar_czf archive, path1, path2, ...
-
Create gzipped tar archive. Requies minitar library.
tar_czf 'file.tar.gz', 'lib', 'doc', 'test'
- tar_xzf archive
-
Extract gzipped tar archive. Requies minitar library.
tar_xzf 'file.tar.gz'
- tar_cjf archive, path1, path2, ...
-
Create gzipped tar archive.
Requies minitar library
and bz2 library.
tar_cjf 'file.tar.bz2', 'lib', 'doc', 'test'
- tar_xjf archive
-
Extract gzipped tar archive.
Requies minitar library
and bz2 library.
tar_xjf 'file.tar.bz2'
The above functions can take arrays as file or directory name. For example, the following code is available.
## specify filenames in strings cp 'file1.txt', 'file2.txt', 'file3.txt', 'dir' ## specify filenames with filename pattern cp '*.txt', 'dir' ## specify filenames in array. files = %w[file1.txt file2.txt file3.txt] cp files, 'dir'
The following file pattern is available.
-
* - Matches sequence of any character.
-
? - Matches a character.
-
{a,b,c} - Matches a or b or c.
-
**/ - Matches directory recursively.
Helper Functions
The following fuctions are available at 'properties:', 'parameters:', 'preparation*:', and 'method*:' in cookbook.
- Enumerable#apply(method_name, *args)
-
Appy method with args.
[1, 2, 3].apply(:'+', 5) #=> [6, 7, 8]
- Enumerable#add_prefix(prefix)
-
Add prefix for each item.
['a', 'b', 'c'].add_prefix('dir/') #=> ["dir/a", "dir/b", "dir/c"] - Enumerable#add_suffix(suffix)
-
Add suffix for each item.
['a', 'b', 'c'].add_suffix('.txt') #=> ["a.txt", "b.txt", "c.txt"] - Enumerable#sandwich(prefix, suffix)
-
Add prefix and suffix for each item.
['a','b','c'].sandwich('p/', '.s') #=> ["p/a.s", "p/b.s", "p/c.s"] - Enumerable#each_sub(pattern, replace, &block)
-
Apply 'sub()' method to each item.
['a.txt', 'b.txt'].each_sub(/\.txt$/, '.html') #=> ["a.html", "b.html"]
- Enumerable#each_gsub(pattern, replace, &block)
-
Apply 'gsub()' method to each item.
['a.txt', 'b.txt'].each_gsub(/\.txt$/, '.html') #=> ["a.html", "b.html"]
- Enumerable#delete_suffix(suffix=/\.\w+\z/)
-
Delete suffix for each item.
['a.txt', 'b.txt'].delete_suffix() #=> ["a", "b"]
- Enumerable#basenames()
-
Get basename of each item.
['dir1/a.txt', 'b.txt'].basenames() #=> ['a.txt', 'b.txt']
- Enumerable#dirnames()
-
Get dirname of each item.
['dir1/a.txt', 'b.txt'].dirnames() #=> ['dir1', '.']
Command-line options
Usage: kook [-hvTnqQFlLc] [-b bookname] product
Options:
- -h
- Print help and quit.
- -v
- Print version and quit.
- -l
- List properties and described recipes.
- -L
- List all propeties, parameters, and recipes.
- -q
- Quiet mode.
- -Q
- More quiet mode.
- -F
- Force recipes to execute (ignore timestamps).
- -n
- No command execution. Notice that ruby code is executed.
- -T
- Don't untabify cookbook. In default Rook command expands tab characters in cookbook into spaces, because YAML parser doesn't allow cookbook (= YAML document) to include tab characters. Command-line option '-T' doesn't expand tab characters.
- -c
- Validation check of cookbook.
- -b bookname
- Cookbook filename.
- -P propfile
- Property filename (default is 'Rookbook.props' and 'properties.yaml'). '-P -' means not to read property file.
- --propname=propvalue
- Property name and value.