<terp.cpp> - C++ compilers in ANT

Introduction

The terp C++ ANT task aims to shield you from a lot of the complexity of C++ builds while providing you with simplicity of use. It is not just a C++ compiler invoker where you specify an output directory and a bunch of compiler switches. Just to list some of the advanced and probably quite unexpected features:

  • automatic link generation for versioned libraries (makelinks attribute),
  • dependency-based builds (forcebuild and depends attributes),
  • resource file generation for Windows builds (resource element),
  • dynamic source generation for various tasks (e.g. inclusion of build information in binary target, timestamping),
  • abstract options that map to proper compiler switches,
  • BoundsChecker integration,
  • automatic discovery of compilers.

You can take advantage of all these features while maintaining full control over compiler-specific features that have not been abstracted yet via the nested <option> element that allows you to specify arbitrary compiler switches.

The <terp.cpp> task has several complex features that deserve more detailed documentation. Please follow the respective links if you are interested in

Using the <terp.cpp> task requires that the ant-terp.jar file be available to ANT and that the task be registered via a <taskdef> statement:

<taskdef name="terp.cpp" 
         classname="com.codemesh.terp.ant.compiler.cpp.CppCompilerImpl"/>
<!-- if you're using option set references also define the following --> <typedef name="terp.cpp.options" classname="com.codemesh.terp.ant.compiler.cpp.CppCompilerOptionsImpl"/>

Please note that you could use any value for the name attribute that you wish; you are not limited to terp.cpp. All our examples and documentation use the terp.cpp name and use a name prefix of terp. to signal that they originate with and rely on the terp framework.

The <terp.cpp> task is probably the most complex task in the entire terp framework. It is a compiler- and platform-portable wrapper around common C++ compilers. Its goal is to allow you the high-level specification of a C++ build without having to have detailed knowledge of compiler locations, versions, options, etc.

The <terp.cpp> task cooperates very closely with the built-in CppExecutor types that are used to describe, select, and invoke a C++ compiler from within the terp framework.

In its simplest form, you invoke the C++ compiler task as follows:

<terp.cpp>
    <sources dir="${basedir}" includes="*.cpp"/>
</terp.cpp>

At a minimum, in a real use case, you typically have to

  • specify the C++ compiler to be used.
  • specify directories for temporary and final build results.
  • specify a target name and type.
  • specify some C++ source files to be compiled.

While it is conceptually easy to understand what the above example does, the devil lies in the detail. You are probably asking yourself:

  • What are we building here?
  • Don't I have to specify the C++ compiler that is going to be used? How does terp find it? What if there's more than one compiler installed? Can I specify which one to use?
  • What about name prefixes and suffixes? Don't I need my Windows executable to have a .exe suffix?
  • What about different processor architectures, 32- and 64-bit builds, debug vs. release and all the other options I commonly have to use?

You are of course absolutely right to wonder about these issues but you will find that the <terp.cpp> task takes care of a lot of the details for you. Let's follow up the minimalist example with a more realistic example that demonstrates some of <terp.cpp>'s features:

<target name="compile-cpp" description="Build the shared library">
    
    <terp.foreach name="cpp.comp" expression="{^gcc(3.4), ^suncc()}" />

        <terp.cpp compiler="cpp.comp"
                  targetDir="${basedir}/lib/${cpp.comp.code}.${cpp.comp.version}"
                  tempDir="${java.io.tmpdir}/${cpp.comp.code}.${cpp.comp.version}"
        >
            <target name="funcs" type="shared" version="1.0.5" />
            <relocatable />
            <multithreaded />
            <rtti value="false" />
            <sources dir="${basedir}" includes="*.cpp" />
        </terp.cpp>

    </terp.foreach>

</target>
    

The above target will build a versioned shared library using a g++ 3.4 compiler and the latest installed SUN Workshop compiler. The libraries and intermediate object files will be built into compiler-specific directories. The <terp.cpp> compiler task is smart enough to translate the build instructions represented by the nested elements into compiler switches.

The following section discusses how the C++ compilers got picked in more detail.

Compiler Specification and Selection

On every platform, there is a preferred C++ compiler. On Windows for example, most people use the Microsoft Visual Studio tool chain for software development. The default compiler is therefore the latest Miscrosoft Visual C++ compiler terp can find on the build host. On all Unix platforms, there usually is a platform vendor-preferred compiler. On Solaris for example, the SUN Workshop compiler is chosen over a GNU C++ compiler in the absence of any more detailed compiler specification; on AIX, IBM's Visual C++ compiler is preferred over GNU as well. In general, the preference rules are:

  1. platform-native compiler has precedence over g++.
  2. when multiple versions of preferred compiler are present, latest compiler has precedence.

When you don't specify a compiler (as in the example above) or when you only specify the compiler as ^cpp(), you are telling the terp framework to pick its preferred compiler among all the C++ compilers it can find. If terp can't find any C++ compiler, it won't build anything.

You can specify a certain compiler type by using its specific compiler type converter rather than the generic C++ compiler type converter. To pick a SUN Workshop compiler, you would choose ^suncc(), to pick a GNU C++ compiler, you would choose ^gcc(). See a list of all understood C++ compiler type converters here.

What if you want a specific version of a compiler? Try a version specifier, for example ^gcc(3.4) or ^gcc({version:'3.4'}). You need to be aware that this is only going to work if terp can find a g++ compiler with the requested version on your system. See the list of all selectors and specifiers here.

You can use the terp command line utility to experiment with different compiler specifiers. Remember to doublequote your expressions so the shell does not try to interpret special characters. The following example demonstrates what happens on my Windows development system for various compiler specifiers:

