<terp.cpp> build process

Build process

Frequently, terp is compared with some flavor of make that had previously been used to manage the build of the C++ sources into a finished product. make is very flexible, portable, and the de-facto standard for building C and C++ software. make excels at managing dependencies and at performing time-saving, partial rebuilds when only one file or a few files have changed.

The terp C++ task supports many of the same features but, as we like to believe, is easier to use and maintain. Let's take a look at the typical flow of a terp build:

  1. Is the forcebuild attribute set to true?
    If yes, all enabled buildsteps are performed without regard to up-to-date status and other checks are skipped.
  2. Has one of the non-dynamic source files been modified since the target files were created or is one of the target files missing?
    If yes, continue with step 3. If no, the C++ build task will not perform any further steps.
  3. Perform <prebuild> steps if there are any.
  4. Perform compilation and link steps if indicated, including compilation of dynamically generated sources.
  5. Perform <postbuild> steps if there are any.

What about dependency analysis?

The terp C++ compiler task can perform two types of dependency analysis:

  1. The analysis of directly specified input files and specified build targets.
  2. The analysis of indirectly referenced input files and specified build targets.

The latter analysis requires a C++ compiler/preprocessor that can be invoked such that it reports include file dependencies in a standard format that can be parsed by the compiler task. The compiler task supports g++ and most other Unix compilers for dependency generation. Normally, it colocates a dependency (.d) file with the object file that is created when a source file is compiled. To enable dependency analysis, you need to set the depends attribute to true; it is disabled by default. When disabled, the compiler task will only perform the first type of dependency analysis.

Why exclude dynamic sources from the dependency analysis?

Let's look at an example first. We generate a C++ source file that declares a function that returns the build version:

<terp.cpp ...
>
    ...
    <dynamicsources>
        <create>
extern "C" __declspec(dllexport) const char * getVersion()
{
    return "";
}
        </create>
    </dynamicsources>
    <sources dir="" includes="*.cpp"/>
</terp.cpp>

We have one dynamic source file that contains the getVersion() method. It is dynamically generated fro mthe template that is provided in the build script. We also have static source files that constitute the bulk of our build.

When should the compiler task perform a rebuild? Definitely when one of the static source files has changed since the last build. But what about the dynamic source file? We interpret dynamic source files as files that are generated unconditionally as long as at least one other build action has to be performed. There's one consequence of that rule that can catch you unaware if you have a compilation task that consists entirely of dynamic sources, for example because you wish to template-expand every source file. If there are only dynamic sources no build action ever takes place unless you set the forcebuild attribute to true.

Imagine the consequences if we were to treat dynamic source files differently. If dynamic source files were taken into account in the dependency analysis, a target with at least one dynamic source file would always have to be rebuilt. This would trigger all <prebuild> and <postbuild> tasks that have been configured. One of the major use cases of the <prebuild> tasks is to preprocess source files with third party utilities (database precompilers, profilers, etc.). This would probably trigger a rebuild of all the static source files, even if nothing has changed.

What if you want to always regenerate the dynamic sources but not force a rebuild for all the static sources? In this case you will not want to use the forcebuild attribute because that would force a rebuild for the static sources as well. You will need to break the compilation up into two steps:

  1. A template expansion / code generation step.
  2. A compilation step that includes the dynamically generated code as static sources.

 


Copyright 2006-2016 by Codemesh, Inc., ALL RIGHTS RESERVED

:
<terp.cpp> - the build process
codemesh.com home expressions templates ant about us contact us download   

Commandline