Projekt

Allgemein

Profil

Gwbuild-Tutorial Simple Application

Build File

Gwenbuild uses XML files to define how a project is build. Those files have the name 0BUILD.

The main project file resides usually in the root directory of a project and contains a description of the project (e.g. name, version, dependencies, options etc.).
The following is a simple toplevel build file for a helloworld console program. While explaining this file we also look at other XML elements which are not necessarily used in this example.

<?xml?>

<gwbuild requiredVersion="5.6.0" >

  <project name="HelloWorld" version="0.9.134-beta" write_config_h="TRUE">

    <setVar name="package">$(project_name)</setVar>
    <define name="PACKAGE" value="$(package)"  quoted="TRUE" />

    <!-- prefix handling -->
    <option id="prefix" type="string">
      <default>/usr/local</default>
    </option>
    <setVar name="prefix">$(option_prefix)</setVar>
    <setVar name="bindir">$(option_prefix)/bin</setVar>

    <!-- dependencies ( pkg-config and libs) -->
    <dependencies>
      <dep id="gwenhywfar" name="gwenhywfar" minversion="5.5.1.1" required="TRUE" />
      <dep id="aqdatabase" name="aqdatabase" required="TRUE">
        <variables>aqdatabase_typemakerdir</variables>
      </dep>
    </dependencies>

    <checkheaders>
      stdlib.h stdio.h
    </checkheaders>

    <checkfunctions type="c" >
      fprintf
    </checkfunctions>

    <target type="Program" name="helloworld" install="$(bindir)" >

      <includes type="c" >
        $(aqdatabase_cflags)
        -I$(topbuilddir)
        -I$(topsrcdir)
        -I$(srcdir)
      </includes>

      <sources>
        helloworld.c
      </sources>

    </target>

  </project>

</gwbuild>

Defining the Project

As we can see it defines a project called HelloWorld with the version 0.9.134-beta (in the <project> element).

  <project name="HelloWorld" version="0.9.134-beta" write_config_h="TRUE">

This element also defines that a file called config.h is build. This is a generated header file containing some #define stances, most users probably know this file from autotools.

    <setVar name="package">$(project_name)</setVar>
    <define name="PACKAGE" value="$(package)"  quoted="TRUE" />

These elements create a variable called package to which the name of the project is assigned, this variable is also known from autotools. Variables defined/set via <setVar> are available throughout the remainder of the build file and also to all later included build files in sub folders (of which are none in this tutorial here).

The second element creates the following line in the generated config.h file mentioned above:

    #define PACKAGE "HelloWorld" 

As you can see we use the content of the previously assigned variable package. This is the way variables are accessed in build files: $(VARIABLENAME).