C:\terp\bin>terp -e "^cpp()"
Microsoft Visual C++ 15.00.21022
C:\terp\bin>terp -e "^msvc()"
Microsoft Visual C++ 15.00.21022
C:\terp\bin>terp -e "^msvc(14)"
Microsoft Visual C++ 14.00.50727
C:\terp\bin>terp -e "^msvc(16)"
null
C:\terp\bin>terp -e "^msvc({procarch:x86, version:14}).compiler"
C:\Program Files\Microsoft Visual Studio 8\VC\bin\cl.exe
C:\terp\bin>terp -e "^msvc({procarch:amd64, version:14}).compiler"
C:\Program Files\Microsoft Visual Studio 8\VC\bin\x86_amd64\cl.exe

Using the generic ^cpp() specifier on a Windows host, terp selects the latest Microsoft compiler. Specifying ^msvc() has exactly the same result on a Windows system but would not select any compiler on a non-Windows system whereas ^cpp() would pick the default compiler for that system. Specifying the version number 14 selects a compiler because terp was able to find an older Microsoft compiler on the system by inspecting the registry. Specifying the version number 16 did not select a compiler because no compiler with a major version of 16 is installed on the system.

The last two examples demonstrate the full power of compiler selection. On my build system, I have the normal 32-bit compilers as well as the 64-bit cross-compilers installed. By specifying both a version and a target processor architecture, I can pick the desired compiler without having to know where it is installed. In the example I select the chosen compiler's compiler property to prove that the different specifications result in different executables being selected (they would both report the same version string).

terp.cpp compiler type specifiers
Name Description
^acc() The HP-UX ANSI C++ compiler (aCC), code 'acc'.
^cpp() Any C++ compiler that can be found. Preference rules apply.
^gcc() The GNU C++ compiler (g++), code 'gcc'.
^icc() The Intel C++ compiler (icc or icl.exe), code 'icc'.
^msvc() The Microsoft Visual Studio C++ compiler (cl.exe), code 'msvc'. See here for more details on using terp's C++ task with Microsoft Visual Studio's C++ compiler.
^qcc() The QNX Neutrino compiler wrapper (qcc), code 'qcc'.
^suncc() The SUN Workshop C++ compiler (CC), code 'suncc'.
^xlc() The IBM Visual C++ compiler (xlC), code 'xlc'.

All compiler specifiers accept the following initializers/specifiers:

terp.cpp initializers
Type Description
String

Interpreted as the path of the compiler executable if first letter is not a digit. Interpreted as compiler version number if first letter is digit (also see the entry on specifying compiler versions below).

Example: ^cpp('/usr/bin/g++')

File

Compiler executable path.

Example: ^suncc(^file('/opt/SUNWspr/bin/CC'))

Number

Compiler version. Please note that you cannot specify a version as a number if it violates decimal number rules (e.g. 4.2.3). To specify such a version number, use a string representation (e.g. '4.2.3')

Example: ^msvc(13.0)

Map

Compiler attributes. Supported attributes include:
   compiler: the compiler path.
   msvc: the MSVC++ compiler whose utilities the Intel C++ compiler will use (Intel C++ only).
   procarch: the processor architecture (bittedness).
   vcvars: the environment configuration file (for MSVC++ and Intel C++ only).
   vcvararg: optional argument for environment configuration file (Intel C++ only).
   version: the compiler version.

Example: ^gcc( {procarch:x86, version:'3.4.6'} )
               ^gcc( {compiler:'/opt/gcc4/bin/g++'} )

Compiler task attributes

The <terp.cpp> compiler task supports the attributes listed in the following table.

terp.cpp attributes
Name Type Description
bom String Not used.
cleanup boolean

Optional attribute to force deletion of created files. You can use this attribute as part of a clean target that removes intermediate and result files. Invoke the compiler task with cleanup="false" to perform a build or with cleanup="true" to perform a cleanup. Default value is false.

compiler Cpp.CppExecutor Semi-optional compiler specifier, a terp expression that is cast to a C++ compiler executor. For a more detailed explanation, see above. This is the only attribute which has a terp expression as its value, all other attributes have expandable text values. Use of this attribute is highly recommended because usually you want to control which compiler is being used.
createdirs boolean Optional attribute that causes creation of temporary and target directories if they don't exist yet. Default value is true.
cppextensions String Optional attribute. A comma- or semicolon-separated list of filename extensions that are accepted as C++ source files. Default value is ".cpp;.cxx;.c;.cc".
definitionfileextensions String Optional attribute. A comma- or semicolon-separated list of filename extensions that are accepted as C++ definition files. Definition files are only used by the Microsoft compiler. Default value is ".def".
depends boolean Optional attribute. Turns on dependency analysis mode if compiler supports it. When enabled, the compiler will be invoked first to find out on which files the source files depend. Recompilation will occurr when the source file is ou-of-date with respect to at least one include file. Default value is false.
Please see the documentation on the build process.
failonerror boolean Optional attribute governing behavior when compilation fails. Default value is true.
forcebuild boolean Optional attribute governing whether a build is performed unconditionally. Default value is false.
Please see the documentation on the build process.
iconfileextensions String Optional attribute. A comma- or semicolon-separated list of filename extensions that are accepted as icon files. Icon files are only used by the Microsoft compiler. Default value is ".ico".
if String Optional attribute specifying a boolean terp expression. The expression represents a condition that must be satisfied for the task to be executed.
showonly boolean Optional attribute governing whether compiler actions are taken or whether the compiler command lines are displayed.
targetdir File Semi-optional attribute specifying the directory containing compilation results. If not specified, value defaults to the project basedir. Use of this attribute is highly recommended.
tempdir File Semi-optional attribute specifying the directory containing intermediate compilation results (object files, generated source files, etc.). If not specified, value defaults to the project basedir. Use of this attribute is highly recommended.
unless String Optional attribute specifying a boolean terp expression. The expression represents a condition that must not be satisfied for the task to be executed.

Compiler task nested elements (Overview)

The <terp.cpp> task supports a number of nested elements, listed in the table below. All nested elements support the if and unless attributes that allow you to add compiler switches or input files just for a particular operating system or compiler.

