Autobuild FAQ

Questions

Answers

1.1 Why cook?

Short answer: cook is a better make. It has crucial features allowing the implementation of a reliable incremental autodiscovering build system.

Long answer: GNU make is a tool which shoehorned many useful features into bizarre constructs, mainly for maintaining backwards compatibility. The bizarre constructs render Makefiles hard to read and maintain.

The following features of cook, not available in GNU make, enable the construction of a reliable incremental parallel autodiscovering build system:

1.2 How do I add a new project?

  1. Find a location in the source tree, create a directory.

  2. Decide what gets built in that directory:

    • a set of small programs (simplex)
    • one large program (complex)
    • a static library (archive)
    • an ordinary shared object (dumb_so)
    • a dynamically loadable shared object (smart_so)
    • a set of pre-compiled perl scripts (perl)
    • a set of validated xml files (xml)
    • a set of preprocessed shell scripts (sh)
    • a set of preprocessed NT batch files (bat)
    • rpms (rpm)
    • a maven project (maven)
    • something using its own build system (sub_build)
    • a one-off build via cook (custom)
  3. Add a Howto.cook file with at minimum one line containing:

            make something;
    
    where something is one of the things listed above. For example:
            make complex;
    

  4. Add additional customizations as needed (see Tutorial).

  5. Add code.

  6. Run b.

1.3 How do I add third party C/C++ libraries?

Assuming you have the source tree laid out as described in the tutorial, proceed as follows:

If you have a binary distribution:

  1. Pack the binary distro into a tarball. Ensure that the tarball unpacks into a directory with the same basename. In other words, ensure that foo-2.7.tar.gz unpacks into foo-2.7.

  2. Check the tarball into the 3party/platform directory.

  3. Go to cook-install-prefixshare/cook/autobuild/include/use and clone an existing third party product, for example jdk1.5.0_09.

    If foo-2.7 is a C++ library with some header files, it is likely to have the following structure:

        foo-2.7/
           |_include/
           |   |_foo.h
           |_lib/
               |_libfoo.a
    

    The use file for foo-2.7 will have at least the following content:

    /* simplify the expressions to follow - not used elsewhere */
    foo-2.7_dir = [thirdparty]/[variant]/[unix]/foo-2.7;
    
    /* cause retrieval and unpacking of tarball */
    foo-2.7_use_unpack = [foo-2.7_dir]/[dummy];
    
    /* cause rebuilds for users of this file, if it changes */
    foo-2.7_use_dependency = ADD [__FILE__]':'[__LINE__] [__FILE__];
    
    /* compiler flags */
    foo-2.7_cc_I_flags = ADD [__FILE__]':'[__LINE__] -I[foo-2.7_dir]/include;
    foo-2.7_cpp_I_flags = ADD [__FILE__]':'[__LINE__] -I[foo-2.7_dir]/include;
    
    /* link dependency */
    foo-2.7_external_lib = ADD [__FILE__]':'[__LINE__] [foo-2.7_dir]/lib/libfoo.a;
    
    /* the ADD [__FILE__]':'[__LINE__] documents
     * the location of the definitions
     */
    
  4. Edit the Howto.cook files of the projects using foo-2.7, adding a line:

          use foo-2.7;
    
  5. Run b and see if it works.

If you have a source distribution only, you have two options:

The best procedure for accomplishing the latter is to create a separate vendor branch for the source distro, then merge the vendor branch into your development tree.

The idea is to have a ready-made location to merge upgrades from the vendor with your own. If the vendor chooses to reorg their source tree, you are kind of hosed, but you may still have a shot at merging it if the reorg is small. Most source trees change rarely, so it's still a good process.

1.4 How can I accelerate build turnaround?

You can use b -par=n to increase the number of build threads. Note that not all portions of the build are completely parallelizable, but most are.

You can run b in a subdirectory, thereby only building the targets therein and their prerequisits.

You can run b skip in a subdirectory, thereby skipping any prerequisits outside the subdirectory. Note that this requires to have done a complete build before, and you need to be sure no dependencies have changed.

Every build step creates a convenience symlink called last_command to the last build script executed in any directory. If you encounter compiler errors in a directory, it may be quickest to just rerun ./last_command until the compilation succeeds.

