README.rdoc in rubu-0.0.1 vs README.rdoc in rubu-0.0.2
- old
+ new
@@ -4,59 +4,287 @@
programs. {Rubu} is in practice a replacement for Make and Rake type
of tools. {Rubu} is targeted to provide means for creating flexible
build environments.
Make and Rake are rule and recipe based, and they are very effective
-when the build process can be captured to these rules and are fairly
+when the build process can be captured to these rules and is fairly
fixed. {Rubu} includes declarations which correspond to rules, but the
declarations are lower level and provide more control over the
behavior. {Rubu} also provides direct control over when the rules are
executed.
Make and Rake are more compact for the simplest build environments,
but there is a break-even point where {Rubu} becomes more
convenient. This is likely to happen when the user needs many
-exceptions to basic rules, also when other tasks than just build task
+exceptions to basic rules, also when other tasks than just build tasks
are needed.
{Rubu} library can be used from any Ruby program, since it is just a
library. It can also be part of a larger program as less significant
part of the overall functionality. From maintenance point of view this
can be a win in many cases.
+The easiest way to get started with {Rubu} is to study examples (See:
+"Example" chapter below). However for longer term the main concepts
+are important as well (See: "Concepts" chapter below).
-== Concepts
-TBD
-
-
== Example
Example of {Rubu} build program is placed in:
example/bin/rubu_example
It builds a "hello world" type program which is split into two C
source files. The main file is hand written and the other file is
generated with a script. File generation is part of the build program.
-Various executions of the build is captured in:
+Different execution variations of the build program is captured in:
example/runme
Please see example executions within the script and run the script in
the "example" directory:
cd example
runme
-Also take a look into the build program:
+Also take a look into the build program itself:
cat example/bin/rubu_example
There are comments which highlight purpose of the build program
content.
+
+== Concepts
+
+{Rubu} Build Program, RBP, is a Ruby program that uses the {Rubu}
+library. Typically command line options are accepted to give options
+for the build process.
+
+Build programs are about collecting source files and transforming them
+into targets. This process is explicit in {Rubu}. Each file, whether
+it's source or target, is collected within the {Rubu} build
+program. The files are converted into *Mark* objects. Basically *Mark*
+corresponds to a file, but it includes methods that make the build
+process convenient.
+
+*Step* is build action that transforms source file(s) to target
+file(s). For example, a C source file compilation is a *Step* where
+one source file is converted to a corresponding object file. *Step* is
+given source *Mark* and target *Mark* as arguments. Hence this type of
+*Step* a is one-to-one mapping.
+
+Linking multiple objects to an executable is a many-to-one
+mapping. This is performed by a *Step* where there are multiple
+sources and one target, i.e. objects and the executable respectively.
+
+*Step* class (or object) has "step" method which includes the *Move*
+that is needed to create the target. *Move* is either a Shell or a
+Ruby command. *Move* is executed only if necessary. Typically the
+*Step* is sensitive to source being newer that the target, just as
+with Make and Rake. However, it is possible to have a *Step* that is
+sensitive to file content, for example. Timestamp might change, but if
+the content is the same as previously, no *Move* is made.
+
+*Step* is typically a simple command. In some cases *Step* can include
+multiple *Moves*. These can be performed in sequence or in parallel,
+with a multicore CPU.
+
+*Trail* is a collection of *Steps*. *Trail* has a name, and it can be
+references by another *Trail*. *Trail* can be sequential or
+parallel. For example, C source to object compilation can be done in
+parallel, but linking of objects to executable must occur after all
+objects have been created.
+
+{Rubu} Build Program includes all of the above, and the last step is
+to select which *Trail* is executed. There should be at least
+"default" *Trail*, and typically there is also a "clean" *Trail*.
+
+{Rubu} supports the configuration spaces: *Order*, *Var*, and
+*Info*. *Order* is meant to be used for controlling {Rubu} itself. For
+example if user wants to see all executed command, then "verbose"
+option should be selected.
+
+*Var* is used to control how *Steps* behave and also details of *Move*
+internals. *Info* is meant for runtime generated information. The
+division is semantic and user might choose to combine everything to
+*Var* space, for example.
+
+To summarize and highlight the relationships between concepts, see the
+diagram below:
+
+ RBP => Trial+ => Step+ [s/p] => Move+ [s/p] => Source*, Target*
+ => Order+
+ => Var*
+ => Info*
+
+ * = Zero or more
+ + = One or more
+ s = Serial
+ p = Parallel
+
+RBP includes *Trials*, *Orders*, and possibly *Vars* and
+*Infos*. *Trial* includes one or more *Steps*, which can be executed
+in sequence or in parallel.
+
+*Step* includes one or more *Moves*, and they can also be sequential
+or parallel. *Move* has typically at least one *Source* and at least
+one *Target*. However, there are exceptions as well.
+
+
+The suggested ordering within {Rubu} Build Program is:
+
+[configuration] Process command line arguments and assign options that
+ are used to control the build process.
+
+[file collection] Collect source and target files either using Ruby
+ with glob pattern, direct references, or through
+ manifest files. Convert file names to *Mark*
+ objects.
+
+[step definition] Define *Steps*. Inherit one of the *Step* types
+ (classes) and define at least the "step" method.
+
+[trail definition] Define *Trails*. Create all possible flows that
+ user wants to execute. Use hierarchy for your
+ benefit in order to make RBP maintenance as light
+ as possible.
+
+[trail selection] Find the selected *Trail* or *Trails* and execute.
+
+
+== Configuration
+
+*Order* space default are set by the {Rubu} library and user can
+override the default based on command line parameters if needed.
+
+Set default values for *Var* space. After default setting, override
+the defaults with possible control from command line arguments.
+
+Finally setup *Trails* by calling:
+
+ Trail.setup
+
+
+== File collection
+
+If the build environment has clear and clean directory structure, it
+is typically easiest to collect files programmatically. For example,
+to get all C source files:
+
+ cee_files = Mark.glob( "#{Var[:source_dir]}/*.c" )
+
+This creates a list of *Mark* objects which are source files. The
+corresponding object files can be created with "peer" method:
+
+ obj_files = cee_files.peer( Var[ :build_dir ], '.o' )
+
+This method invocation takes an Array of *Mark* objects and calls
+"peer" method for them. "peer" method changes the directory and
+extension, and returns a new *Mark* object. In some cases also the
+"basename" of the file is changed. This is achieved by giving the
+"peer" method a third argument specifying the new "basename".
+
+
+== Step definition
+
+*Step* is defined by inheriting of the {Rubu} *Step* classes and
+defining the custom "step" method. If a specific setup for the *Step*
+is required, then an additional "setup" method can be defined as well.
+
+ class CleanUp < StepAlways
+ def step
+ shrun "rm -f #{Var[ :build_dir ]}/*.o"
+ end
+ end
+
+
+"setup" method is typically used to change the command according to
+user configuration. "step" method is the action for *Step*. {Rubu}
+executes it, if necessary. For example, if *Step* is a *StepAged*
+class, then the "step" method is only called if the source *Mark* is
+newer than the target *Mark*.
+
+"step" method may include only one command or a set of commands. If
+one command is needed, it can be simply executed:
+
+ def step
+ shrun "rm -f #{Var[ :build_dir ]}/*.o"
+ end
+
+If there are multiple commands, then the commands can be grouped for
+sequential ("walk") or parallel execution ("fork").
+
+ def step
+ fork do
+ shuse "rm -f #{Info[ :gen_files ].rpath}"
+ shuse "rm -f #{Var[ :build_dir ]}/*.o"
+ end
+ end
+
+Note that the commands are defined with "shuse". The logic is that we
+don't execute the commands right away as in "shrun". Instead we just
+register the commands to the group. When the group definition is
+closed ("end" keyword), it is complete, and we execute it either
+sequentially or in parallel.
+
+
+== Trail definition
+
+*Trail* is a collection of *Steps* or other *Trails*, or a mix of
+both. For example, in order to compile a set of C source files to
+object files, we might define:
+
+ Fork.form 'compile-cee' do
+ GccCompileFile.usezip( cee_files, obj_files )
+ end
+
+This creates a parallel ("Fork") *Trail* called
+"compile-cee". Collection of *Steps* is created with "usezip"
+method. "usezip" performs two operations: it creates a zip type mix of
+"cee_files" and "obj_files". The "zip" method of Ruby Array is
+used. "zip" creates an Array of *Mark* pairs. Each pair is passed to a
+"GccCompileFile" *Step* object. Finally we have a collection of
+*Steps* which are executed in parallel when "compile-cee" is invoked.
+
+Here the defined *Trail* from above is referenced in order to create a
+*Trail* for the complete compile flow:
+
+ Walk.form 'default' do
+ pick 'compile-cee'
+ GccLinkExe.use( obj_files, exe_file )
+ end
+
+This *Trail* definition defines that we want to first execute
+"compile-cee" and then a *Step* which will create an executable from
+object file collection. "GccLinkExe" *Step* is called with "use"
+method, which will register the *Step* to the enclosing *Trail*,
+"default".
+
+
+== Trail selection
+
+The final stage in RBP execution is to use command line control for
+selecting the executed *Trails* and then execute them. The above
+definition (from previous section) is executed with:
+
+ Trail.run( 'default' )
+
+
+== Final notes
+
+{Rubu} treats single *Mark* objects and Array of *Mark* objects the
+"same" way. In practice this means that the most common methods for
+*Mark* objects are implemented for Array as well. Hence, you can call
+a *Mark* method for an Array, and they will actually be applied for
+each individual *Mark* in the collection.
+
+*Step* has sources and targets. Even if a step has only one source and
+one target, the actual container is an Array. If you want to refer to
+the source, i.e. the only existing source, you can use the "source"
+method. Same applies to targets (i.e. use the "target" method).
== Testing
For this version, example is the testcase.