The following variables are defined by the <terp.cpp> task and are available for evaluation in nested elements:

  • compiler   the executor which can be used to run the C++ compiler.
  • self       the <terp.cpp> instance itself.
  • targetDir  the directory into which compilation results are put (java.io.File).
  • tempDir    the directory into which intermediate compiler output or
                   dynamically generated input files are put (java.io.File).

For example, to add a preprocessor definition just for a particular compiler type, you could write:

<define name="USING_GCC" if="compiler.code=='gcc'" />
<define name="USING_MSVC" if="compiler.code=='msvc'" />

The above example would add -D USING_GCC when invoked with a g++ compiler and /D USING_MSVC when invoked with a Microsoft compiler. In general, this mechanism allows you to accommodate all compiler switches for a multi-platform/multi-compiler project in one place.

terp.cpp nested elements
Name Attributes Description
<boundscheck>

nmcl

Optional. Turns on instrumentation of compilation units with Compuware's Boundschecker tool. Optional nmcl attribute specifies path of nmcl.exe compiler wrapper.
Boundschecker is only supported on Windows with the MSVC++ compilers that are compatible with the respective version of Boundschecker.
You can run instrumented executables and generate XML reports using the <terp.boundschecker> task.

Example:      <boundscheck/>

<compileonly>

value

Optional. Suppresses any steps beyond compilation (link, postlink). Optional boolean value attribute specifies whether compile-only mode is enabled or disabled. When the <compileonly> element is present, the value is enabled by default. Use when you just want to compile to object files and link yourself. Be aware that there are many options that need to be used consistently at compile and link time. Compiling with one set of options and linking with another set of options might yield unpredicatable results.

Example:      <compileonly value="true" />

<debug>

value
level

Optional. Creates debug builds. Optional boolean value attribute specifies whether debug mode is enabled or disabled. Optional level attribute allows specification of debug level (appended to debug option). When the <debug> element is present, the value is enabled by default.

Example:      <debug value="${create.debug}" />

<define>

name
[value]

Optional. Adds a preprocessor definition. name attribute is required, value attribute is optional. Remember to use &quot; to represent the double quote character if you intend to define string literals.

You can pass single-quoted character literals as well as double-quoted string literals as values. Remember to use &quot; to represent the doublequote. When you omit the value attribute, the preprocessor will be free to assign a value, typically 1. To define an empty value (a macro that expands to nothing) you need to provide an empty value attribute.

Example:      <define name="FOO" value="bar" />

<dynamicsources>

[options]

Optional. <dynamicsources> is a container for <create> elements. Create elements are <terp.echo> tasks that allow you to create source files on the fly, as part of the compiler invocation. This is a great mechanism for generating source code that contains meta information about the build itself.

You do not have to specify a filename, but if you don't a random filename will be generated for you. As a result, the compiler will not be able to perform automatic cleanup of generated files for you because it will not be able to generate the same name again in a later cleanup run.

The options attribute allows you to add compiler switches defined in an external compiler options set to the compiler invocation for the files of this fileset rather than for all files in the compilation.

Example: 
This example adds a file called info.cpp to the list of source files for the compiler invocation. That file is re-created dynamically every time the compiler is invoked and contains the terp-expanded, nested text.


<dynamicsources>
<create file="${tempDir}/info.cpp"> int getMajorVersion() { return ${version.major}; } </create>
</dynamicsources>
<exceptions>

value
[synchronous]

Optional. Enables or disables exceptions. Optional boolean value attribute specifies whether exceptions are enabled (default) or disabled. Not all compilers support this switch. In these cases, no compiler switches will result from this element.
Optional boolean synchronous attribute specifies type of exception handling for Microsoft and Intel compilers on Windows.

Example:      <exceptions value="false" />

<include>

path

Optional. Adds directories to the preprocessor's search path for include files. Use either a path attribute or a nested <path> element to specify the directories to be added.

Example:   <include path="${basedir};${tempDir}" />

<libpath>

path

Optional. Adds directories to the linker's search path for libraries. Use either a path attribute or a nested <path> element to specify the directories to be added.
When building software that might itself be used by other developers it is often useful to put libraries into directories with mangled names including the operating system, processor architecture, and compiler type/version. In such cases the bestcppdir formatter can be very useful for locating a library directory even if the compiler being used is not an exact match for the compiler that was used to build the library.

Example:  <libpath path="${basedir};${tempDir}" />

<library>

[dynamic]
names
[static]

Optional. Adds linker references to libraries that the target module depends on. Only specify the library name without any filename extension or prefix. Separate multiple libraries by comma or semicolon.

Example:   <library names="Crun,test" />
Example:   <library names="kernel32,oleaut32" />

<manifest>

[file]
[identity]

Optional; applies only to Microsoft's VisualStudio C++ compiler. Adds a manifest to the module or suppresses the creation of a manifest file. Normally, you use manifest files whose name is the target module name plus ".manifest", but you can override the manifest file name via the optional file attribute. You can also specify an identity attribute that is merged into the manifest to provide a module identity. Please see the Microsoft manifest tool (MT.exe) documentation for more details.

<multithreaded>

value

Optional, but recommended. Enables or disables multithreading. Optional boolean value attribute specifies whether multithreading is enabled (default) or disabled. This switch has very different effects for different compilers. In some compilers, there are options that directly control multithreading support, for example the -mt option in SUN's Workshop compiler. In Microsoft's VisualStudio C++ compiler, this switch influences which C++ runtime library is being used (/MT, /MD, etc.)

Example:      <multithreaded value="true" />

<objects>

dir

Optional. Allows the addition of object files that were compiled in a separate step, possibly using a different build process. <objects> is a FileSet, with all attributes and nested elements supported by the ANT FileSet type.

<optimize>

forspace
forspeed
level

Optional. Allows the specification of an optimization level. forspace and forspeed are boolean options, level is a String option. The string values of none, disabled, all, and enabled receive special treatment, all other values are appended to the compiler-specific optimization switch (usually -O or /O).

