Chapter 1. Setting Up Your Project

This chapter will walk you through the basics of setting up a MakeKit project. It will assume a C language project, the finer details of which are covered in Chapter 3, C Language Projects. However, the steps here remain the same for any sort of project.

Directory Layout

All MakeKit rules are placed in MakeKitBuild. You can opt to use a single such file in the top level directory of your project, or split your build logic into smaller MakeKitBuild files in each subdirectory of your project, or some intermediate arrangement. A typical project opting for multiple small files might look like the following:

project-name/

  • MakeKitBuild

  • default.conf

  • include/

    • MakeKitBuild

    • project-name.h

  • src/

    • MakeKitBuild

    • project-name.c

  • doc/

    • MakeKitBuild

    • readme.txt

Build Files

Once you've decided where to place your MakeKitBuild files, you'll need to put something in them. Each file is an ordinary POSIX shell script, but it should adhere to a particular structure which MakeKit expects. The following shows what the top-level MakeKitBuild might look like for our example project layout:

SUBDIRS="include src doc" 1
MODULES="core compiler" 2
PROJECT_NAME="project-name" 3

defaults() 4
{
    PREFIX=/opt/project-name
}

option() 5
{
    mk_option \
        OPTION="config-file" \
        VAR="CONFIG_FILE" \
        PARAM="path" \
        DEFAULT="${MK_SYSCONFDIR}/project-name.conf" \
        HELP="Configuration file path"
}

configure() 6
{
    mk_msg "configuration file: $CONFIG_FILE"
}

make() 7
{
    mk_stage SOURCE="default.conf" DEST="$CONFIG_FILE"
}

1

The SUBDIRS variable specifies a list of subdirectories where MakeKit should look for further MakeKitBuild files. This line can be omitted if there are no subdirectories to specify.

2

The MODULES variable specifies which MakeKit modules you will be using. Each module adds additional functionality on top of the core build engine. For example, the compiler module gives you access to functions for configure and building C language programs and libraries.

3

The PROJECT_NAME variable specified the name of the project. If unspecified, the name of the directory of the MakeKitBuild file is used instead.

4

The defaults function allows you to set default values for options that are usually specified by the user when configuring the project to be built, such as the installation prefix.

5

The option function is where you can define configuration options for your project which can be passed on the command line. Each option is declared with with mk_option function, which specifies the option name as passed on the command line (OPTION), the shell variable to store the setting in (VAR), a one-word description of the parameter that should be passed to the option (PARAM), a default value to use if the option isn't specified (DEFAULT), and a help message descriping the option (HELP). This particular example allows customizing the location of a configuration file.

6

The configure function is where you can perform any configuration for your project. Typical operations performed here include processing any options which were specified and testing that the system meets any prerequisites for building. Messages are usually printed to the user summarizing what is being done.

7

The make function is the meat of your build system. This is where you should define the build rules for your project that produce its actual end products. The example above installs a default configuration file into the location that was specified with the config-file parameter.

All of these elements are optional. It is not unusual for subdirectories that act only as organizational elements to contain a MakeKitBuild with only a SUBDIRS line.

Your build files will be processed as follows, starting from the top-level MakeKitBuild:

  • The option function will be run if present.
  • The configure function will be run if present.
  • For each directory in SUBDIRS, the MakeKitBuild file in that directory will be recursively processed according to this list.
  • The make function will be run if present

If you wish for the make function of a directory to be run before (or in the middle) of processing subdirectories, you may put . in SUBDIRS.

Installing MakeKit

Once your build files are in place, you're ready to install a copy of MakeKit into your project directory. This step makes your build system self-contained and ready to distribute to other people. Simply run the following in your shell:

$ makekit init

This will install a copy of MakeKit into the mk subdirectory, and create an executable configure script which can be run to configure your project.

You can see additional options to the makekit init command by specifying the -h option:

$ makekit init -h
makekit init -- install MakeKit files into current directory

Usage: makekit init [ options ]

Options:

  -h,--help         Show this help
  -s                Symlink files instead of copying them
  -f                Overwrite existing files

Configuring

Once MakeKit has been installed, you are ready to run configure to generate a Makefile for your project. You should always configure from a directory other than your project directory to avoid cluttering it with generated files. This also allows you to have multiple build areas with different settings (e.g. debug versus release).

For example, the following sequence of commands demonstrates creating a build area called build and the result of running configure from within it.

$ mkdir build
$ cd build
$ ../configure
           [makekit] initializing
          [platform] build operating system: linux
          [platform] build distribution: ubuntu
          [platform] build distribution version: 10.10
          [platform] build processor architecture: x86
          [platform] build instruction set architectures: x86_32
          [platform] host operating system: linux
          [platform] host distribution: ubuntu
          [platform] host distribution version: 10.10
          [platform] host processor architecture: x86
          [platform] host instruction set architectures: x86_32
              [path] prefix: /opt/project-name
              [path] exec prefix: /opt/project-name
              [path] library dir (x86_32): /opt/project-name/lib
              [path] include dir: /opt/project-name/include
              [path] binary dir: /opt/project-name/bin
              [path] system binary dir: /opt/project-name/sbin
              [path] system config dir: /opt/project-name/etc
              [path] local state dir: /opt/project-name/var
              [path] data root dir: /opt/project-name/share
              [path] data dir: /opt/project-name/share/project-name
              [path] documenation dir: /opt/project-name/share/doc/project-name
              [path] HTML documentation dir: /opt/project-name/share/doc/project-name/html
              [path] manpage dir: /opt/project-name/share/man
          [compiler] default C compiler: gcc
          [compiler] default C preprocessor flags: 
          [compiler] default C compiler flags: -O2 -g
          [compiler] default linker flags: -O2 -g
          [compiler] C compiler (build/x86_32): gcc -m32
          [compiler] C preprocessor flags (build/x86_32): 
          [compiler] C compiler flags (build/x86_32): -O2 -g
          [compiler] linker flags (build/x86_32): -O2 -g
          [compiler] C compiler (host/x86_32): gcc -m32
          [compiler] C preprocessor flags (host/x86_32): 
          [compiler] C compiler flags (host/x86_32): -O2 -g
          [compiler] linker flags (host/x86_32): -O2 -g
      [project-name] configuration file: /opt/project-name/etc/project-name.conf