The main difference between variables (<setVar>) and defines (<define>) is that defines inside a <project> element are written to the config.h header file (or, when inside a <target> element added via -DVAR=VALUE" to the command line of the compiler process) so those definitions are directly accessible to source files which include config.h while variables are only available to the build system (e.g. all 0BUILD files).

Using Options

Next some options which are used to influence the build process, e.g. in larger projects you may want to make the building of parts of your project optional or you want to set the installation prefix for your project which, incidently, the following XML elements do:

    <option id="prefix" type="string">
      <default>/usr/local</default>
    </option>

In general for every option a variable of the name option_OPTIONID is created (in this example: option_prefix).
Here we define the option "<prefix>" which gets assigned a default value ("/usr/local"). You can set the option with the "-o" argument of gwbuild as in "gwbuild -s SOURCEPATH -o prefix=/somewhere". Options are only handled in the setup phase which is entered by "-s SOURCEPATH".

The next two lines again are variable definitions. We use the variables prefix and bindir known from autotools.

    <setVar name="prefix">$(option_prefix)</setVar>
    <setVar name="bindir">$(option_prefix)/bin</setVar>

Checking Dependencies

Now lets check some dependencies. As an example we assume that our HelloWorld example depends on the libraries named gwenhywfar (version 5.5.11 or newer, required) and aqdatabase (any version acceptable, required).

    <dependencies>
      <dep id="gwenhywfar" name="gwenhywfar" minversion="5.5.1.1" required="TRUE" />
      <dep id="aqdatabase" name="aqdatabase" required="TRUE">
        <variables>aqdatabase_typemakerdir</variables>
      </dep>
    </dependencies>

The XML attributes name and minversion are conveyed to the pkgconfig tool. For our build file we want to use the prefix gwenhywfar and aqdatabase, respectively. Using the same words for id and name makes it easier to remember those names later.

Thess XML elements create some variables which can be used in your build files. Those dependencies are marked as required, so the setup phase fails if those are missing.
If those libraries exist the following variables will be made available to the build files:

  • gwenhywfar_EXISTS set to "TRUE" (if missing: "FALSE")
  • gwenhywfar_CFLAGS set to the CFLAGS reported by pkgconfig
  • gwenhywfar_LIBS set the LDFLAGS reported by pkgconfig

Same for aqdatabase. However, this example shows an extension of this mechanism. pkgconfig - which is internally used by gwbuild to resolve dependencies - also sometime defines special variables (e.g. path to some modules or to data files of the project). In this example we use the variable aqdatabase_typemakerdir which contains the path to some data files for the typemaker2 tool. So the created gwbuild variables for aqdatabase are:

  • aqdatabase_EXISTS set to TRUE
  • aqdatabase_CFLAGS
  • aqdatabase_LIBS
  • aqdatabase_AQDATABASE_TYPEMAKERDIR (content of the pkgbuild variable aqdatabase_typemakerdir)

Checking Libraries Without pkgconfig

Some libraries don't provide support for pkgconfig which is used by gwbuild for dependency tracking. In these cases you can check with this XML element for such a library:

    <checklibs>
      <lib id="gmp" name="gmp" function="__gmpz_init" />
    </checklibs>

Here we check for the existence of the library libgmp. Gwbuild compiles a small code snippet which contains a call to the given function (in this case "__gmpz_init") and tries to link that snippet against matching candidate libraries. This creates the variable gmp_EXISTS. If the library can be found that variable is set to "TRUE" (otherwise to "FALSE") and the variable gmp_LIBS contains needed LDFLAGS to link against that library.

Checking Header Files

Here we check for the existence and usability of the system header files stdlib.h and stdio.h:

    <checkheaders>
      stdlib.h stdio.h
    </checkheaders>

These tests create variables which tell you whether they exist (and are usable):

  • stdlib_h_EXISTS set to "TRUE" (otherwise "FALSE")
  • stdio_h_EXISTS

They also generate #define lines in the config.h header file:

  #define HAVE_STDLIB_H 1
  #define HAVE_STDIO_H 1

Checking Functions

We want to use the function fprintf, which is probably always available on nearly every system, but let's be sure:

    <checkfunctions type="c" >
      fprintf
    </checkfunctions>

This creates the variable

  • fprintf_EXISTS set to "TRUE" ("FALSE" if not available)

and the following #define lines in config.h:

  #define HAVE_FPRINTF 1

Checking Programs

This XML element is not used in this particular example but introducing it fits right in since program checks belong in the same area as checking for headers and functions.
<checkProgs> checks for the existence of a given application on the build system.

As a result variables with the given id as prefix will be created, in this example we use the application xmlmerge which is part of AqBanking.
The following variables will be set:
  • xmlmerge_EXISTS: "TRUE" if found, "FALSE" otherwise
  • xmlmerge: full path to the executable (e.g. /usr/local/bin/xmlmerge)

Short Version


 <checkProgs>
   <prog id="xmlmerge" cmd="xmlmerge" />
 </checkProgs>

Long Version

Look for a program/executable with the given name (or alternative names) and write the result into a variable to be used later on in the build files.


 <checkProgs>
   <prog id="xmlmerge" cmd="xmlmerge" >
     <alternativeNames>
       xmlmerge
     </alternativeNames>

     <paths>
       /usr/local/bin
       /usr/bin
       /bin
     </paths>
   </prog>
 </checkProgs>

Defining the Build Targets

gwbuild knows multiple types of targets:

  • Program: an executable binary, created by compiling source files and linking the generated object files into an executable
  • ConvenienceLibrary: internally used static library which is to be combined with other internal libraries to form a (shared) library or an executable. Those are also used in autotools to split big projects into smaller modules
  • InstallLibrary: shared library to be installed to the system (e.g. libaqbanking.so)

In our tutorial we want to create a simple executable called helloworld.

    <target type="Program" name="helloworld" install="$(bindir)" >

This XML element also defines the final installation path using the variable bindir we defined just a few lines back.
The target type is Program so gwbuild now knows that we are to build an executable build using source files which are compiled into object files and those linked into an executable.

For the compilation process the compiler probably needs some information, the most important being where to find header files (e.g. in this case those of the project aqdatabase).

      <includes type="c" >
        $(aqdatabase_cflags)
        -I$(topbuilddir)
        -I$(topsrcdir)
        -I$(srcdir)
      </includes>

In this case we use the CFLAGS for AqDatabase (stored in aqdatabase_cflags) but we also add as a general approach the top build folder, the top source folder and the current source folder.
Why do we differentiate between source and build folders?

gwbuild is a strictly out-of-source-tree system. It doesn't modify files in your source tree and it doesn't write files into it. Rather, you setup your build environment outside the source tree (or in a special folder called build).

If files are created by gwbuild - most typical case being creating source files from tm2-files using the typemaker2 tool - they are written into the build tree, not the source tree. So in order to allow for a simple include "HEADER_FOR_GENERATED_FILE" w need to add the build folders to the include list.

In this example the XML element <includes> is used for C code files.

Defining the Source Files

In this tutorial we only have on source file: helloworld.c.

      <sources>
        helloworld.c
      </sources>

If your target also contains header files then those go into the XML element <headers>:

    <headers dist="true" install="$(prefix)/include">
      helloworld.h
    </headers>

In this example we want the fictional header file helloworld.h to be installed to the folder include under the prefix we defined above. This header is also to be included into a packaging tarball (dist=true).
Multiple occurrences of the header element are allowed which lets you specify header files which are to be installed somewhere and those which aren't.

Source File

This is the source file which is compiled into an object file and that linked into the executable helloworld.
Like most source files this first includes the aforementioned header file config.h which contains the defines we talked about.


#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <stdio.h>

void HelloWorld(const char *senderName)
{

  fprintf(stdout, "Hello world from \"%s\"\n", senderName?senderName:"<no name>");

}

int main(int argc, char **argv)
{
  HelloWorld("helloworld.c");
}

Files and Folders Created in the Build Folder

After the first invocation of gwbuild a few files and folders are generated in the build folder which are sometimes interesting to inspect:
  • .gwbuild.args: arguments used for the initial setup (e.g. for gwbuild -s ../)
  • .gwbuild.buildfiles: list of 0BUILD files in the project (once for every folder)
  • .gwbuild.ctx: contains descriptions for source files, build targets and dependency information
  • .gwbuild.ctxtree: contains variables and infos for every build context (e.g. the status of gwbuild in every folder). This is usefull for inspecting available variables.
  • .gwbuild.files: definitions of files used in the project
  • .gwbuild.installfiles: definitions of files to be installed (including destination paths)
  • .gwbuild.projectinfo: definitions of the project itself (name, version etc.)
  • .logs: folder for logfiles possibly generated by compiler and linker (e.g. redirected stfout/stderr output of the commands, deleted after printing content to the console)

Next Tutorial

Please see Gwbuild-Tutorial2 for an example for building a project with a library and an app.