Example:      <optimize forspeed="true" />

<option>

values
phase

Optional. Adds an arbitrary compiler switch to the compiler invocation. The phase attribute is optional. When none is specified, the option applies to both compile and link steps.

Example: <option values="/Yx" phase="compile"/>

<options>

refid

Optional. Allows the inclusion of an externally defined options set. With this mechanism you can define an id'ed compiler option set and the ninclude it in your compilation by reference. Please see the C++ compiler task test and example file for more details.


<terp.cpp.options id="defTEST" >
<define name="TEST" value="4" />
</terp.cpp.options> <terp.cpp ... > <options refid="defTEST" /> ... </terp.cpp>
<prebuild>  

Optional. A task container whose nested tasks are executed before any compiler invocations take place. You can do whatever you wish in this context while taking advantage of the variables that are defined by the compiler.
Please see the documentation on the build process to understand how this element's interacts with others during a rebuild.


<prebuild>
<echo message="Starting build with ${compiler}" />
</prebuild>
<postbuild>  

Optional. A task container whose nested tasks are executed after any compiler and linker invocations take place. You can do whatever you wish in this context while taking advantage of the variables that are defined by the compiler.
Please see the documentation on the build process to understand how this element's interacts with others during a rebuild.


<postbuild>
<echo message="Finishing build with ${compiler}" />
</postbuild>
<procarch>

procarch

Optional. This element might be necessary in multi-platform/multi-compiler builds, even if you specified the compiler with a procarch qualifier already. Some compiler executables support more than one processor architecture and you will have to use this element to indicate which processor architecture you wish to build for.

Example:      <procarch procarch="amd64" />

<profile>

value
tool

Optional. This element can be used to enable profiling support.

Example:      <profile tool="gprof" />

<quiet>

value

Optional. Switches the compiler into minimal verbosity mode. Optional boolean value attribute specifies whether to be quiet(default) or not. Not all compilers support this switch. In these cases, no compiler switches will result from this element.

Example:      <quiet/>

<relocatable>

value

Optional. Create relocatable code. Most compilers support an option that governs whether the built module can reside anywhere in memory or is expected at a fixed address. Shared libraries and executables are usually built relocatable but you might wish to build an archive containing relocatable objects.

Example:      <relocatable value="true"/>

<resource> charsetid
comments companyname filedescription fileversion [internalname] languageid legalcopyright legaltrademarks originalfilename
productname productversion
[specialbuild] targettype
[template]

Optional. Applies only to Microsoft Visual C++ compiler invocations but does not hurt when other compilers are used. Allows the dynamic creation of a resource file from a template, populated with data from this element's attributes.


<resource comments="A library"
          companyname="Codemesh, Inc."
          filedescription="A library we need"
          fileversion="${product.version}.2500"
          languageid="english"
          legalcopyright="Blahblah"
          legaltrademarks="... is a trademark of"
          productname="myprod"
          productversion="${product.version}"
targettype="shared" charsetid="ascii"
/>

The default template contains only a VERSIONINFO resource, but you could write your own resource template that contains additional resources. You could even specify a template that contains no variables, in which case the compiler will just invoke the resource compiler and add the resulting binary reseource file in the link step.

<rtti>

value

Optional. Enables or disables Runtime Type Information. Optional boolean value attribute specifies whether RTTI is enabled (default) or disabled. Not all compilers support this switch. In these cases, no compiler switches will result from this element.

Example:      <rtti value="false" />

<runpath>

[environment]
[linktime]
[path]

Optional. Adds a library searchpath to the built module. This is a link-time option that is only available on Unix. The legal values for this option differ. Some platforms support pseudo-variables refering to the location of the loading executable or the library itself, others don't.

Some linkers support suppression of the library paths that were used at linktime (linktime="false"). Some linkers support disabling of the environment searchpath overrides (environment="false"). Both linktime and environment are expected to resolve to boolean values and default to "true".

<runtime>

dynamic
static
debug
flavor

Optional, but recommended. Applies to Microsoft's VisualStudio C++ compilers and qcc but does no harm when used with other compilers. Allows the specification of the C++ runtime library that is to be used.

Example 1:     <runtime static="true" debug="false"/>

Example 2:    <runtime flavor="embedded"/>

<sources>

dir
[options]

Optional, but usually the whole point of the task. Allows the addition of C++ source files. <sources> is a FileSet, with all attributes and nested elements supported by the ANT FileSet type.

The options attribute allows you to add compiler switches defined in an external compiler options set to the compiler invocation for the files of this fileset rather than for all files in the compilation.

<target>

name
installname
type
[version] [makelinks]

Optional, but highly recommended. Specify the name and type of the built module. For portability reasons, omit any prefixes and suffixes. For example, specify <target name="mymod" type="shared"/> to build mymod.dll on Windows and libmymod.so on Linux. The legal values for type are archive, executable, and shared. The build result is created in targetDir.
When you are building a shared library for an ELF platform, the result can be versioned. The name and major version are embedded in the module and the makelinks option allows you to generate less version-specific links to the built library. By default, version is not set and makelinks is true.
The optional installname attribute allows you to specify an install name on MacOS-X.

<undefine>

name

Optional. Undefine a preprocessor definition.

Example:      <undefine name="MYDEF" />

<warning>  

Optional. Specify warning levels and selectively enable/disable certain warnings.

Compiler task nested elements (Detail)

<boundscheck>

In addition to if and unless, this element has just one optional attribute: nmcl. The nmcl attribute is interpreted as the full path to the Compuware compiler wrapper nmcl.exe that will delegate to the MSVC++ compiler during code instrumentation. Currently, no other compilers are supported. Certain versions of BoundsChecker are limited to certain versions of MSVC++. Please consult the Compuware documentation for details.