1.5 Can I run multiple concurrent builds on the same box?

Short answer: No.

Long answer: There is resource contention:

maven
local repo is a shared resource
rpm
transaction lock file is a shared resource
b
the error collection files and the build log files could be clobbered.

1.6 How do I troubleshoot compile problems?

Every build step in b generates a target file and the following additional files:

target.need
Direct prerequisites
target.why
Prerequisites which were out of date
target.how.sh
Shell script to generate target.
target.errors
Output produced by running target.how.sh.

In addition, every failure generates an entry in the file DO/Errors-, which lists all the targets which failed to build.

Running b will also generate a file named Howto.list, which is a copy of the screen output. Use this to examine errors which scrolled off the screen.

The b output takes the following format:

    Operation: sources -> (x)
           (x) -> target

The first line is displayed when construction of the target begins, the second line is displayed when the target is done. The (x) denotes the thread id, thereby permitting correct interpretation of the interleaved output of a parallel build.

Build errors are displayed by first dumping the contents of the target.errors file, then the following output:

### (x) -> Errors detected: target.errors
### (x) -> Command run:     target.run.sh

The paths are always relative to the top of the source tree. You can easily reproduce the error by executing target.run.sh. You can also re-examine the error messages by inspecting target.errors.

The build script target.run.sh will have comments indicating the origin of various settings used in the build.

1.7 How do I troubleshoot "cook undefined variable" problems?

If the undefined variable contains a path, it is usually an indication that someone either introduced an error into the Howto.cook file in the location implied by the path, or that it is a new Howto.cook file which someone forgot to add to version control.

The way b stores localized settings is by using variables named with the path to the directory where that setting applies. The end of the path will often have the variant (debug or optimized) appended to it. This last entry should be ignored when determining the location

1.8 How can I exclude portions of the source tree from b?

If you wish to totally exclude a portion of the source tree, place a file named .do_not_cook in the directory to be excluded.

If you wish to exclude a portion of the source tree for specific variants or platforms, use the exclude-platform list; or the exclude-variant list; constructs.

1.9 How does the environment affect the build?

Setting any of the following environment variables may cause build problems:

 JAVA_HOME
 CDPATH
 CLASSPATH
 ANT_HOME
 JAVA5_HOME
 LITMUS_HOME
 JWSDP_HOME
 JBOSS_HOME
 PLATFORM
 BLDOPT
 VARIANT

Most of these are listed in the Howto.cook file at the top of the source tree and will cause b to abort. This list is unfortunately not complete due to the vagaries of various sub-build handoffs.

1.10 What are those [...] variable references used all over the place?

Cook's [...] construct is the equivalent of GNU make's $(...) construct. b will define a a variety of these variables which can be used to express dependencies, compiler options and commands and other things.

Cook's [...] is also used to invoke functions, again somewhat similar to the built-in functions provided by GNU make. Cook makes it very simple to add new functions, making it look somewhat like lisp, whereas GNU make's $(call ...) semantics are rather bizarre.

In b, variables are scoped by directory. This is accomplished by making the relative path in the branched source tree part of the name. When applicable, the variant (debug, optimized, ...) is appended to that path.

The following is an incomplete list of commonly used variables.

    
Globals
[build]
    Purpose:Location of shared cook files
