Bouwer Tutorial

This page gives a introduction how to use bouwer for simple C/C++ development. Make sure you have bouwer installed on your system:

$ git clone https://github.com/nieklinnenbank/bouwer

If you want to invoke bouwer by simply calling bouw on the commandline, make an alias in bash:

$ alias bouw=/path/to/bouwer.git/bouw

To view all available

Hello World

First we make a hello world C program. Open your favorite text editor and create the file hello.c:

1
2
3
4
5
6
7
#include <stdio.h>

int main(void)
{
    printf("Hello world!\n");
    return 0;
}

To build a C program using Bouwer, you must create a Bouwfile which describes how to build it. Create a file called Bouwfile in the same directory as the hello.c file:

1
2
def build(conf):
    Program('hello.c')

Now you need to run Bouwer to let it read the Bouwfile and compile the Hello world program. Type the following command on your terminal:

$ bouw
    CC  hello.o
  LINK  hello

The hello world program is now compiled! Run it to test:

$ ./hello
Hello world!

If you run bouwer again, you will see it will not re-compile the Hello world program, until you actually change the source code:

$ bouw
$ touch hello.c
$ bouw
    CC  hello.o
  LINK  hello

Adding configuration

To make the hello world configurable, you must add configuration items in a Bouwconfig file. Create the Bouwconfig file with the following contents:

config HELLO
    bool "Hello World program"
    help
      This program outputs a message to standard output

config HELLOMSG
    string "Hello World Message"
    default "This is the Hello World program!"
    depends on HELLO
    help
      Message for the Hello World Program

The Bouwconfig specifies a configuration item HELLO and HELLOMSG. The HELLO item is a boolean and determines if the hello world program should be build. The HELLOMSG is a string with the message to print from the hello world program.

To let the hello world program use the HELLOMSG you need to generate a C configuration header config.h from the current configuration. You can use the builder ConfigHeader to generate it. Change your Bouwfile to the following:

def build(conf):
    """
    Build the hello world program
    """
    ConfigHeader('config.h')
    Program(conf.HELLO, 'hello.c')

Change the hello world program to include the config.h file, so that it can insert the HELLOMSG to the printf() line:

#include <stdio.h>
#include "config.h"

int main(void)
{
    printf(CONFIG_HELLOMSG "\n");
    return 0;
}

Now compile the hello world program and see it work:

$ bouw
    GEN  config.h
     CC  hello.o
   LINK  hello
$ ./hello
This is the Hello World program!

Changing configuration

../_images/menuconfig1.png

To change the configuration for the hello world program, you can use the MenuConfig graphical configuration editor. Simply start bouwer with the --menuconfig commandline argument to change the configuration:

$ bouw --menuconfig

In the graphical interface, press the UP, DOWN keys to navigate. Press ENTER to change a configuration item. For example, you can change the HELLOMSG by using the UP & DOWN keys to select it, then press ENTER and type a new message. Press TAB to select the OK button. Then press ENTER to press it. You can also change other settings, such as the compiler (CC) or the build directory (BUILDROOT). After changes, press Q to quit and save the configuration. Then (re)compile the hello world program to apply your changes to the program:

$ bouw
    GEN  config.h
     CC  hello.o
   LINK  hello
$ ./hello
My changed hello world message

Adding build targets

You can add more build targets in a Bouwfile. By default bouwer execute the build target build. For example, you can add a build target dist and uname to the hello world project:

def build(conf):
    """
    Build the hello world program
    """
    ConfigHeader('config.h')
    Program(conf.HELLO, 'hello.c')

def dist(conf):
    Archive('hello.tar.gz', exclude=['hello', 'config.h'])

def uname(conf):
    Command('uname -a')

Now you can run the build targets simply by passing them as a parameter to the bouw program:

$ bouw
     CC  hello.o
   LINK  hello
$ bouw dist
    TAR  hello.tar.gz
$ bouw uname
Linux nemesis 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Customizing build output

It is possible to output the build progress in various ways. By default bouwer outputs the build status in pretty output mode (--pretty). Bouwer also supports a progress bar (-p / --progress), full command output (--full) and quiet mode (-q, --quiet). See the bouwer commandline help for more information about the bouwer commandline arguments:

$ bouw --help

Prebuild Compiler checks

You can let various system checks run before the build using Autoconf-a-like checks. For example the following Bouwfile does header, library and function checks for the hello world program.

def build(conf):
    """ Build the hello world program """

    RequireVersion('0.1.0')

    CheckOS(conf.OS)
    CheckCompiler(conf.CC)
    CheckHeader('HAS_SYSLOG', 'syslog.h', False)
    CheckFunction('HAS_ADDCH', 'addch', 'ncurses', False)
    CheckFunction('HAS_ADDSTR', 'addstr', 'ncurses', False)
    CheckLibrary('HAS_NCURSES', 'ncurses', False)
    ConfigHeader('config.h')

    Program(conf.HELLO, 'hello.c')

Linking with a library

See the library demo in the demo/c/library directory of the bouwer source code on how to link with libraries.

Separating into Subdirectories

See the library demo in the demo/c/library directory of the bouwer source code on how to separate your project into directories.

Installing files

To install files, simply invoke the Install() builder. For example, the nano example project can install nano using the following build target in the Bouwfile:

def install(conf):
    Install(conf.BINDIR, ['src/nano'])

Creating a source archive (.tar.gz)

def dist(conf):
    Archive('hello.tar.gz', exclude=['hello', 'config.h'])

Adding configuration trees (debug/release)

See the configuration trees examples in the demo/c/trees directory in the bouwer source code.