You usually don't need to use the nmcl attribute because the terp runtime locates BoundsChecker and its components automatically. Only use it if nmcl.exe cannot be located. When you build with this option, the task wrapper will instrument all compilation units that are being compiled.

Simply instrumenting your code does not generate any information for you. To get error information you have to run an instrumented executable with the BoundsChecker executable BC.exe. You can either invoke BoundsChecker using a standard ANT task, or a terp process executor, or the custom <terp.boundschecker> task.

<compileonly>

In addition to if and unless, this element has just one optional attribute: value. The value attribute is interpreted as a boolean terp expression that decides whether the compiler invocation shall attempt a link pass or stop after compilation. A default value of true is assumed.

You usually don't need to use this attribute because the compiler abstraction gives you the ability to mix in link phase sources (object files, resource files, etc.) via specialized nested elements.

<debug>

In addition to if and unless, this element has two optional attributes: value and level. The value attribute is interpreted as a boolean terp expression that decides whether the compiler-specific debug flag shall be set or not. A default value of true is assumed. The level attribute allows the specification of a string that is appended to the debug option for non-Windows compilers. For example, a level of "1" will yield a debug value of -g1.

Every compiler has different flags. When enabled, the following flags are passed to the compiler:

  • Microsoft (and Intel on Windows) compilers: /Zi in Compile phase, /DEBUG in Link phase
  • Unix/Linux compilers: -g

<define>

In addition to if and unless, this element has the required name attribute and the optional value attribute. The name is interpreted as a preprocessor definition. The value attribute is interpreted as the optional value of the preprocessor definition.

Most preprocessors seem to assign the numerical value 1 to a definition that has no specified value. To define a property with an "empty," non-numeric value, you need to provide an empty value attribute. This is important when you have preprocessor definitions in your code that have to resolve to nothing, i.e. compile out of the source code. Take the following examples:

  • <define name="EXPORT" />
    This will result in all instances of EXPORT in source code being replaced with whatever value the preprocessor assigns by default to a definition without a value, normally the numerical literal 1.
  • <define name="EXPORT" value="" />
    This will definitely result in an empty value. All instances of EXPORT in source code will be replaced with nothing.
  • <define name="EXPORT" value="__declspec(dllexport)" />
    This will result in all occurrences of EXPORT being replaced with __declspec(dllexport).
  • <define name="STRVAL" value="&quot;test&quot;" />
    This will result in all occurrences of STRVAL being replaced with a quoted string value of "test".

This element also supports string literals and character literals as values. Take care to use &quot; as the delimiter for the string literal value. You can directly use single quotes as character literal delimiters.

<dynamicsources>

This is one of the terp template-language oriented features in the compiler task. This element allows you to dynamically generate one or more source files just prior to the compiler invocation and automatically add them to the compiler (and linker) invocations. Why is this such a big deal? It is a big deal because it happens within the context of the compiler task. This means that all context variables are set up in accordance with the specific compiler invocation.

When you are building software on multiple platforms with multiple compiler types and versions, this task allows you to easily generate source files with content customized for the compilation environment. You could for example generate source files containing reflection/introspection functions that report on the compiler version and the compiler settings that were used to build the application.

The <dynamicsources> element supports only one type of nested element: the <create> element. Each <create> element is really a <terp.echo> task. You can optionally specify a file attribute to set the filename to which the dynamic content is written. When you don't specify a filename, a uniquely named file is created in the compiler invocation's tempDir. Please be aware that a file created in such a way cannot be cleaned up by invoking the compiler task a second time, this time with the cleanup flag set to true. The reason for this limitation is that the filename is totally random and cannot be reproduced in the second compiler task run.

An example element might look like this:

<dynamicsources>
    <create file="moduleQuery.cpp">
extern "C" EXPORT int getMajorVersion() {
    return ${version.major};
}

extern "C" EXPORT int getMinorVersion() {
    return ${version.minor};
}

extern "C" EXPORT const char * getVersionString() {
    return "${version}";
}
</create>
    <create file="dynamic1.cpp" template="dynamic.tpl"/>
</dynamicsources>

You can have a (theoretically) unlimited number of <create> elements. The second element in the example above demonstrates how you can use template files to uncouple the C/C++ source code from the build script.

<exceptions>

In addition to if and unless, this element has two optional attributes: value and synchronous. The value attribute is interpreted as a boolean terp expression that decides whether exception support should be generated or not. Not all compilers provide switches for enabling and disabling exceptions. In such cases, the setting might translate to a no-op. A default value of true is assumed. The optional synchronous attribute is only evaluated for Microsoft and Intel compilers on Windows. If true, synchronous exceptions handling is used (/EHsc), if false (default), asynchronous exception handling is used (/EHa).

You usually don't need to set this option unless you wish to disable exception handling or gain control over the type of exception handling on Windows.

<include>

In addition to if and unless, this element has just one attribute: path. The path attribute is interpreted as an ANT path that specifies the pre-processor search path for include files. Alternatively, you can specify the path via a nested <path> element. The following examples illustrate both approaches.

<include path="${basedir}/include;${basedir}/other/include" />
<include>
    <path refid="include.path" />
</include>

<libpath>

In addition to if and unless, this element has just one attribute: path. The path attribute is interpreted as an ANT path that specifies the pre-processor search path for include files. Alternatively, you can specify the path via a nested <path> element. The following examples illustrate both approaches.

<libpath path="${basedir}/lib;${basedir}/other/lib" />
<libpath>
    <path refid="library.path" />
</libpath>

<library>

In addition to if and unless, this element supports several other attributes. The names attribute is the most important one and allows you to specify a comma-, semicolon-, or space-separated list of library names that you want to link with. You only specify the basename of the library, no name prefixes like lib or suffixes like .so. The other two attributes, static and dynamic, are optional and allow you to specify whether you wish to link statically or dynamically. They are only relevant to builds on platforms that make such a distinction in their linker/loaders (Unix, Linux, etc.)