Defined:cook-install-prefix/share/cook/autobuild/include/main
Default:cook-install-prefix/share/cook/autobuild
[default_do]
Purpose:Default DO subdir when invoking b without variant target
Defined:cook-install-prefix/share/cook/autobuild/include/main
Default:[do]/[unix]/[head [supported_variants]]
[do]
Purpose:Name of the DO subdirectory
Defined:cook-install-prefix/share/cook/autobuild/include/main
Default:DO
[indy3party]
Purpose:Location of platform independent third party products
Defined:cook-install-prefix/share/cook/autobuild/include/main
Default:3party/noarch
[pwd]
Purpose:Absolute path to the top of the branched source tree
Defined:cook-install-prefix/share/cook/autobuild/include/main
Default:`pwd`
[supported_platforms]
Purpose:List of known platforms
Defined:Howto.cook
[supported_variants]
Purpose:List of known variants, first one is the default variant
Defined:Howto.cook
[thirdparty]
Purpose:Location of platform dependent third party products
Defined:cook-install-prefix/share/cook/autobuild/include/main
Default:3party/[unix]
[unix]
Purpose:Name of current build platform
Defined:cook-install-prefix/share/cook/autobuild/include/main
Default:default_platform
[XXX_suffix]
Purpose:File name suffixes
Defined:cook-install-prefix/share/cook/autobuild/include/suffixes/[unix]
Various variables named after unix commands
Purpose:Factor out platform specific weirdnesses
Defined:cook-install-prefix/share/cook/autobuild/include/paths/[unix]
Variables representing path and dependency components:
[dirs_in_path/variant]
Purpose:List of directories in path, valid for variant
Defined:DO/[unix]/Whatto.cook
[dirs_under_path/variant]
Purpose:List of directories in path and all subdirectories therein, valid for variant
Defined:DO/[unix]/Whatto.cook
[files_in_path/variant]
Purpose:List of files in path, valid for variant
Defined:DO/[unix]/Whatto.cook
[files_under_path/variant]
Purpose:List of files in path and all subdirectories therein, valid for variant
Defined:DO/[unix]/Whatto.cook
[XXX_files_for_target-dir/variant]
Purpose:List all files of type XXX contributing towards the build of the target defined in target-dir, valid for variant. XXX currently is any one of the following:
as ..........C/C++ assembler files
bat .........Compiled .BAT files
depend ......C/C++ dependency files
header ......Generated header files
lint ........C/C++ lint files
object ......C/C++ object files
pom .........Pom dependency files
perl ........Compiled perl files
rpmbuild ....Target files for building rpms
rpminstall ..Target files for installing rpms
spec ........Compiled spec files
sh ..........Compiled /bin/sh files
source ......Original source files
xml .........Compiled and validated xml files
Defined:cook-install-prefix/share/cook/autobuild/include/main, derived from cook-install-prefix/share/cook/autobuild/include/collectors/*
[build_target_for_path/variant]
Purpose:Path of final build target in path for variant. Use this when defining extra dependencies between top level build targets, for example in make/rpm. Enclose any reference in the following construct to avoid build errors when running "b skip":
[defined-or-warn build_target_for_path/variant]
Defined:cook-install-prefix/share/cook/autobuild/include/main, derived from cook-install-prefix/share/cook/autobuild/include/make/*.
[install_targets_for_path/variant]
Purpose:Path of install target. Use this to express precedence constraints when installing rpms in your source tree. Enclose any reference in the following construct to avoid build errors when running "b skip":
[defined-or-warn build_target_for_path/variant]
Defined:cook-install-prefix/share/cook/autobuild/include/main, derived from cook-install-prefix/share/cook/autobuild/include/make/*
[maven_compile_target_for_groupId.artifactId/variant]
Purpose:Path of target causing "mvn compile" to be run. Use this to insert build rules between the "mvn compile" and the "mvn install" steps.
Defined:[pom_files_for_/variant], constructed from the various pom.xml files.
[maven_install_target_for_groupId.artifactId/variant]
Purpose:Path of target causing "mvn install" to be run. Use this to add rules to post-process a "mvn install" or to include the result in rpms.
Defined:[pom_files_for_/variant], constructed from the various pom.xml files.

1.11 How do I include generated header files?

You check in a "docking header file", like this:


  #ifndef _generated_header_h_
  #define _generated_header_h_

  /* The following macro "stringifies" a constant. This allows constants to be
   * passed in via compiler flags without using fancy quoting mechanisms.
   */

  /* Go ask the ANSI committee why you need two levels of indirection here */
  #ifndef cook_str
  #define cook_str(s)  # s
  #define cook_string(s) cook_str(s)
  #endif

  /* Now include the generated header file */
  #include cook_string(OBJDIR/the_generated_header.h)

  #endif

The code above works when the compiler is invoked with the -DOBJDIR=DO/platform/variant. This setting is defined as a default setting for C/C++ compilers.

This in cause the generated header file to be included from the DO/platform/variant directory.

One unfortunate side effect of this strategy is that any other #define which happens to match platform or variant (or DO for that matter) will be substituted. This may be hard to debug.