You can see the full set of options to configure by passing --help:

$ ../configure --help
Usage: makekit configure [ option | @settings_file ] ...
Options:
  --sourcedir=path            Source directory [.]
  --objectdir=path            Intermediate file directory [object]
  --stagedir=path             Staging directory [stage]
  --rundir=path               Build tool install directory [run]
  --show-vars=yes|no          Always show options as variable names in help
                              output [no]
  --help=yes|no               Show this help [yes]

Options (platform):
  --build-os=value            Build operating system [linux]
  --build-arch=value          Build CPU architecture [x86]
  --build-isas=value          Build instruction set architectures [x86_32]
  --build-distro=value        Build operating system distribution [ubuntu]
  --build-distro-version=value
                              Build operating system distribution version
                              [10.10]
  --build-distro-archetype=value
                              Build operating system distribution archetype
                              [debian]
  --host-os=value             Host operating system [linux]
  --host-arch=value           Host CPU architecture [x86]
  --host-isas=value           Host instruction set architectures [x86_32]
  --host-distro=value         Host operating system distribution [ubuntu]
  --host-distro-version=value Host operating system distribution version [10.10]
  --host-distro-archetype=value
                              Host operating system distribution archetype
                              [debian]

Options (core):
  --fail-on-warn=yes|no       Fail on warnings [no]
  --debug=yes|no              Build in debug mode [no]

Options (path):
  --prefix=path               Architecture-independent installation prefix
                              [/opt/project-name]
  --exec-prefix=path          Architecture-dependent installation prefix
                              [/opt/project-name]
  --libdir=path               Library directory (x86_32) [/opt/project-name/lib]
  --includedir=path           Header file directory [/opt/project-name/include]
  --bindir=path               User executable directory [/opt/project-name/bin]
  --sbindir=path              System executable directory
                              [/opt/project-name/sbin]
  --libexecdir=path           Program executable directory
                              [/opt/project-name/libexec]
  --sysconfdir=path           System configuration directory
                              [/opt/project-name/etc]
  --localstatedir=path        Local state directory [/opt/project-name/var]
  --datarootdir=path          Root data directory [/opt/project-name/share]
  --datadir=path              Data directory
                              [/opt/project-name/share/project-name]
  --docdir=path               Documentation directory
                              [/opt/project-name/share/doc/project-name]
  --htmldir=path              HTML documentation directory
                              [/opt/project-name/share/doc/project-name/html]
  --mandir=path               UNIX man page directory
                              [/opt/project-name/share/man]

Options (compiler):
  CC=program                  Default C compiler [gcc]
  CPPFLAGS=flags              Default C preprocessor flags []
  CFLAGS=flags                Default C compiler flags [-O2 -g]
  LDFLAGS=flags               Default linker flags [-O2 -g]
  BUILD_X86_32_CC=program     C compiler (build/x86_32) [gcc -m32]
  BUILD_X86_32_CPPFLAGS=flags C preprocessor flags (build/x86_32) []
  BUILD_X86_32_CFLAGS=flags   C compiler flags (build/x86_32) [-O2 -g]
  BUILD_X86_32_LDFLAGS=flags  Linker flags (build/x86_32) [-O2 -g]
  HOST_X86_32_CC=program      C compiler (host/x86_32) [gcc -m32]
  HOST_X86_32_CPPFLAGS=flags  C preprocessor flags (host/x86_32) []
  HOST_X86_32_CFLAGS=flags    C compiler flags (host/x86_32) [-O2 -g]
  HOST_X86_32_LDFLAGS=flags   Linker flags (host/x86_32) [-O2 -g]

Options (project-name):
  --config-file=path          Configuration file path
                              [/opt/project-name/etc/project-name.conf]

Note that this includes options for MakeKit itself, all modules which were used (either directly or indirectly), and finally options from the project.

Building

At this point, the project is ready to be built by running make. When complete, all end products will have been placed in the stage directory, which has an internal structure mirroring the UNIX root filesystem. From here, the products can be copied into place or packaged up using your favorite packaging format. MakeKit also generates the following set of convenience targets to aid in your workflow:

make subdir

Builds products only from subdir and any of its transitive subdirectories. For example, make doc or make src.

make clean

Removes all built intermediate products while leaving end products (anything in stage) untouched.

make SUBDIR=subdir clean

Removes all built intermediate products only from subdir and any of its transitive subdirectories.

make scrub

Removes everything that make clean removes, plus all end products in the stage directory.

make scour

Completely removes all generated files from the build area, including all intermediate and end products and anything generated by running configure.

make install

Installs all end products built so far into their final locations on the filesystem. You will generally need root permissions to do this.

make DESTDIR=dest install

Installs all end products built so far into dest rather than /. This might be useful for creating a package or binary tarball. Of course, you could just tar up stage directly...