The following example demonstrates how you can specify libraries in a platform- and compiler-independent fashion.

<libpath path="${targetDir}/${compiler.code}.${compiler.version}/${proc.arch}/lib" />
<library names="pthreads,dl" if="os.family!='windows'" />
<library names="user32,conio,system" if="os.family=='windows'" />

<manifest>

No doc available yet.

<multithreaded>

In addition to if and unless, this element has just one optional attribute: value. The value attribute is interpreted as a boolean terp expression that decides whether an application is built for multithreading or not. Not all compilers provide switches for enabling and disabling exceptions. In such cases, the setting might translate to a no-op. A default value of true is assumed.

When enabled, the following settings are applied:

  • Sun Workshop, HP ANSI C++ compiler: -mt in compile and link phase.
  • all compilers except Microsoft compiler: -D_REENTRANT in compiler phase.
  • Microsoft C++ compilers: the setting is taken into account in determining the C++ runtime library that should be used.

<objects>

In addition to if and unless, this element supports all attributes and nested elements of an ANT FileSet. This element allows you to add object files to the link phase of the compiler invocation. Supposedly, the object files stem from a prior compiler invocation.

You need to be careful when adding object files that originate from a separate compiler invocation. On many platforms, adding objects that were built with different (incompatible) compiler switches can lead to hard-to-diagnose runtime problems.

<optimize>

In addition to if and unless, this element supports one optional attribute named level. The level attribute allows you to use the predefined optimization levels all, enabled, none, and disabled or a custom level. If you choose a custom value for level, it will be appended to the compiler's optimization switch. For example, a level of "3" translates to /O3 with the Microsoft C++ compiler. A default value of enabled is assumed.

There are two optional boolean attributes named forspeed and forspace. Setting either to true will select a compiler-appropriate optimization level reflecting the choice made. Please note that not all compilers support all settings. For example, the SUN Workshop compiler does not have a special switch for forspace and will cause a build exception to be thrown by the C++ task if instructed to optimize for space. You can work around this by conditionalizing the element:

<optimize forspace="true" unless="compiler.code=='suncc'" />

Another possible issue arises from non-native builds and forspeed optimizations. The SUN Workshop compiler supports the -fast switch, which is essentially a macro for several compiler different options. Unfortunately, it includes an option setting the target architecture to "native." To make your multi-processor architecture build work in such as case requires you to have the <procarch> element after the <optimize> element.

<option>

In addition to if and unless, this element has one required attribute called value and one optional attribute called phase. The value attribute is interpreted as an arbitrary string value that is going to be included unmodified as a compiler or linker switch. The optional phase attribute specifies the build phase to which the option should be applied. If no phase attribute is specified, the option applies to both compilation and link phases. The following examples illustrates the usage.

<option value="/Yx" phase="compile" if="compiler.code=='msvc'" />

<options>

In addition to if and unless, this element supports one required attribute named refid. The refid attribute provides the link to a named instanceof of a pre-defined compiler options object. You can combine this option with other compiler options; the effect is cumulative.

Take care to avoid duplication of options. If you include a compiler option both in the external option set and in the options directly specified in the compiler task the option might appear twice or the second instance might be discarded; the exact treatment depends on the option type. You should treat the outcome of such a misconfiguration as undefined.

This element is most useful if you are compiling many different projects and they should include similar settings. You might for example define an options instance for building shared libraries in your framework and another one for building executables and then reference the proper options set from you compiler tasks.

<postbuild>

This optional element is an ANT TaskContainer. It supports any number of ANT tasks as nested elements. The nested ANT tasks don't have to be terp tasks. The nested tasks are executed if the compiler invocation succeeded.

The <postbuild> element is very useful for performing generic postbuild steps that would benefit from having access to the compilation context, including the predefined variables.
Please also see the discussion of this element's interaction with the build process.

<prebuild>

This optional element is an ANT TaskContainer. It supports any number of ANT tasks as nested elements. The nested ANT tasks don't have to be terp tasks. The nested tasks are executed before the compiler is invoked.

The <prebuild> element is very useful for performing generic prebuild setup steps that would benefit from having access to the compilation context, including the predefined variables.
Please also see the discussion of this element's interaction with the build process.

<procarch>

In addition to if and unless, this element supports one additional attribute named procarch. The procarch attribute should be a processor family identifier like x86, amd64, ia64, sparc, etc. At a minimum, this option determines the bittedness of the build, for example 32-bit for x86 and 64-bit for amd64. For some compilers that support cross compilation, the actual processor architecture might be taken into account to create binaries for an architecture other than the one on which the compiler is running.

<profile>

In addition to if and unless, this element has two optional attributes: value and tool. The value attribute is interpreted as a boolean terp expression that decides whether the compiler invocation shall display compiler versions, trademarks, etc. or not. A default value of true is assumed.

The tool attribute allows you to select a profiling tool (usually "gprof" or "prof") for which the instrumentation is generated. Not all compilers support all tools. The default value of the tool attribute is "gprof".

<quiet>

In addition to if and unless, this element has just one optional attribute: value. The value attribute is interpreted as a boolean terp expression that decides whether the compiler invocation shall display compiler versions, trademarks, etc. or not. A default value of true is assumed.

<relocatable>

In addition to if and unless, this element has several optional attributes: value, baseaddress, and mode.

The value attribute is interpreted as a boolean terp expression that decides whether the compiler should generate relocatable code. A default value of true is assumed.

The baseaddress attribute is only used by Microsoft compilers and results in a /BASE: linker switch with the baseaddress value appended to the switch.

The optional mode attribute allows you to select small or large models via the values "small" or "large". The default value is "large".

Code relocation is implicitly assumed on some platforms but needs to be explicitly set on others. Compiler switches for some compilers are provided below. Where available, the PIE variant is chosen when the target is an executable rather than a shared library.

  • Sun Workshop and IBM compilers: -Kpic or -KPIC
  • HP ANSI C++ compiler: +Z
  • all other compilers except Microsoft compiler: -fpic or -fPIC
  • Microsoft C++ compilers: /FIXED or /FIXED:NO.

