18.2. Make Rules

The following sections describe the make-based rule sets provided by abuild.

18.2.1. C and C++: ccxx Rules

Rules for compiling C and C++ code are provided by the ccxx rules. These rules also include support for flex, bison, and Sun RPC. It is possible for a single build item to build multiple targets including any mixture of static library, shared library, and executable targets.

A note about flex and bison before we get to the main event: the flex and bison rules can take advantage of abuild's codegen-wrapper utility. If you set the variable FLEX_CACHE, abuild will cache generated flex output files and input file checksums making it possible for your flex code to be used on systems that don't have flex. The variable BISON_CACHE serves the same function for code generated with bison. Abuild's own build uses this functionality. To use this facility, set FLEX_CACHE and/or BISON_CACHE to directories relative to our source directory. Abuild will copy files to or from this directory during its build. These directories are relative to your source directory, not your output directory. As such, the resulting files are likely to be controlled in your version control system. This is an exception to the ordinary rule of abuild not creating files outside of the output directory, but it's an appropriate exception as the intention is to control these automatically generated files so that they could be available for users who didn't have flex or bison. For information about using the codegen-wrapper utility with your own builds, see Section 22.6, “Caching Generated Files”.

When building C and C++ code, you must define at least one of TARGETS_lib or TARGETS_bin. These variables contain a list of library and executable targets respectively. Targets should be specified without any operating system-specific prefixes or suffixes. For example, the library target moo might generate libmoo.a on a UNIX system or moo.lib on a Windows system. Likewise, the executable target quack might generate quack on a UNIX system and quack.exe on a Windows system.

For each target target listed in TARGETS_lib, you must define the variable SRCS_lib_target to contain a list of source files used to build the library. Likewise, for each binary target in TARGETS_bin, you must define SRCS_bin_target. These variables can contain any mixture of C and C++ files. The source files listed in these variables are typically located in the same directory as the Abuild.mk, but they may also refer to automatically source files that will actually appear in the output directory. [32] There are variables that can be used to control the creation of shared libraries. For details, see Section 21.1, “Building Shared Libraries”. Files whose names end with .c are treated as C code. Files whose names end with either .cc or .cpp are considered to be C++ code. Although you can have any mixture of binary and library targets in a build item, no single source file should be listed in more than one target. Additionally, abuild will automatically include any library targets at the beginning of the library list when linking any binary targets in the build item. All targets are created directly in the abuild output directory.

In addition to the standard targets, the ccxx rules provide a special target ccxx_debug. This target prints the current include and library path as well as the list of libraries that we are linking against. This can be a useful debugging tool for solving dependency declaration problems.

It is also possible to add additional preprocessor, compiler, or linker flags globally or on a per-file basis and to specifically override debug, optimization, or warning flags globally or on a per-file basis. This is done by setting the values of certain make variables, some of which may also be set in Abuild.interface. Details about these variables may be obtained by running abuild rules-help from any C/C++ build item. The following variables are available:


additional flags passed to the preprocessor, C compiler, and C++ compiler (but not the linker)


additional flags passed to the C compiler, C++ compiler, and linker


additional flags passed to the C++ compiler and linker


additional flags passed to the linker—usually not used for libraries


debug flags passed to the processor, compilers, and linker


optimization flags passed to the processor, compilers, and linker


warning flags passed to the processor, compilers, and linker

Note that the XCPPFLAGS, XCFLAGS, XCXXFLAGS, and XLINKFLAGS variables may be set in Abuild.interface as well. Therefore, although you assign to them normally with = in Abuild.interface, when assigning to them in Abuild.mk, it is generally better to append to these variables (using +=) rather than to set them outright. Also, keep in mind that flags are often compiler-specific. It may often make sense to set certain flags conditionally upon the value of the $(ABUILD_PLATFORM_COMPILER) variable or other platform field variables. This can be done using regular GNU Make conditional syntax.

Each of the above variables also has a file-specific version. For the X*FLAGS variables, the file-specific values are added to the general values. For example, setting XCPPFLAGS_File.cc will cause the value of that variable to be added to the preprocessor, C compiler and C++ compiler invocations for File.cc. File-specific versions of XCPPFLAGS, XCFLAGS, and XCXXFLAGS are used only for compilation and, if appropriate, preprocessing of those specific files. They are not used at link time.

The file-specific versions of DFLAGS, OFLAGS, and WFLAGS override the default values rather than supplementing them. This makes it possible to completely change debugging flags, optimization flags, or warning flags for specific source files. For example, if Hardware.cc absolutely cannot be compiled with any optimization, you could set OFLAGS_Hardware.cc to the empty string to suppress optimization on that file regardless of the value of OFLAGS. Similarly, if autogen.c were an automatically generated file with lots of warnings, you could explicitly set WFLAGS_autogen.c to the empty string or to a flag that suppresses warnings. This would suppress warnings for that file without affecting other files. If you wish to append to the default flags instead of replacing them, include the regular variable name in the value, as in WFLAGS_File.cc := $(WFLAGS) -Wextra or even WFLAGS_File.cc := $(filter-out -Wall,$(WFLAGS)).

The ccxx rules provide a mechanism for you to generate preprocessed output for any C or C++ file. For file.c, file.cc, or file.cpp, run abuild file.i. This will generate file.i in the output directory. Its contents will be the output of running the preprocessor over the specified source file with all the same flags that would be used during actual compilation. [33]

When invoking abuild to build C or C++ executables or shared libraries, it is possible to set the make variable LINKWRAPPER to the name of a program that should wrap the link command. This makes it possible to use programs such as Purify or Quantify that wrap the link step in this fashion.

