YAB Programmer's / User's Guide
Version 0.9 (Beta)
March 17, 2002
Toolsets, Features, and Varients
Subdirectories and Imported Projects
Appendix Environment Variables
This manual makes extensive use of hyperlinks. You should read the HTML version whenever possible. Only rely on the printed version when you are away from the computer.
Code examples are placed in grey boxes as follows:
YABFile
executable = Toolset().make_executable('hello')
executable.add_files('hello.c')
If the example code should reside in a file, the filename appears centered on the first line.
Interactive sessions also appear in grey boxes. The OS prompt and user input appears in italics. The computer's response is in plain text.
c:\> echo "foo"
foo
Yab (Yet Another Buildtool) is another replacement for the ubiquitous Make tool. Many replacements have been built as a result of frustration with Make. Yab attempts to combine the best features. In particular:
Yab works at a higher level of abstraction: Make mostly leaves it to the programmer to specify which command line arguments need to be specified when building different types of targets. Unfortunately, these arguments differ from compiler to compiler and operating system to operating system. Yab provides a higher level API to the programmer by adopting the notion of Toolset from the Boost build system.
A Toolset is a Python object that represents the compiler and other build tools available on your system. You specify build parameters using a common toolset API, and Yab translates them into the appropriate compiler arguments.
Yab automatically calculates dependencies: Yab scans through your C and C++ source files for include directives. There is no need to use "mkdepend" or "gcc –M". Yab will automatically rebuild an object if an included header is changed.
Yab handles subdirectories (sanely): Yab scripts can be in one main file, or distributed among several directories. Each script file creates a Python namespace, and scripts can refer to each other's namespaces.
Yab scripts can also refer to other top-level yab scripts. This is useful when one project uses another as a component.
Yab scripts are written in Python: Like Cons and SCons, script writers have a full programming language at their disposal. There is no need to use less powerful macro facilities to cram functionality into a makefile.
Yab is essentially a Python library. In order to use Yab effectively for non-trival projects, you should be mildly familiar with Python.
Yab autoconfs: Yab includes some of the features of GNU autoconf. You can probe your build environment and test your compiler using a Python API. The results can be passed to the compiler through preprocessor macros or a config.h file. Yab can generate the config.h from a template or from scratch.
Yab has a powerful build variable mechanism: Make allows you to specify build variables as global values. The value of the variable must be the same for each target in the Makefile. Jam takes this a step further, allowing you to bind build variables on specific targets.
Yab also allows you to set build variables on any target. However, the components of that target will inherit the target's values. For example, if you set the CCFLAGS variable on a shared library to be '-fPIC', any C object file that gets compiled into that library will also have CCFLAGS=-fPIC.
Yab can do Java: Make cannot examine the directory structure of your source files. This is a major drawback for programming languages that impose a strict structure on their source files (like Java and Python). Yab includes Ant's notion of a fileset. Using filesets, you will not have to redundantly specify information that already exists in your source directory structure.
Yab can generate VC++ project files: Yab can automatically generate a Visual C++ project file from a YABFile. Visual C++ programmers need not give up the browsing features of the IDE to have a platform independent make.
Yab requires Python version 2.2 or greater. Many Linux systems come with 1.54 installed.[1] This is not good enough. By the time that Yab becomes mature enough to take over the world, 2.2 or greater should be the standard. In the meantime, we can speed Yab development by limiting ourselves to the newer versions of Python.
Follow these steps to install Yab:
There are several examples in the examples.tar.gz file on SourceForge. This simplest of these is the "hello" example, which consists of a YABFile and hello.c. Here is the YABFile:
YABFile
executable = Toolset().make_executable('hello')
executable.add_files('hello.c')
The Toolset function returns an object that represents the compiler and other build tools that exist on your system. It will look for common compilers in your system's path and return an appropriate toolset object. Currently there are toolsets for Visual C++, Cygwin, and GCC.
All toolsets support a common API. The link_executable method will create a new build target for an executable program. Targets also support a common API. The add_files method will add a set of files to the given target. The target object will choose the appropriate actions depending on the file name's extension. In this case, Yab will compile the hello.c source file and link it into the executable.
Yab takes a list of targets as arguments. There are three ways to specify a target to Yab:
In addition to normal targets, you may invoke an action by supplying the name of a Python function defined in the YABFile.
You may invoke Yab from any subdirectory of where the YABFile is stored. Yab will search up the directory tree for the YABFile.
Invoking Yab without any options will build the specified targets and run the specified actions. Type 'yab -?' to get a list of the other things Yab can do with the targets.
A toolset object represents the various compilation tools that are available to the environment. The currently supported toolsets are:
Toolset |
Description |
vc7 |
Visual C++ 7.0. Invoked from settings stored in the registry. |
vc6 |
Visual C++ 6.0. Invoked from settings stored in the registry. |
vc |
Visual C++, invoked from the command line. You must have installed VC so that the command line tools are in your path. |
gcc |
Gnu C / C++ under Linux. |
cygwin |
Gnu C / C++ under Cygwin (under Windows). |
You can specify which toolset to use through Yab's –t command line option, or let it pick an appropriate toolset automatically.
Toolsets have features which are mapped to specific compilation flags for that toolset. Each feature has a default value that is used if the user doen't override the value.
Feature |
Value |
Default |
Description |
threading |
single |
x |
Single threaded application |
|
mutli |
|
Multi-threaded application |
runtime_link |
dynamic |
x |
Link with shared version of runtime library |
|
static |
|
Link with static runtime library |
runtime_build |
release |
x |
Link with the release version of the runtime library |
|
debug |
|
Link with the debug version |
optimization |
speed |
x |
Optimize for speed |
|
off |
|
Disable optimizations |
|
space |
|
Optimize for space |
inlining |
on |
x |
Inline functions marked 'inline', or implemented in the class declaration |
|
off |
|
Don't inline anything |
|
full |
|
May inline anything it makes sense to |
debug_symbols |
1 |
x |
include debug symbols in build |
|
0 |
|
don't include debug symbols (also perform optimizations that would normally screw up the debugger) |
wide_character |
1 |
|
Include support for wide characters |
|
0 |
x |
don’t |
exception_handling |
1 |
x |
Include support for C++ exception handling |
|
0 |
|
don’t |
rtti |
1 |
x |
Include support for C++ runtime type identification |
|
0 |
|
don't |
profiling |
1 |
|
Include support for the profiler |
|
0 |
x |
don't |
struct_alignment |
1,2,4,8,16 |
|
align struct members on an N byte boundary |
|
auto |
x |
choose the default for the platform |
You may override toolset features by passing keyword / value pairs to the Toolset function. For example, to turn off inlining and optimizations (possibly for a debug build), do this:
YABFile
ts = Toolset(inlining='off', optimization='off')
executable = ts.make_executable('hello')
executable.add_files('hello.c')
A variant is a collection of feature settings that you can treat as a unit. There are 2 variants that come pre-defined for Yab. They are:
Any feature that is not specified in the variant takes on its default value.
You select a particular variant through the 'variant' keyword argument to the Toolset constructor. You may also specify other feature values, which will override the feature default and any settings in the variant:
YABFile
# make an executable suitable for the profiler.
ts = Toolset(variant='debug', profiling=1)
executable = ts.link_executable('hello')
executable.add_files('hello.c')
You can define your own variants through the define_variant function:
YABFile
define_variant('profiler', {'profiling' : 1}, base = 'release')
# make an executable suitable for the profiler.
ts = Toolset(variant='profiler')
executable = ts.link_executable('hello')
executable.add_files('hello.c')
You may specify a default variant through the –v command line parameter. If a variant is not specified to the Toolset function, Yab will use that variant. For example, to build 'hello' for release:
D:\projects\yab\test\hello>yab –v release executable
And for debugging:
D:\projects\yab\test\hello>yab –v debug executable
In order to compile the program, switch to the directory where the hello files are stored and type 'yab executable'. On a Windows machine with Visual C++ installed you get this:
D:\projects\yab\test\hello>yab executable
Parsing YABFile
Looking for cl.exe... Found at d:\Program Files\Microsoft Visual Studio .NET\VC7\BIN\cl.exe
Target fixup
Scanning d:\projects\yab\test\hello\hello.c for includes
Building d:\projects\yab\test\hello\vc\release\hello\hello.exe
cl /nologo /MDd /O2 /Ob1 /GX /GR /Fd.\vc\release\hello\ /Fo.\vc\release\hello\ /c .\hello.c
hello.c
link /out:.\vc\release\hello\hello.exe /nologo /OPT:ICF /OPT:REF .\vc\release\hello\hello.obj
Just for fun, we'll try the same thing on a Linux machine, and get this:
[jeff@localhost hello]$ yab executable
Parsing YABFile
Looking for gcc... Found at /usr/bin/gcc
Target fixup
Scanning /home/jeff/yab/test/hello/hello.c for includes
Building /home/jeff/yab/test/hello/linux/release/hello/hello
gcc -O3 -Wno-inline -o ./linux/release/hello/hello.o -c ./hello.c
gcc -s ./linux/release/hello/hello.o -o ./linux/release/hello/hello
Yab put the built files for the different toolsets in different subdirectories. You may override the default location for built targets by specifying the keyword argument 'locate' to either the Toolset function or the make_executable method.
If you don't specify the 'locate' keyword, the defaults are calculated as follows:
The following YABFile will place hello's built files in the 'build' directory:
YABFile
executable = Toolset().make_executable('hello', locate = 'build')
executable.add_files('hello.c')
This YABFile will place hello's build targets in the "hello" subdirectory (removing the toolset subdirectory).
YABFile
executable = Toolset(locate = '.').make_executable('hello')
executable.add_files('hello.c')
Yab supports build attributes that can be used to parameterize build steps much like Make's macros. A build attribute is simply a Python attribute on a toolset or target object who's name is ALL UPPERCASE.
During the build process, the build attributes are merged together to get a build environment that is used to expand command strings. Each target has its own build environment. However, components inherit their target's and toolsets build environment.
For example:
>>> ts = Toolset()
>>> prog = ts.make_executable('prog')
>>> lib = prog.make_static_lib('lib')
>>> ts.BAR = 'a'
>>> prog.BAR = 'b'
>>> lib.BAR = 'c'
>>> ts.subst_vars('echo "$BAR"')
'echo "a"'
>>> prog.subst_vars('echo "$BAR"')
'echo "ab"'
>>> lib.subst_vars('echo "$BAR"')
'echo "abc"'
Since "prog" was constructed from "ts", the value of BAR is the concatenation of ts.BAR and prog.BAR (through the + operator). Likewise, since lib was constructed from "prog", BAR's value is ts.BAR + prog.BAR + lib.BAR.
You will never get an AttributeError when retrieving a build attribute from a toolset or target. Instead, the system will create a new, empty sequence and return it. The sequence is an object of type 'EnvVar'. EnvVars are exactly like tuples, except the "+" operator will split strings before adding them to the variable.
Toolset API
For example, if you are building a debug version of a program in MSVC, Yab will pass /Zi to the compiler. Under GCC, it will pass –g. If you are building a shared library under Unix, yab ignores the .def file. Under windows, Yab links in the import library and not the DLL.
[1] You can find out which version of Python is installed by typing ‘python -V’ at the command prompt. If it comes back with “unknown option –V” you need to install a newer version.