<resource>

The <resource> element is a complex element with many attributes. It applies only to Microsoft compilers but does not cause any damage when used with other compilers. Its main purpose is to dynamically create a resource file from a template, compile it to a binary resource and then pass it to the linker. By default, this task uses a template that only contains a VERSIONINFO resource with place holders that are expanded with this element's attribute values.

This element extends the <terp.echo> task, so it supports all its attributes, including file and template. To use your own resource template, simply specify the template filename as value for the template attribute. Don't confuse template with file which is used to specify the name of the generated resource file.

<rtti>

In addition to if and unless, this element has just one optional attribute: value. The value attribute is interpreted as a boolean terp expression that decides whether the compiler shall support Runtime Type Information (RTTI) or not. A default value of true is assumed.

You usually don't have to use this option.

<runtime>

In addition to if and unless, this element has several additional optional attributes: debug, dynamic, static, and flavor. Use this attribute to specify the C++ runtime to be used when compiling with Microsoft compilers. The Microsoft C++ runtime supports multiple variants, differentiated by threading (single or multi), linkage (static or dynamic), and debug vs. release. The <runtime> task takes the optional <multithreaded> and <debug> elements into account for default values. The debug attribute can be used to override the <debug> derived default value.

Also use this element to specify a runtime flavor for the QNX Neutrino compiler. Supported flavors include "abridged" (acpp), "embedded" (ecpp), "standard" (cpp), or "gnu" (gpp).

In the following example, we're asking the Microsoft tools to perform a multithreaded debug build, but we want to link with the static release runtime:

<debug/>
<multithreaded/>
<runtime debug="false" static="true" />

In the following example, we're asking the QNX Neutrino compiler (qcc) to perform a build using the embedded C++ runtime without exception support:

<exceptions value="false"/>
<runtime flavor="embedded" />

<sources>

In addition to if and unless, this element supports all attributes and nested elements of an ANT FileSet. This element allows you to add C and C++ source files to the compile phase of the compiler invocation. You can add multiple <sources> elements. You are not limited to C and C++ source files. You can also add other project files like module definition files or resource files.

Please note that the <sources> element interacts with several extensions attributes that are defined for the compiler task. The files selected by this element are filtered through the cppextensions and definitionfileextension filters to determine how to handle them. Only files selected by the cppextensions attribute will be handed to the C or C++ compiler for compilation.

The following example includes in the build all source files located in the project directory.

<sources dir="${basedir}" includes="*.cpp" />

<target>

The target element allows you to specify the name and type of binary that you wish to build.

Only specify the basename when specifying the name. For example, instead of specifying "mytool.exe", just specify "mytool". This makes the build more platform portable. The terp compiler task will pick correct name prefixes and suffixes based on platform and target type.

You can also specify an installname attribute that will only be evaluated on MacOS-X. You have to specify the exact name that you wish the built module to have, including any special variables like @rpath and including the file extension.

The target type should be one of archive, executable, and shared.

While the name and type attributes are the most important attributes, there are other optional attributes.

On Windows, the implib attribute governs the name and path of the import library for a shared library. The default is to create the import library in the same directory as the shared library.

Also on Windows, the subsystem attribute governs the Windows subsystem for which the module is built. Legal values are one of CONSOLE or WINDOWS. The default value is CONSOLE.

The version attribute can be used to create versioned modules. On platforms with ELF linkers, shared libraries can be versioned with up to three version numbers (major, minor, build). The major version number is treated as the compatibility version and gets embedded in the internal module name. The other version numbers become part of the filename.

The makelinks attribute (default value of true) governs whether the compiler task should create links for a versioned library (see the example in the examples section below).

<undefine>

In addition to if and unless, this element has the required name attribute. The name is interpreted as a preprocessor definition that is to be removed from the preprocessor's list of defined names.

Please note that undefining a preprocessor definition is not what you want to do when you are trying to make a preprocessor definition resolve to nothing. To do that, you have to define it to an empty value.

<warning>

In addition to if and unless, this element has the optional level, asErrors, and portability attributes and optional nested <error> and <disabled> elements. The level attribute allows you to use the predefined optimization levels all, enabled, none, and disabled or a custom level. If you choose a custom value for level, it will be appended to the compiler's warning switch.

The optional asErrors attribute is a boolean attribute that allows you to convert all enabled compiler warnings to compiler errors. The default is false.

The optional portability attribute is a boolean attribute that allows you to specifically enable portability warnings. The default is false.

The nested <error> element allows you selectively treat certain warnings as errors. <error> elements support multiple warning and warnings attributes. The warning attribute expects one warning code as value, the warnings attribute expects several comma-, semicolon-, or space-separated warning codes as value.

The nested <disabled> element allows you selectively ignore certain warnings. <disabled> elements support multiple warning and warnings attributes. The warning attribute expects one warning code as value, the warnings attribute expects several comma-, semicolon-, or space-separated warning codes as value.

The following example illustrates how you can use this element to set the warning level to exactly what you want. As every compiler uses different warning codes, you almost certainly will have to use one <warning> element per compiler type and distinguish between them with an if attribute.

<warning level="enabled" 
         portability="true"
         if="compiler.code=='msvc'>
    <disabled warnings="4385,4507,4034" />
</warning>

Examples

Building a versioned shared library

We want to use the SUN Workshop compiler (without specifying a particular version or compiler location) to build a shared library which contains the name and version number and we also want to create less version-specific links to it. Here's how we might achieve this:

<terp.cpp compiler="^suncc()"
          targetDir="${basedir}/suncc.${compiler.version}"
          tempDir="${basedir}/suncc.${compiler.version}/temp"
>
    <target name="test" type="shared" version="1.0.3" />
    <sources dir="${basedir}" includes="*.cpp" />