Ordinarily, abuild uses a C++ compiler or linker to link all executables and shared libraries. If you are writing straight C code that doesn't make any calls to C++ functions including those in external libraries and you want to link your program as a C program to avoid runtime dependencies on the C++ standard libraries, set the variable LINK_AS_C to some non-empty value in your Abuild.mk. This applies to all shared libraries and executables in the build item.

Most of the time, abuild manages all the dependencies of the source and object files (as opposed to inter-build-item dependencies) automatically, but there are some rare instances in which you may have to create such dependencies on your own, such as when an object file depends on an automatically generate header file that is generated in the same build item. For an example of this, see Section 22.5, “Dependency on a Make Variable”. To make it possible to express such dependencies in a portable fashion, the ccxx rules provide the variables LOBJ and OBJ which are set to the object file suffixes for library object files and non-library object files respectively. For example, if you have a source file called File.cc that is part of a library, the name of the object file will be File.$(LOBJ), and the file will be created inside the abuild output directory. If File.cc were part of an executable instead, the object file would be File.$(OBJ) instead. [34]

As is the case for any rule set, you can run abuild --help rules rule:ccxx for additional information. This help text is also included in Section E.10, “abuild --help rules rule:ccxx.

There is a lot more to abuild's C and C++ generation than is discussed here. For a complete understanding of how it works, you are encouraged to read rules/object-code/ccxx.mk in the abuild distribution (Appendix I, The ccxx.mk File). There you will find copious comments and a lot of pretty hairy GNU Make code.

18.2.2. Options for the msvc Compiler

Abuild includes built-in support for Microsoft's Visual C++ compiler on Windows. There are three MSVC-specific variables that can be set:

  • MSVC_RUNTIME_FLAGS: set to /MD by default, which causes the executable to dependent on Microsoft runtime DLLs. By setting this to /MT, it is possible to create executables that statically link with the runtime environment. A trailing “d” is automatically appended when building debugging executables or libraries.

  • MSVC_MANAGEMENT_FLAGS: set to /EHsc by default, which enables synchronous exception handling and assumes “C” functions do not throw exceptions. By setting this to /clr, it is possible to build programs that work with the .NET framework.

  • MSVC_GLOBAL_FLAGS: contains flags that are passed globally to all compilation commands. Users will seldom have to modify this. For details, see comments in make/toolchains/msvc.mk.

18.2.3. Autoconf: autoconf Rules

The autoconf rules provide rules for including autoconf fragments for a build item. [35] Rather than having a monolithic autoconf-based component in a source tree, it is recommended that individual build items use autoconf for only those things they need. This reduces the likelihood that something may fail to build due to lack of support for something it doesn't need (but that is checked for by a monolithic autoconf component). The only caveat to doing this is that, if you use autoconf-generated header files, you may find that the same symbols are defined in more than one place. You will have to experiment and come up with appropriate standards for your project.

The autoconf rules don't supply any special targets. A reasonably complete example of using autoconf follows in Section 18.3, “Autoconf Example”. You may also run abuild --help rules rule:autoconf for full information on using these rules. This help text is also included in Section E.9, “abuild --help rules rule:autoconf.

Some of the tools run by autoconf create temporary files that may cause problems when running parallel builds. It is therefore recommended that you place attributes: serial in the Abuild.conf file of build items that use autoconf rules.

Autoconf properly honors your C/C++ toolchain and runs configure with the proper C/C++ compilation environment defined. The usual approach for autoconf-based build items is that, if make variables need to be defined based on the results of running configure, configure.ac generates a file called autoconf.interface which is specified as an after-build file in Abuild.interface. This means that the autoconf-based build item itself may not include code that is conditional upon the results of running autoconf. It is okay, however, for build items that depend on an autoconf-based build item to include conditional code in their Abuild.interface and Abuild.mk files based on variables defined in its autoconf.interface should this be required.

18.2.4. Do Nothing: empty Rules

In some rare cases, it may be desirable to create an Abuild.mk file that does nothing. One reason for doing this would be if you had a library that contained some code that should only exist on certain platforms. You might want to create an Abuild.mk file that was conditional upon some value of the ABUILD_PLATFORM_OS variable, for example. Since abuild requires that you set at least one of RULES or LOCAL_RULES, you can set the RULES variable to the value empty. Abuild will still attempt to build the item in this case, but the build will not do anything. The empty rule set is available for build items of any target type.

[32] For example, if you have a local rule that generates autogen.cc in the output directory, you can simply list autogen.cc in one of your SRCS variables, and abuild will find it anyway. This is because abuild's make code uses GNU Make's vpath feature. We provide an example of this construct in Section 22.2, “Code Generator Example for Make”.

[33] The .i suffix is a traditional UNIX suffix for preprocessed C code and was created as an intermediate file by some compilers. GCC recognizes this as preprocessed C code and also recognizes .ii as a suffix for preprocessed C++ code. When abuild is given a .i file as a suffix, its make rules use a pattern-based rule to run the preprocessor over the file, it never uses the resulting files as input to the compiler. Abuild uses the original suffix of the file (.c, .cc, or .cpp) to determine whether the file is a C or C++ source file and does not therefore need to distinguish between .i and .ii.

[34] LOBJ and OBJ usually have the same value as each other, and the value is usually “o” on UNIX systems and “obj” on Windows systems. However, there are some circumstances under which either of these conditions may not be true, so it is best to use LOBJ or OBJ explicitly as required.

[35] Autoconf is a package used to help software developers create portable code. This section assumes some familiarity with autoconf.