Copyright © 2007, 2008 Jay Berkenbilt, Argon ST
Table of Contents
Abuild.conf FileAbuild.mk FileList of Figures
general/referencegeneral/taskgeneral/userList of Tables
Welcome to the abuild manual! You may always find the latest copy of this manual on abuild's website. This manual is designed to get you up and running with abuild quickly: the most essential and common topics are presented first so that you can just start at the beginning and stop reading when you feel that you've seen enough to get going. Then, when you are ready, you can come back for documentation on the full depth of abuild's functionality. If you come across something in the first reading that you don't understand, it's probably safe to skip it and come back when you're more comfortable. As each new concept is presented, it is enhanced with examples. A list of all the examples in the document can be found in Appendix E, List of Examples. If you are just looking for changes from previous versions of abuild, please see Appendix A, Release Notes.
This manual is divided into four parts. Each part of the document draws on material introduced in the earlier parts. Although earlier parts of the documentation are intended to be understandable without the material from the later parts, they contain forward cross references where appropriate.
In Part I, “Quick Start”, we cover basic information that should help you come up to speed on using abuild for day-to-day work. It is geared toward people who are working on an existing software baseline that uses abuild. In Part I, you will learn about what abuild is and the types of problems it was designed to solve, be introduced to some basic terminology, and see a few examples of how to perform some simple build operations. This part of the manual is very short and is designed to be readable in one sitting. Casual users of abuild may have no need to read past Part I.
In Part II, “Normal Operation”, we introduce the most common features of abuild. All the basic features are covered, and a few advanced features are covered. All the information you need for simple projects has been presented by the end of Part II.
In Part III, “Advanced Functionality”, we introduce advanced topics. By the end of Part III, you will have been exposed to every feature of abuild.
Part IV, “Appendices” consists of a small handful of appendices.
For those wishing to go still deeper, the abuild source code is heavily commented, and the software comes with a thorough automated test suite that covers every feature of the software and many error conditions as well.
The creation of abuild would not have been possible without the enthusiastic support of my employer, Argon ST. Argon not only recognized the important role of a strong build tool in contributing to the overall quality and reliability of its software, but saw the value of releasing it to the open source community in hopes of making an even broader contribution.
There are many people within Argon who helped take abuild to where it is now, but among these, a handful of people deserve special mention:
Chris Costa, who served as a sounding board and contributed numerous ideas throughout the entire development process of abuild, including conducting a thorough review of this document
Andrew Hayden, who spent many hours reviewing and critiquing this entire document and who was the visionary behind Xenon, the abuild Eclipse plugin
Joe Davidson, the primary abuild evangelist who has been invaluable in getting abuild to become as widely accepted within Argon ST as it is
Gavin Mulligan, who has consistently taken the time to report any problem, no matter how small, and who is probably responsible for reporting more issues than everyone else combined
Bob Tamaru, who in addition to being a mentor and supporter for most of my career, provided considerable assistance to me as I presented the case to Argon ST to allow me to release abuild as an open source project
This manual is written in docbook. The PDF version of the manual was generated with Apache fop, which as of this writing, is still incomplete. There are a few known issues with the PDF version of the documentation. Hopefully these issues will all be addressed as fop matures.
There are many bad line breaks. Sometimes words are incorrectly
hyphenated, and line breaks also occur between two dashes in
command line options and even between the two
+ characters of “C++”.
In many of the example listings, there are lines that would be
longer than the shaded boxes in the PDF output. We wrap those
lines and place a backslash (\) character
just before and after the extra line breaks. This is done for
both the HTML and the PDF output even though the long lines are
only a problem for the PDF output.
Some paragraphs appear to have extra indentation. This is because the formatting software generates a hard space whenever we have an index term marker in the text.
There are no bookmarks. It would be good if we could create bookmarks to the chapter headings, but as of this writing, the documented procedure for doing this does not appear to work.
The material contained in this part is geared toward new and casual users of abuild. Without going into excessive detail, this part gives you a quick tour of abuild's functionality and presents a few examples of routine build operations. By the end of this part, you should be able to use abuild for simple build operations, and you should have begun to get a feel for the basic configuration files.
Table of Contents
Table of Contents
Abuild is a system designed to build large software projects or related families of software projects that are divided into a potentially large number of components. It is specifically designed for software projects that are continually evolving and that may span multiple languages and platforms. The basic idea behind abuild is simple: when building a single component (module, unit, etc.) of a software package, the developer should be able to focus on that component exclusively. Abuild requires each component developer to declare, by name, the list of other components on which his or her component depends. It is then abuild's responsibility to provide whatever is needed to the build environment to make other required items visible.
You might want to think of abuild as an object-oriented build system. When working with abuild, the fundamental unit is the build item. A build item is essentially a single collection of code, usually contained within one directory, that is built as a unit. A build item may produce one or more products (libraries, executables, JAR files, etc.) that other build items may want to use. It is the responsibility of each build item to provide information about its products that may be used by other items that depend on it. This information is provided by a build item in its abuild interface. In this way, knowledge about how to use a build item is encapsulated within that build item rather than being spread around throughout the other components of a system.
To implement this core functionality, abuild provides its own system for managing build items as well as the dependencies and relationships among them. It also provides various build rules implemented with underlying tools, specifically GNU Make and Apache Ant, to perform the actual build steps. We refer to these underlying tools as backends. Although the bulk of the functionality and sophistication of abuild comes from its own core capabilities rather than the build rules, the rules have rich functionality as well. Abuild is intended to be your build system. It is not intended, as some other tools are, to wrap around your existing build system. [1]
Support for compilation in multiple programming languages and on
multiple platforms, including embedded platforms, is central to
abuild's design. Abuild is designed to allow build items to
be built on multiple platforms simultaneously. An important way
in which abuild achieves this functionality is to do all of its
work inside of an output directory. When
abuild performs the actual build, it always creates an output
directory named
abuild-
and invokes the backend in that directory. By actually invoking
the backend in that directory, abuild avoids the situation of
temporary files conflicting with each other on multiple
simultaneous builds of a given build item on multiple platforms.
Abuild is designed to never create or remove any output files
outside of its output directories. This enables abuild's
cleanup operation to simply remove all output directories created
by any instance of abuild, and also reduces the likelihood of
unintentionally mixing generated products with version-controlled
sources.
platform
The following list shows the font conventions used throughout this document for the names of different kinds of items.
literal text |
replaceable text |
build items and build item
scope names
|
| Abuild.conf keys, flags, and traits |
Abuild.interface variables, java
properties, and make variables
|
Abuild.interface keywords
|
| commands, build targets, and ant hooks |
command line options and build
sets
|
environment variables
|
file names and make rule
sets
|
| platforms, platform types, and target types |
This section describes what you can expect in terms of abuild version numbers and non-compatible changes.
Each abuild release is assigned a version number. For abuild releases, we use the following version numbering convention:
major.minor[patch-or-pre]
The major field of the version number
indicates the major version number. It changes whenever a major
release is made. A new major release of abuild represents a
wholesale change in the way abuild works. Major release are
expected to be very infrequent.
The minor field of the version number
indicates the minor version number. It changes whenever a minor
release is made. A minor release is an incremental release that
may introduce new features, fix bugs, or change the way some
things work, but it will not fundamentally shift the way
abuild works. We impose tight restrictions on the
introduction of non-backward-compatible changes in minor
releases as discussed below.
The patch-or-pre field can indicate
either a patch release or a prerelease. A patch release is a
bug-fix only release. A prerelease is an alpha or beta release
or a release candidate that precedes a regular release.
Before a regular major or minor release, there may be a series
of alpha releases, beta releases, and release candidates. In
those cases, the patch-or-pre field
of the version number is either
“a”,
“b”, or
“rc” followed by a number. The
prerelease version numbers clearly indicate which regular
release the prerelease applies to. For example, version
1.3.a4 would be the fourth alpha release
preceding the release of version 1.3.
After any major or minor release, it is possible that a small
problem may be corrected in a bug-fix release. In such a
release, the patch-or-pre field
contains a number that indicates which bug-fix release this is.
For example, version 1.2.1 would be a bug-fix
release to version 1.2.
In a new major release of abuild (e.g., version 2.0), there is no promise that changes will be backward compatible, nor is there any expectation that configuration files from older abuild releases will work with the new version. When possible, care will be taken to mitigate any inconvenience such as providing upgrade scripts.
In each new minor release of abuild, there may be new features
and backward-compatible changes. In minor releases, we adopt a
stricter policy regarding non-backward-compatible changes.
Specifically, non-backward-compatible changes may be introduced
only if the changed construct generated a deprecation warning in
the previous minor release. In other words, if particular
construct in version 1.3 is going to be
dropped or changed in a non-compatible way, the change can't be
made until version 1.5. In version
1.4, the new way may work, but use of the
deprecated construct must still work and must generate a
warning. The old way can be dropped entirely in version
1.5 once users have had a chance to adjust
their configuration files. In that way, users who take every
minor release upgrade can be guaranteed that they will not
experience surprise non-compatible changes, and they will not
have to update their configuration files at the same time that
they upgrade abuild.
With alpha releases, there is no commitment to avoiding non-compatible changes. In particular, a feature that was introduced into abuild during an alpha testing period may be modified in non-compatible ways or dropped entirely during the course of alpha testing. During beta testing, every effort will be made to avoid non-compatible changes, but they are still allowed. No non-compatible changes will be made from the first release candidate through the next minor release.
Specific exceptions may be made to any of the above rules, but any such exceptions will be clearly stated in the release notes or the documentation. It may happen, for example, that a particular new feature is still in development when a release is made. In that case, the release notes may declare that feature to still be alpha, in which case non-compatible changes can be introduced in the next release.
We'll clarify with some concrete examples. Suppose a new
feature is planned for version 1.4 of
abuild. It would be okay if the first implementation of that
feature appeared in version 1.4.a2 and if the
feature were changed in a non-compatible way in
1.4.a6. However, after version
1.4.rc1 was released, the next non-compatible
change would not be permitted until version
1.5.a1, and even then, the feature as it
worked in version 1.4 would still have to
work, though a deprecation warning would be issued. The old
version 1.4 way of doing things could stop
working altogether in version 1.6.a1.
This section describes many of the principles upon which abuild was designed. Understanding this material is not critical to being able to use abuild just to do simple compiles, but knowing these things will help you use abuild better and will provide a context for understanding what it does.
Abuild puts the integrity of the build over all other concerns. Abuild includes several rigorously enforced integrity checks throughout its implementation in order to prevent many of the most common causes of build integrity problems.
Build items must explicitly declare dependencies on other build items. These dependencies are declared by name, not by path. The same mechanism within abuild that is used to declare a dependency is also used to provide visibility to the dependent build item. (A build item reads the interfaces of only those build items on which it directly or indirectly depends.) In this way, it is impossible to accidentally become dependent on something by unwittingly using files that it provides. Abuild guarantees that there are no circular dependencies among build items and also provides a fundamental guarantee that all build items in a dependency chain resolve names to paths in a consistent way within the dependency tree.
Build items refer to each other only by name and never by path. Abuild resolves build item names to paths internally and provides path information at runtime as needed. This makes any specific abuild installation agnostic about directory structure and makes it possible to move things around without changing any build rules. In this way, abuild stays out of the way when it's time to reorganize your project.
When using abuild, you are generally able to focus on building just the item you are working on without having to worry about the details of the items it depends on. Abuild does all the work of figuring out what your environment has to look like to give you access to your dependencies. It can then start a local build from anywhere and pass the right information to that local build. This is achieved through encapsulation of knowledge about a build item's products inside the build item itself and making that knowledge available to its users through an abuild-specific interface.
Abuild does not require you to have any project-specific or source tree-specific environment variables set, be using any particular shell or operating system, or have the abuild software itself installed in any particular location. Abuild is designed so that having the abuild command in your path is sufficient for doing a build. This keeps abuild independent from any specific source tree or project. Abuild can be used to build a single-source-file, stand-alone program or an elaborate product line consisting of hundreds or thousands of components. It can be also used for multiple projects on the same system at the same time. No special path settings or environment variable settings are required to use abuild, other than ensuring that the external tools that your build requires (GNU Make, compilers, etc.) are available and in your path.
When building multiple items, abuild creates a build set consisting of all the items to be built. It computes the directories in which it needs to build and invokes the build iteratively in those directories. Abuild automatically figures out what can be built in parallel and what the build order should be by inspecting the dependency graph. Abuild avoids many of the pitfalls that get in the way of parallel and distributed operation including recursive execution, shell-based loops for iteration, file system-based traversal, and writing files to the source directory.
Abuild was designed to work on multiple platforms. It includes a structure for referring to platforms and for encapsulating platform-specific knowledge. This makes it easier to create portable build structures for portable code.
Abuild aims to be as efficient as possible without compromising build integrity. Abuild calculates as much as possible up front when it is first invoked, and it passes that information to backend build programs through automatically-generated files created inside its own output directories. By computing the information one time, abuild significantly reduces the degree to which its backend build programs' rules have to use external helper applications to compute information they need. Abuild's configuration files and build tree traversal steps are designed in such a way that abuild never has to perform unbounded searches of a build tree. This enables startup to be fast even on build trees containing thousands of build items.
Build items encapsulate knowledge about what is required by their users in order to make use of them at build time. The user may also create build items with restricted scope, thus allowing private things to be kept private. This makes it possible to refactor or reorganize individual components of a system without affecting the build files of other build items that depend on them.
The majority of build item configuration files are declarative: they contain descriptions of what needs to be done, rather than information about how to do it. Most end user configuration files contain nothing but variable settings or key/value pairs and are independent of the platform or compiler used to build the item. For those cases in which a declarative system is insufficient to express what needs to do be done, abuild provides several mechanisms for specific steps to be defined and made available to the items that need them.
The parts of abuild that manage dependencies and build integrity are distinct from the parts of abuild that actually perform builds. Initial versions of abuild can use either GNU Make or Apache Ant to perform builds. The internal integration between abuild and its backend build programs is fairly loose, and adding additional backends would require relatively minor and localized code changes. In addition, abuild requires only the backends that a particular build tree uses to be present on your system when you are performing a build. That is, if you are building only Java code, you don't need GNU Make, and if you're building only C and C++ code, you don't need Apache Ant.
[1] Abuild can, however, interoperate with other build systems as needed, which may be useful while transitioning a software development effort to using abuild.
Table of Contents
You may always find the latest version of abuild by following the links on abuild's website. To use abuild, the following items must be available on your system:
GNU Make version 3.81 or higher is required if you are building any build items that use GNU Make as a backend. This would include platform-independent code and C/C++ code, but not Java code.
A Java 5 or newer Java SDK is required if you are going to use abuild to build Java code. Abuild is known to pass its test suite using GNU gcj 4.2.1 in addition to versions 1.5 or newer of Sun's Java SDK, so it is possible to build and run abuild in a pure open source environment.
Apache Ant version 1.7.0 or newer with ant-contrib version 1.0.b3 or later installed in ant's lib directory is required if you are building any Java code. Abuild's own Java code requires a Java 5 Java SDK, but it could likely be ported fairly easily to any Java SDK that can build Apache Ant.
Perl version 5.8 or newer is required if you are performing any GNU Make-based builds.
Perl version 5.8 or newer and qtest version 1.0 or newer are required if you are using the qtest automated test framework. Abuild's own test suite uses qtest. Note also that qtest requires GNU diffutils. Any version should do.
In order to use abuild's autoconf support, you need autoconf version 2.59 or newer, automake version 1.9 or newer. These are also required for abuild's test suite to pass since the test suite exercises its autoconf support.
If you are planning on building any GNU Make-based build items on Windows, Cygwin is required. For a Java-only abuild installation on Windows, Cygwin and Perl are not required. It is hoped that a future version of abuild will not require Cygwin. For details on using Cygwin with abuild, please see Section 2.4, “Additional Requirements for Windows Environments”.
To build abuild, you must also have version 1.33.1 or newer of the boost regex and thread libraries. [2] Abuild is known to buildable by gcc, xlc, Microsoft Visual C++ (7.1 or newer), and mingw, [3] and it should be buildable by on any system and with any compiler that supports the boost thread and regular expression libraries. In order for shared library support to work properly with gcc, gcc must be configured to use the GNU linker. [4] Abuild itself contains C++ code and Java code, so all the runtime requirements for both systems are required to build abuild.
Since abuild determines where it is being run from when it is
invoked, a binary distribution of abuild is not tied to a
particular installation path. It finds the root of its
installation directory by walking up the path from the abuild
executable until it finds a directory that contains
make/abuild.mk. This makes it easy to have
multiple versions of abuild installed simultaneously, and it
also makes it easy to create relocatable binary distributions of
abuild.
Abuild itself does not require any environment variables to be
set, but ant and/or the Java
development environment may. If you have the
ANT_HOME environment variable set, abuild will
honor it when selecting which copy of
ant to run. Otherwise, it will run
ant from your path.
As you begin using abuild, you may find yourself generating a
collection of useful utility build items for things like specific
third-party libraries, external compilers, documentation
generators, or test frameworks. There is a small collection of
contributed build items in the
abuild-contrib package, which is available
at abuild's web
site. These may have additional requirements. For
details, please see the information about
abuild-contrib on the website.
Abuild is self-hosting: it can be built with itself, or for
bootstrapping, it can be built with a GNU Makefile that uses
abuild's internal GNU Make support.
To build abuild's Java code, you also need Apache
Ant and a Java development environment. Please see
the file src/README.build in the source
distribution for instructions on building abuild.
If you are creating a binary distribution or installing from
source, please see the file src/README.build
in the source directory. If you are installing from a pre-built
binary distribution, simply extract the binary distribution in
any directory. Abuild imposes no requirements on where the
directory should be or what it should be called as long as its
contents remain in the correct relative locations. You may make
a symbolic link to the actual bin/abuild
executable from a directory in your path. Abuild will follow
this link when attempting to discover the path of its
installation directory. You may also add the abuild
distribution's bin directory to your path,
or invoke abuild by the full path to its executable.
To build abuild and use it in a Windows environment for make-based builds, certain pieces of the Cygwin environment are required. [5] Note that abuild is able to build with and be built by Visual C++ on Windows. It uses Cygwin only for its development tools. Cygwin is not required to run executables built by abuild in a Windows environment, including abuild itself. However, Cygwin is required to supply make and perl to abuild. The following parts of Cygwin are required:
| autoconf |
| automake |
| make |
| rebase |
| diffutils |
Perl is required, but appears to be installed by default in recent Cygwin installations.
Note that rebaseall (from the rebase package) may need to be run
in order for fork to work from perl with
certain modules. (Although abuild itself doesn't call
fork from perl,
qtest, which is used for abuild's
test suite, does.)
Other modules may also be desirable. In particular,
libxml2 from the Text
section is required in order to run certain parts of abuild's
test suite, though the test suite will just issue a warning and
skip those tests without failing if it can't find
xmllint.
If you intend to use autoconf from Windows and you have Rational
Rose installed, you may need to create
/usr/bin/hostinfo (inside of the Cygwin
environment) as
#!/bin/false
so that ./configure's running of hostinfo doesn't run hostinfo from Rational Rose.
In order to use Visual C++ with abuild, you must have your environment set up to invoke Visual C++ command line tools. This can be achieved by running the shortcut supplied with Visual Studio, or you can create a batch file on your own. The following batch file would enable you to run abuild from a Cygwin environment with the environment set up for running Visual C++ from Visual Studio 7.1 (.NET 2003):
@echo off call "%VS71COMNTOOLS%"\vsvars32.bat C:\cygwin\cygwin.bat
Adjust as needed if your Cygwin is installed other than in
C:\cygwin or you have a different version of
Visual C++ installed.
In order to use qtest with abuild under Windows, the Cygwin version of Perl must be the first perl in your path.
Abuild creates output directories in the source directory, and
all generated files are created inside of these
abuild-generated directories. All output directories are named
abuild-*. It is recommended that you
configure hooks or triggers in your version control system to
prevent these directories or their contents from being
accidentally checked in. It may also be useful to prevent
Abuild.backing from being checked in since
this file always contains information about the local
configuration rather than something that would be CM controlled.
If it is your policy to allow these to be checked in, they should
be prevented from appearing in shared areas such as the trunk.
[6]
[2] Abuild is known to work with the boost 1.32 in Red Hat Enterprise Linux 4, but it may not work with that version of boost on other systems. It has also been tested with boost 1.34 and 1.34.1.
[3] Abuild does not pass its test suite reliably when built with mingw, and it also appears to run more slowly when built with mingw than when built with Visual C++. No serious effort has been put into figuring out why there are problems with abuild when built with mingw, but this may be addressed in conjunction of creating a version of abuild that works in Windows for C/C++ builds without Cygwin. Abuild appears to work with equal reliability on Windows with Visual C++ as on Linux with gcc.
[4] The only reason for the GNU linker requirement is that abuild currently knows about -fPIC. It would be better to have a more robust way of configuring flags for position-independent-code, but it's not clear how to do this without replicating all the knowledge built into libtool or having some autoconf-like method of configuring abuild at runtime.
[5] This may cease to be true in a future version of abuild.
[6]
Note, however, that the abuild test suite contains
Abuild.backing files, so any CM system
that contains abuild must have an exception for abuild
itself. It's conceivable that other tools could also have
reasons to have checked in Abuild.backing
files in test suites or as templates.
Table of Contents
In this chapter, we will describe the basics of running abuild
on a few simple build items, and we will describe how those build
items are constructed. We will gloss over many details that will
be covered later in the documentation. The goal of this chapter
is to give you enough information to work on simple build items
that belong to existing build trees. Definitions of
build item and build
tree appear below. More detailed information on them
can be found in Chapter 4, Build Items and Build Trees. The
examples we refer to in this chapter can be found in
doc/example/basic in your abuild source or
binary distribution.
Abuild imposes few system-based restrictions on how you set it up and use it, but here are a few important things to keep in mind:
Avoid putting spaces in path names wherever possible. Although abuild tries to behave properly with respect to spaces in path names and is known to handle many cases correctly, make is notoriously bad at it. If you try to use spaces in path names, it is very likely that you will eventually run into problems as they generally cause trouble in a command-line environment.
Be careful about the lengths of path names. Although abuild itself imposes no limits on this, you may run up against operating system limits if your paths are too long. In particular, Windows has a maximum path name length of 260 characters. If you have a build tree whose root already has a long path and you then have Java classes that are buried deep within a package-based directory structure, you can bump into the 260-character limit faster than you'd think. On Windows, it is recommended that you keep your build tree roots as close to the root of the drive as possible. On any modern UNIX system, you should not run into any path name length issues.
Here are a few basic terms you'll need to get started:
A build item is the most basic item
that is built by abuild. It usually consists of a directory
that contains files that are built. Any directory that
contains an Abuild.conf file is a build
item. We refer to the build item whose
Abuild.conf resides in the current
directory as the current build item.
A build tree is a collection of build items arranged hierarchically in the file system. All build items in a build tree may refer to each other by name. Each build item knows the locations of its parents and children within the file system hierarchy and the names of the build items on which it depends.
A target is some specific product to be built. The term “target” means exactly the same thing with abuild as it does with other build systems such as make or ant. In fact, with the exception of a small handful of “special” targets, abuild simply passes any targets given to it onto the backend build system for processing. The most common targets are all and clean. For a more complete discussion of targets, see Section 8.1, “Build Targets”. Be careful not to confuse target with target type, defined in Section 5.1, “Platform Structure”.
For a more complete description of build items and build trees, please see Chapter 4, Build Items and Build Trees.
Full details on compiler support and compiler selection are
covered in Section 21.1, “Platform Selection”. To get
started, on Linux systems, abuild will build with
gcc by default. On Windows, if you
run abuild from a shell that is appropriately set up to run
Microsoft Visual C++ (such as by running
%VS71COMNTOOLS%\vsvars32.bat, or similar for
your compiler version), abuild will automatically use Visual
C++. If you have cygwin installed with gcc and the mingw runtime
environment, abuild will attempt to use gcc
-mno-cygwin to build as long as you set the
MINGW environment variable to
1.
The directory cxx-library under
doc/example/basic contains a simple C++
library. Our library is called
basic-library. It implements the single C++
class called BasicLibrary using the header
file BasicLibrary.hh and the source file
BasicLibrary.cc. Here are the contents of
those files:
basic/cxx-library/BasicLibrary.hh
#ifndef __BASICLIBRARY_HH__
#define __BASICLIBRARY_HH__
class BasicLibrary
{
public:
BasicLibrary(int);
void hello();
private:
int n;
};
#endif // __BASICLIBRARY_HH__
basic/cxx-library/BasicLibrary.cc
#include "BasicLibrary.hh"
#include <iostream>
BasicLibrary::BasicLibrary(int n) :
n(n)
{
}
void
BasicLibrary::hello()
{
std::cout << "Hello. This is BasicLibrary(" << n << ")." << std::endl;
}
Building this library is quite straightforward. Abuild's build
files are generally declarative in nature: they describe what
needs to be done rather than how it is done. Building a C or C++
library is a simple matter of creating an
Abuild.mk file that describes what the names
of the library targets are and what each library's sources are,
and then tells abuild to build the targets using the C and C++
rules. Here is this library's Abuild.mk
file:
basic/cxx-library/Abuild.mk
TARGETS_lib := basic-library SRCS_lib_basic-library := BasicLibrary.cc RULES := ccxx
The string ccxx as the value of the
RULES variable indicates that this is C or C++
code (“c” or “cxx”). In order for
abuild to actually build this item, we also need to create an
Abuild.conf file for it. The existence of
this file is what makes this into a build item. We present the
file here:
basic/cxx-library/Abuild.conf
this: cxx-library platform-types: native parent-dir: ..
In this file, the this key is used to
specify the name of the build item, the
parent-dir key is used to connect this build
item into the build tree, and the
platform-types key is used to help abuild
figure out on which platforms it should attempt to build this
item. Finally, we want this build item to be able to make the
resulting library and header file available to other build items.
This is done in its Abuild.interface file:
basic/cxx-library/Abuild.interface
INCLUDES = . LIBDIRS = $(ABUILD_OUTPUT_DIR) LIBS = basic-library
This tells abuild to add the directory containing this file to
the include path, the output directory in which the generated
targets were created to the library path, and the
basic-library library to the list of
libraries to be linked with. Notice that the name of the library
assigned to the LIBS variable is the same as
the value assigned to the TARGETS_lib variable
in the Abuild.mk file, and that the
abuild-provided variable
$(ABUILD_OUTPUT_DIR) is used as the library
directory.
To build this item, you would run the command
abuild in the
basic/cxx-library directory. Abuild would
create an output directory whose name would start with
abuild- and be based on the platform or
platforms on which abuild was building this item. This is the
directory to which the variable
$(ABUILD_OUTPUT_DIR) refers in the
Abuild.interface file.
There is a lot of capability hiding beneath the surface here and quite a bit of flexibility in the exact way in which this can be done, but this is the basic pattern you will observe for the majority of C and C++ library build items.
The directory basic/cxx-program contains a
simple C++ program. This program links against the library
created in our previous example. Here is the main body of our
program:
basic/cxx-program/program.cc
#include <BasicLibrary.hh>
int main()
{
BasicLibrary b(5);
b.hello();
return 0;
}
This program includes the BasicLibrary.hh
header file from the cxx-library build
item. Here is the Abuild.mk for this build
item:
basic/cxx-program/Abuild.mk
TARGETS_bin := cxx-program SRCS_bin_cxx-program := program.cc RULES := ccxx
Notice that this is very similar to the
Abuild.mk from the library build item. The
only real difference is that the TARGETS and
SRCS variables contain the word
bin instead of lib. This
tells abuild that these are executable targets rather than
library targets. Notice the conspicuous lack of any references to
the library build item or the location of the headers or libraries
that it makes available. A principle feature of abuild is that
this program build item does not need to know that information.
Instead, it merely declares a dependency on the
cxx-library build item by name. This is
done in its Abuild.conf:
basic/cxx-program/Abuild.conf
this: cxx-program platform-types: native parent-dir: .. deps: cxx-library
Notice the addition of the deps key in this
file. This tells abuild that our program build item
depends on the library build item. When
abuild sees this, it automatically makes all the information in
cxx-library's
Abuild.interface available to
cxx-program's build, alleviating the need
for the cxx-program build item to know the
locations of these files. This will also tell abuild that
cxx-library must be built before we can
build cxx-program.
To build this item, we could just run the
abuild command as we did for
cxx-library, but this would only work
properly if cxx-library were already built.
A better way to build this item would to go to the
cxx-program directory and run abuild
--with-deps. The --with-deps option to
abuild tells abuild to build all build items that this item
depends on before building this item itself. Using the
--with-deps command line option, you can start a
build from any build item and let abuild automatically take care
of building all of its dependencies in the correct order. (For a
complete list of all of abuild's command line options, please
see Chapter 11, Command-Line Reference.)
The output of running abuild --with-deps in the
cxx-program directory when starting from a
clean build is shown below. Your actual output will differ
slightly from this. In particular, the output below has the
string --topdir-- in place of the path to
doc/example, and the string
<native> in place of your native
platform.
[7]
Notice that abuild builds
cxx-library first and then
cxx-program:
example.basic-cxx-program.out
abuild: cxx-library (abuild-<native>): all make: Entering directory `--topdir--/basic/cxx-library/abuild-<native>' Compiling ../BasicLibrary.cc as C++ Creating basic-library library make: Leaving directory `--topdir--/basic/cxx-library/abuild-<native>' abuild: cxx-program (abuild-<native>): all make: Entering directory `--topdir--/basic/cxx-program/abuild-<native>' Compiling ../program.cc as C++ Creating cxx-program executable make: Leaving directory `--topdir--/basic/cxx-program/abuild-<native>'
To remove all of the files that abuild created in any build item's directory, you can run abuild clean in that directory. To clean everything in the build tree, run abuild --clean=all. More details of how to specify what to build and what to clean can be found in Chapter 8, Telling Abuild What to Build.
Note that abuild's Java support is considered alpha as of version 1.0. Version 1.1 of abuild may include a Java solution that is not backward compatible with the solution in version 1.0.
In our next example, we'll demonstrate how to build a simple Java library. Abuild has two different ways to build Java code: a property-driven method, and a build.xml-driven method. The property-driven method of building Java code is truer to abuild's philosophy of using declarative build files but has proven to be inadequate for some of the more complex Java build problems that arise in an enterprise environment. It is hoped that a future version of abuild will provide a more complete answer to the Java build problem, perhaps by integrating with a Java build engine other than Apache Ant. In the mean time, we will demonstrate the property-driven approach to doing Java builds with abuild using this library example. For an example of the build.xml-based approach, see Section 19.3, “Build.xml-driven Java Example”.
You will find the Java example in
basic/java-library. The files here are
analogous to those in our C++ library example. First, here is a
Java implementation of our BasicLibrary
class:
basic/java-library/src/java/com/example/basic/BasicLibrary.java
package com.example.basic;
public class BasicLibrary
{
private int n;
public BasicLibrary(int n)
{
this.n = n;
}
public void hello()
{
System.out.println("Hello. This is BasicLibrary(" + n + ").");
}
}
Next, look at Abuild.conf:
basic/java-library/Abuild.conf
this: java-library platform-types: java parent-dir: ..
This is essentially identical to our C++ library except that the
platform-types key has the value
java instead of the value
native. This is always true for Java build
items. Next, we'll look at the
Abuild-ant.properties file:
basic/java-library/Abuild-ant.properties
abuild.jar-name = java-library.jar
Property-driven Java build items have this file instead of
Abuild.mk. The only thing we have to set
here is the property abuild.jar-name. For
Java build items, we don't explicitly list the source files.
Instead abuild automatically finds sources in the
src/java directory. There are more
properties that can be set, and there are other ways to get files
into the JAR file. We provide detailed information about the
directory structure for property-driven Java builds in Section 16.2, “Directory Structure For Java Builds”. Finally,
look at the Abuild.interface file. This
file provides information to other build items about what they
should add to their classpaths in order to make use of the JAR
file created by this build item:
basic/java-library/Abuild.interface
abuild.classpath = $(ABUILD_OUTPUT_DIR)/dist/java-library.jar
As with the C++ library, it is possible to build this item by
running abuild from the
basic/java-library directory. Notice that
abuild puts the JAR file in the dist
subdirectory of the abuild output directory.
Note that abuild's Java support is considered alpha as of version 1.0. Version 1.1 of abuild may include a Java solution that is not backward compatible with the solution in version 1.0.
In Java, there is no deep distinction between a
“library” and a “program” except that a
JAR file that provides a program must have a
main method. If a JAR file contains a main
method, it can be executed, though it can also be used as a
library. A JAR file can also contain a manifest
file that identifies a class that contains a
main method. Starting with version 1.0.1,
abuild adds the Main-Class attribute to
the manifest file when the abuild.main-class
property is set in the
Abuild-ant.properties.
Here are the relevant files for the program example:
basic/java-program/src/java/com/example/basic/BasicProgram.java
package com.example.basic;
import com.example.basic.BasicLibrary;
public class BasicProgram
{
public static void main(String[] args)
{
BasicLibrary l = new BasicLibrary(10);
l.hello();
}
};
basic/java-program/Abuild.conf
this: java-program platform-types: java parent-dir: .. deps: java-library
basic/java-program/Abuild-ant.properties
abuild.jar-name = java-program.jar abuild.main-class = com.example.basic.BasicProgram abuild.wrapper-name = java-program
Note the addition of the abuild.main-class and
abuild.wrapper-name properties. If these are
both set, abuild will create a wrapper executable (script file
and/or batch file as appropriate) so that you can run the
resulting program with the correct classpath. The wrapper
programs are placed directly in the abuild output directory.
Here is the output of running abuild
--with-deps in this directory. As in the C++ program
example, the output has been modified slightly: in addition to
the --topdir-- substitution, we have also
filtered out time stamps and other strings that could potentially
differ between platforms:
example.basic-java-program.out
abuild: java-library (abuild-java): all
Buildfile: --abuild.xml--
init:
[mkdir] Created dir: --topdir--/basic/java-library/abuild-java/empty
[mkdir] Created dir: --topdir--/basic/java-library/abuild-java/classes
[mkdir] Created dir: --topdir--/basic/java-library/abuild-java/dist
[mkdir] Created dir: --topdir--/basic/java-library/abuild-java/src/java
[mkdir] Created dir: --topdir--/basic/java-library/abuild-java/src/r\
\esources
[mkdir] Created dir: --topdir--/basic/java-library/abuild-java/src/web
[mkdir] Created dir: --topdir--/basic/java-library/abuild-java/src/conf
-do-compile:
[javac] Compiling 1 source file to --topdir--/basic/java-library/abu\
\ild-java/classes
-jar-without-main-class:
[jar] Building jar: --topdir--/basic/java-library/abuild-java/dist\
\/java-library.jar
BUILD SUCCESSFUL
Total time: <time>
abuild: java-program (abuild-java): all
Buildfile: --abuild.xml--
init:
[mkdir] Created dir: --topdir--/basic/java-program/abuild-java/empty
[mkdir] Created dir: --topdir--/basic/java-program/abuild-java/classes
[mkdir] Created dir: --topdir--/basic/java-program/abuild-java/dist
[mkdir] Created dir: --topdir--/basic/java-program/abuild-java/src/java
[mkdir] Created dir: --topdir--/basic/java-program/abuild-java/src/r\
\esources
[mkdir] Created dir: --topdir--/basic/java-program/abuild-java/src/web
[mkdir] Created dir: --topdir--/basic/java-program/abuild-java/src/conf
-do-compile:
[javac] Compiling 1 source file to --topdir--/basic/java-program/abu\
\ild-java/classes
-jar-with-main-class:
[jar] Building jar: --topdir--/basic/java-program/abuild-java/dist\
\/java-program.jar
BUILD SUCCESSFUL
Total time: <time>
[7] All example output in this document is normalized this way since it all comes directly from abuild's test suite. Testing all the examples in the test suite guarantees the accuracy of the examples and ensures that they work as advertised on all platforms for which abuild is released. Should you wish to study abuild's test suite with the examples, be aware that the bold italicized text preceding each block of example output is the name of the expected output file from the test suite.
In this part of the manual, we discuss the standard features of abuild. For most ordinary build problems, these chapters provide all the information you will need. A few advanced topics are presented here. Where appropriate, they include cross references to later parts of the document where functionality is covered in more depth. By the end of this part, you should have a reasonably complete understanding of the structure of abuild's build trees, and a fairly complete picture of abuild's overall functionality. You will know enough about abuild to be able to use it for tasks of moderate complexity.
Table of Contents
Table of Contents
Now that we've had a chance to see abuild in action for a simple case, it's time to go into more detail about how things fit together. In Section 3.2, “Basic Terminology”, we briefly defined the terms build item and build tree. In this chapter, we will describe them in bit more detail and briefly introduce a number of concepts that apply to them.
A precise definition of build item would
state that a build item is any directory that contains an
Abuild.conf. Perhaps a more useful
definition would say that a build item is the basic object that
participates in abuild's object-oriented view of a software
build. A build item provides some service
within a build tree. Most build items build some kind of code:
usually a library, executable, or Java archive. Build items may
provide other kinds of services as well. For example, a build
item may implement a code generator, support for a new compiler,
or the ability to make use of a third-party software library. In
addition, a build item may have certain attributes including a
list of dependencies, a list of
supported flags, information about what
types of platforms the build item may be built on, a list of
traits, and other non-dependency
relationships to other build items. Each of these concepts is
explored in more depth later in the document.
All build items that provide a service are required to have a name. Build item names must be unique within their build tree and all other build trees accessible to their build tree since the build item name is how abuild addresses a build item. Build item names consist of period-separated segments. Each segment may contain mixed case alphanumeric characters, underscores, and dashes. Build item names are case-sensitive.
The primary mechanism for describing build items is the
Abuild.conf file. This file consists of
colon-separated key/value pairs. A complete description of the
Abuild.conf file may be found in Chapter 13, The Abuild.conf File. In the mean time, we will
introduce keys as they become relevant to our discussion.
Although every build item has an Abuild.conf
file, there are various other files that a build item may have.
We defer a complete list and detailed discussion these files for
later in the document, but we touch briefly upon a few of the
common ones here.
Abuild.confThis is the most basic of the build item files, and it is the only file that must be present for every build item. We sometimes refer to this as the build item configuration file.
Abuild.mk, Abuild-ant.properties, Abuild-ant.xml
These are the files that direct abuild what to actually
build in a given build item. Each build file is associated
with a specific backend. Exactly one of these files must be
present in order for abuild to attempt to build a build
item. As such, these files are known as build
files. When we say that a build item has or does
not have a build file, we are specifically talking about one
of these files. In particular, it is important to note that
Abuild.conf and
Abuild.interface are not considered build
files.
Abuild.interface
The Abuild.interface file is present for
every build item that wants to make some product of its build
accessible to other build items. We refer to this as the
build item's interface file. There has
been some confusion among some abuild users about the term
interface. Please understand that
abuild interfaces are distinct from Java interfaces, C++
header files, and so forth, though they serve essentially the
same function. If you view a build item as an object, the
abuild interface contains information about what services
that object provides. It exposes the interfaces through which
other build items will access a given build item's products.
A build tree, as defined before, is a collection of build items
arranged hierarchically in the file system. A build tree is
formed as a result of the items it contains holding references to
the locations of their parents and children within the file
system hierarchy. These locations are named as relative paths in
the parent-dir and
child-dirs keys of the items'
Abuild.conf files. Ordinarily, the value of
the parent-dir key is
“..”, but it doesn't have to be.
It is also customary to have the value of
child-dirs contain single path elements
(i.e.just a file without a directory), but
this is also not a hard restriction. When abuild starts up, it
looks for an Abuild.conf in the current
directory and looks for its parent-dir key.
Using that, it locates the parent build item's
Abuild.conf file and reads it for its
parent-dir key. This process is repeated
until abuild discovers a build item that does not have a
parent-dir key. This build item is known as
the root build item of the build tree.
Note that the hierarchy defined by the layout of build items in the file system is a file system hierarchy and nothing more. It doesn't have to have any bearing at all on the dependency relationships among the build items. [8] That said, it is sensible to organize build items in a manner that relates to the architecture of the system, and this in turn usually has implications about dependencies. Still, it is important to keep in mind that abuild is not file-system driven but rather is dependency driven.
In addition to containing build items, build trees can contain
other attributes. Among these are references to other build
trees, a list of supported traits, and a
list of plugins. We will discuss these
topics later in the document. Most of a build tree's attributes
appear in the root build item's Abuild.conf
file.
In further describing build items and their attributes, it is useful to classify build items into several types. Most build items serve the purpose of providing code to be compiled. There are a number of special types of build items that serve other purposes. We discuss these here:
The root build item of a build tree is the item in that tree
that has no parent-dir key. It is often
the case that the root build item is unnamed and therefore
lacks a this key. (See below for a
discussion of unnamed build items.) Keys that define
attributes of the build tree may appear only in the root
build item's Abuild.conf.
In order to refer to one build item from another, both build
items must have names. Abuild requires that every named
build item in a build tree be named uniquely within that
tree. A name is given to a build item by setting the
this key in its
Abuild.conf.
[9]
Sometimes, a build item exists for the sole purpose of
bridging its parent with its children in the file system.
Such items do not need to be referenced by other build items,
so they do not need names. The only use of an unnamed build
item is to serve as an intermediary during traversal of the
file system. Such a build item's
Abuild.conf may only contain the
parent-dir and
child-dirs keys. Abuild doesn't
retain any information about these build items. It simply
traverses through them when locating build items at startup
time.
Interface-only build items are build items that contain (in
addition to Abuild.conf) an
Abuild.interface file. They do not
build anything and therefore do not contain build files (such
as Abuild.mk or
Abuild-ant.properties). Since they have
nothing to build, abuild never actually invokes a backend
on them. They are, however, included in all dependency and
integrity checks. A typical use of interface-only build
items would be to add the locations of external libraries to
the include and library paths (or to the classpaths for Java
items). There may also be some interface-only build items
that consist solely of static files (templated C++ classes,
lists of constants, etc.).
Pass-through build items are useful for solving some more advanced abuild problems. They contain no build or interface files, but they are named and have dependencies. This makes pass-through build items useful as top-level facades for hiding more complicated build item structures. This could include build items that have private names relative to the pass-through item, and it could also include structures containing build items that cross language and platform boundaries. Several examples in the documentation use pass-through build items to hide private build item names. For further discussion of using pass-through build items in a cross-platform environment, please see Section 21.2, “Cross-Platform Dependencies”.
Plugins are capable of extending the functionality of abuild beyond what can be accomplished in regular build items. Plugins must be named and not have any dependencies. No other build items may depend on them. Plugins are a topic in their own right. They are discussed in depth in Chapter 26, Enhancing Abuild with Plugins.
Virtually every software development project has some need to
integrate with third-party software libraries. In a traditional
build system, you might list the include paths, libraries, and
library directories right in your Makefile
or configuration file for whatever build system you are using.
With abuild, the best way to integrate with a third-party
library is to use a build item whose sole purpose is to export
that library's information using an
Abuild.interface file. In the simplest
cases, a third-party library build item might be an interface
only build item (described above) that just includes the
appropriate library directives in a static
Abuild.interface file. For example, a build
item that provides access to the PCRE (Perl-compatible regular
expression) libraries on a Linux distribution that has them
installed in the system's standard include path might just
include an Abuild.interface with the
following contents:
LIBS = pcrecpp pcre
For Java build items, a third-party JAR build item would
typically append the path to the JAR file to the
abuild.classpath.external interface variable.
Sometimes, the process may be more involved. For example, on a
UNIX system, it is often desirable to use
autoconf to determine what interface
is required for a particular library. We present an example of
using autoconf with abuild in Section 15.3, “Autoconf Example”. Still other libraries may use
pkg-config. For those libraries, it
may make sense to create a simple set of build rules that
automatically generate an Abuild.interface
after-build file (also discussed in Section 15.3, “Autoconf Example”) by running the
pkg-config command.
[10]
Whichever way you do it for a given package, the idea is that you should always create a build item whose job it is to provide the glue between abuild and the third-party library. Other build items that need to use the third-party library can then just declare a dependency on the build item that provides the third-party library's interface. This simplifies the process of using third-party libraries and makes it possible to create a uniform standard for doing so within any specific abuild build tree. It also alleviates the need to duplicate information about the third-party library throughout your source tree.
[8] This information is used only at startup to locate the root of the build tree and to find the locations of all of the build items in the tree so that they may be later referred to only by name. In fact, abuild doesn't even retain the information about the parents and children of build items in the file system after it uses the information during startup.
[9] Perhaps name would have been a better name for this key, but it's too late to change it now. This key might be renamed in a future major release of abuild.
[10]
An example pkg-config build item may
be found in the abuild-contrib package
available at abuild's web
site.
Abuild was designed with multiplatform operation in mind from the beginning. Up to this point, we have largely glossed over how abuild deals with multiple platforms. In this chapter, we will cover this aspect of abuild's operation in detail.
Abuild classifies platforms into a three-level hierarchy. The three levels are described by the following terms:
A target type encompasses the overall kind of targets that are being built. A target type essentially encapsulates a build paradigm. Abuild understands three target types: platform-independent for truly platform-independent products like scripts and documentation, object-code for compiled object code like C and C++, and java for Java byte code and related products. One could argue that Java code is platform-independent, but since Java code has its own build paradigm, abuild considers it to be a separate target type. Be careful not to confuse target type with target, defined in Section 3.2, “Basic Terminology”.
A platform type essentially defines a grouping of platforms. Platform types belong to target types and contain platforms. When configuring build items, developers assign build items to platform types rather than to platforms or target types. The platform-independent target type has only platform type: indep. The java target type has only one platform type: java. [