</terp.cpp>

If the compilation succeeds, the end result will be a directory looking something like this:

[alex@x2100]ls -la ./suncc.5.8
total 28
drwxrwxrwx   2 alex     codemesh     512 May 18 15:55 .
drwxrwxrwx 4 alex codemesh 512 May 18 15:55 .. lrwxrwxrwx 1 alex codemesh 10 May 18 15:55 libtest.so -> libtest.so.1
lrwxrwxrwx 1 alex codemesh 12 May 18 15:55 libtest.so.1 -> libtest.so.1.0
lrwxrwxrwx 1 alex codemesh 14 May 18 15:55 libtest.so.1.0 -> libtest.so.1.0.3
-rwxrwxrwx 1 alex codemesh 4046 May 18 15:55 libtest.so.1.0.3 drwxr-xr-x 2 alex codemesh 512 May 18 15:55 temp

You can see that the shared library libtest.so.1.0.3 was built. The <terp.cpp> task also created links to that library so that the linker can find it when the library reference simply specifies the name or the name and a partial version number. To suppress the link generation, you would have to add the attribute makelinks="false" to the nested <target> element. On Windows these settings are ignored.

All the object files can be found in the the temp folder as specified by the tempDir attribute.

Adding a version query API for a shared library

Let's take the previous example and make the version queryable via a set of C functions without adding another source file.

<property name="version" value="1.0.3" />

<terp.cpp compiler="^suncc()"
          targetDir="${basedir}/suncc.${compiler.version}"
          tempDir="${basedir}/suncc.${compiler.version}/temp"
>
    <target name="test" type="shared" version="${version}" />
    <sources dir="${basedir}" includes="*.cpp" />

    <define name="EXPORT" value="" if="os.family!='windows'"/>
    <define name="EXPORT" value="__declspec(dllexport)" if="os.family=='windows'"/>

    <dynamicsources>
        <create file="version_query.cpp">
extern "C" EXPORT int getMajorVersion() {
    return ${^v(version).major};
}
extern "C" EXPORT int getMinorVersion() {
    return ${^v(version).minor};
}
extern "C" EXPORT int getPatch() {
    return ${^v(version).patch};
}
${eol}</create>
    </dynamicsources>

</terp.cpp>

We have modified the first example in the following ways:

  • we introduced the version number as a regular ANT property so we can reference it in more than one place rather than sprinkling hardcoded duplicate strings throughout the code.
  • we added a dynamically generated source file that contains three functions that each return one component of the version as an integer value.
  • we added a preprocessor definition to export those functions from the shared library. The definition is conditional upon the operating system (could and should be conditional upon the compiler) and provides different values for Windows and non-Windows systems. This is only for illustration purposes because we're really only compiling with the SUN Workshop compiler which does not work on Windows.

Building for multiple architectures

So far, we've built with one compiler in the default compiler mode, which is 32-bit on most platforms and for most compilers. Let's see what it would take to convert the above example to building with both SUN Workshop and g++ compilers, as long as g++ is installed. We would also like to build in 32-bit and 64-bit mode. In ANT, without the <terp.cpp> task and its support tasks, this would already turn into quite an undertaking. Let's take the first example again (to get an uncluttered starting point) and point out the changes:

<!-- bind the compiler instance to the name 'comp' 
     each could be null if we could not locate the 
     compiler -->
<terp.foreach name="comp" 
              expression="{^suncc(), ^gcc()}"
>
    <!-- check whether we have a compiler -->
    <continue if="comp==null" />

    <!-- bind the procarch instance to the name 'pa' -->
    <terp.foreach name="pa" 
                  expression="{x86,amd64}"
    >
        <terp.cpp compiler="comp"
                  targetDir="${basedir}/${compiler.code}.${compiler.version}/${pa}"
                  tempDir="${basedir}/${compiler.code}.${compiler.version}/${pa}/temp"
        >
            <procarch procarch="${pa}" />
            <target name="test" type="shared" version="1.0.3" />
            <sources dir="${basedir}" includes="*.cpp" />
        </terp.cpp>
    </terp.foreach>
</terp.foreach>

As you see, the key to multiplatform/multicompiler building lies in using nested <terp.foreach> elements to iterate over all desired combinations of compiler and processor architecture. There are different ways you could do this. Un Unix and Linux, one compiler executable will usually support both 32-bit and 64-bit architectures and you can derive the supported processor architectures from the compiler:

<!-- bind the compiler instance to the name 'comp' 
     each could be null if we could not locate the 
     compiler -->
<terp.foreach name="comp" 
              expression="{^suncc(), ^gcc()}"
>
    <!-- go to the next compiler if the value was null 
         because we could not find it -->
    <continue if="comp==null" />

    <!-- iterate over the processor architectures 
         supported by the compiler -->
    <terp.foreach name="pa" 
                  expression="comp.procarchs"
    >
        ...
    </terp.foreach>
</terp.foreach>

On 32-bit Windows, the default Microsoft Visual C++ compiler only supports the native 32-bit platform. If a crosscompiler for amd64 is installed, it's a separate executable that needs to be selected by processor architecture. In that case, you need to iterate over the processor architectures first to perform builds for both architectures:

<!-- bind the processor architecture family name to 
     the name 'pa' and iterate over it -->
<terp.foreach name="pa" 
              expression="{x86,amd64}"
>
    <!-- look for a compiler that supports that procarch -->
    <terp.foreach name="comp" 
                  expression="^msvc({procarch:pa})"
    >
         <!-- check whether we found such a compiler -->
        <continue if="comp==null" />
        ...
    </terp.foreach>
</terp.foreach>

The <continue> element checks whether a compiler could be found. If not, it skips the remainder of the nested elements and continues with the next compiler.

Even with our compiler abstraction, you might still find such corner cases where a slightly different treatment of Unix and Windows is required.


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

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

Commandline