# C Language Projects

This chapter will show you how to use MakeKit to configure and build a C language project.

## Building Programs

Programs are built using the `` function, which requires at a minimum a list of source files and a program name. The program name is passed as the `PROGRAM` parameter, and the list of sources files is passed as the `SOURCES` parameter. Consider for a simple "Hello, World" program `hello`, it's source file `main.c`, and the `MakeKitBuild` file to build it.

### Using `mk_program`

`main.c`

      #include <stdio.h>

      int main(int argc, char** argv)
      {
          printf("Hello, world!\n");
      }

`MakeKitBuild`

      make()
      {
          mk_program \
              PROGRAM=hello \
              SOURCES="main.c"
      }

By default, the program will be installed in the usual binary directory (e.g. `/usr/local/bin`). The install location can be changed with the `INSTALLDIR` parameter to `mk_program`.

### Finding Header Files

A C project with a complex layout will typically need to look for headers from multiple directories within the source tree. Using the `INCLUDEDIRS` parameter to `mk_program`, you can specify a list of directories where the compiler should look for header files. Adding a header file `include/values.h` to our small example project and modifying `MakeKitBuild` to find it yields .

#### Using `INCLUDEDIRS`

`include/values.h`

      #define MESSAGE "Hello, World!"

`main.c`

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

      int main(int argc, char** argv)
      {
          printf("%s\n", MESSAGE);
      }

`MakeKitBuild`

      make()
      {
          mk_program \
              PROGRAM=hello \
              SOURCES="main.c" \
              INCLUDEDIRS="include"
      }

### Linking To Libraries

To link your program to other libraries, specify them as a list via the `LIBDEPS` parameter. Each library should be specified exactly as you would with `-l` to the C compiler (without the `lib` prefix). See , which links to `libm` to use the `sqrt()` function.

#### Using `LIBDEPS`

`main.c`

      #include <stdio.h>
      #include <math.h>
      #include "values.h"

      int main(int argc, char** argv)
      {
          printf("%s\n", MESSAGE);
          printf("The square root of 2.0 is %f\n", sqrt(2.0));
      }

`MakeKitBuild`

      make()
      {
          mk_program \
              PROGRAM=hello \
              SOURCES="main.c" \
              INCLUDEDIRS="include" \
              LIBDEPS="m"
      }

## Building Libraries

MakeKit allows you to build two types of libraries:

Shared libraries

  A shared library is a collection of address-relocatable code which is combined with the program that links against it at startup by the system dynamic linker. Shared libraries are built with the `mk_library` function.

Dynamically loadable objects

  A dynamically loadable object is a blob of code which is explicitly loaded and executed by a program through functions like `dlopen()`. Dynamically loadable objects are typically used to implement plugins. Although on many UNIX systems dynamically loadable objects and shared libraries are the same type of file, some systems distinguish between the two. For example, Mac OS X distinguishes between `.dylib` (shared libraries) and `.bundle` or `.so` (dynamically loadable objects). To build a dynamically loadable object, use the `mk_dlo` function, which takes nearly identical parameters to `mk_library`.

As with building programs, building a library requires a library name, passed as the `LIB` parameter, and a list of sources, passed as the `SOURCES` parameter. The library name should be specified without a filename extension or the `lib` prefix. Consider , which expands on previous examples by building a small library and linking it into the program `hello`.

### Using `mk_library`

`include/example.h`

      external unsigned long factorial(unsigned long i);

`main.c`

      #include <stdio.h>
      #include <math.h>
      #include "values.h"
      #include "example.h"

      int main(int argc, char** argv)
      {
          printf("%s\n", MESSAGE);
          printf("The square root of 2.0 is %f\n", sqrt(2.0));
          printf("5! is %lu\n", factorial(5));
      }

`example.c`

      #include "example.h"

      unsigned long factorial(unsigned long i)
      {
          return i == 1 ? 1 : i * factorial(i - 1);
      }

`MakeKitBuild`

      make()
      {
          mk_library \
              LIB=example \
              SOURCES="example.c" \
              INCLUDEDIRS="include"

          mk_program \
              PROGRAM=hello \
      	SOURCES="main.c" \
              INCLUDEDIRS="include" \
              LIBDEPS="m example"
      }

As you can see, `mk_library` accepts many of the same parameters as `mk_program`, including `INCLUDEDIRS`. As with programs, libraries can specify dependencies on other shared libraries with `LIBDEPS`.

Some systems do not allow inter-dependencies between shared libraries. On those systems, MakeKit will propagate dependencies forward to any program that links against them. The resulting program may explicitly depend on libraries from which it consumes no symbols.

Our new library was linked into `hello` the same way as the system library `libm`. Note that the order of the two function invocations in `MakeKitBuild` is significant. Because `mk_program` was called after `mk_library`, it knows that `libexample` is a library we build ourselves rather than pre-existing on the system, and it adds appropriate dependencies to the generated `Makefile`.

## Groups

When a library or program grows large enough, it is often prudent to break its source code up across multiple subdirectories to enhance organizational clarity and separation of concerns. Rather than maintaining a long list of source files from disparate subdirectories in your invocation of `mk_library` or `mk_program`, you can use the `mk_group` function to group together a set of source files and compiler flags into a logical unit which can then be added in to your program or library. This feature is an analogue of "convenience libraries" available in GNU `libtool`.

Imagine that the currently diminuitive `libexample` is destined to become a vast utility library with both math and string manipulation functions. In anticipation of this, we break it into two subdirectories, `math` and `string`. We will build each subdirectory as an object group and then combine the two into `libexample` using the `GROUPS` parameter to `mk_library`. This yields .

### Using `mk_group`

`include/example.h`

      /* Math functions */
      external unsigned long factorial(unsigned long i);
      /* String functions */
      external void reverse_string(char* str);

`math/factorial.c`

      #include "example.h"

      unsigned long factorial(unsigned long i)
      {
          return i == 1 ? 1 : i * factorial(i - 1);
      }

`math/MakeKitBuild`

      make()
      {
          mk_group \
              GROUP=math \
              SOURCES="*.c" \
              INCLUDEDIRS="../include"
      }

`string/reverse.c`

      #include <string.h>
      #include "example.h"

      unsigned void reverse_string(char* str)
      {
          char* front = NULL;
          char* back = NULL;
          char tmp = 0;

          for (front = str, back = str + strlen(str) - 1;
               back > front;
               front++, back--)
          {
              tmp = *front;
              *front = *back;
              *back = tmp;
          }
      }

`string/MakeKitBuild`

      make()
      {
          mk_group \
              GROUP=string \
              SOURCES="*.c" \
              INCLUDEDIRS="../include"
      }

`main.c`

      #include <stdio.h>
      #include <math.h>
      #include "values.h"
      #include "example.h"

      int main(int argc, char** argv)
      {
          char reverse[] = MESSAGE;

          reverse_string(reverse);

          printf("%s\n", MESSAGE);
          printf("Reversed: %s\n", reverse);
          printf("The square root of 2.0 is %f\n", sqrt(2.0));
          printf("5! is %lu\n", factorial(5));
      }

`MakeKitBuild`

      SUBDIRS="math string"

      make()
      {
          mk_library \
              LIB=example \
              GROUPS="math/math string/string"

          mk_program \
              PROGRAM=hello \
      	SOURCES="main.c" \
              INCLUDEDIRS="include" \
              LIBDEPS="m example"
      }

The `mk_group` function accepts nearly the same set of optional parameters as `mk_library`; in fact, you can even use `GROUPDEPS` to combine object groups together hierachically.

This particular example also demonstrates a useful shortcut enabled by keeping source files neatly separated into subdirectories: filename patterns. The invocations of `mk_group` in the `math` and `string` subdirectories can include all C source files with `*.c`. In general, MakeKit supports filename patterns whenever specifying source files.

Filename patterns only work against files that exist at the time that `configure` is run. Files that are created by a build rule will not be included.

## Configuration Tests

Most projects of any significant complexity will need to determine how and what source code to build based on the attributes of the host operating system, available libraries, and so forth. To this end, MakeKit provides an array of functions to use in `configure()` within your `MakeKitBuild` files.

The idioms used to configure projects with MakeKit are very similar to `autoconf`'s. This was an intentional design decision to ease migration.

Most configuration tests result in `#define` statements being written into a config header which you can include in your source files to make use of the test results. You can set the current config header with `mk_config_header`. You can add `#define` statements to the current config header with `mk_define`:

### Using `mk_config_header`

    configure()
    {
        # Place #defines in include/config.h relative
        # to location of MakeKitBuild in source tree
        mk_config_header "include/config.h"

        # Define FOOBAR to 1
        mk_define FOOBAR 1

        # Define BUILD_TIME to current time as a string
        # using UNIX 'date' program
        mk_define BUILD_TIME "\"`date`\""
    }

### Test Basics

Most configuration checks do one or more of the following:

- Display the test being performed to the user
- Perform a test using the C compiler
- Set a variable to the result of the test
- Add a define to the config header with the result of the test
- Cache the results to speed up subsequent runs of `configure`
- Display the result of the test to the user

The names of variables and defines are usually based on the item in question (e.g. the name of the header or library), with a few transformations:

- All lowercase letters are converted to uppercase
- All `*` (asterisk) characters are converted to `P`.
- All other characters are converted to _.

These rules are the same as those used in `autoconf`. Hereafter, a name derived by these rules will be referred to as a varname.

Most of the configuration checks have two forms, singular and plural. The plural form takes a list of items and performs the above steps for each item, and is the most common and convenient way of performing a check. The singular form takes only a single item has none of the side effects of the plural form. Instead, it sets the variable `result` to the result of the test, and returns 0 (logical true in shell script) if the test succeeded, and 1 (logical false) otherwise. This form is useful for writing more elaborate custom tests.

Many of the tests have 3 possible results: `internal`, `external`, and `no`. If the item being checked for is provided by the current project -- for example, checking for a library which is built in a preceding source directory -- the test will trivially succeed with `internal`. If the item was identified as being provided by the operating system or build environment, such as a system header, the result will be `external`. If the item was not found, the result will be `no`.

### Checking For Headers

To check for the presence of a header on the system, use `mk_check_headers`. This function takes a list of header names and tests for the presence and usability of each one by attempting to compile a small program that includes it. For each header, it will do the following:

- Set the variable `HAVE_varname` to the result of the test.
- If the header was found, define `HAVE_varname`

#### Using `mk_check_headers`

    configure()
    {
        mk_config_header "include/config.h"

        # Check for some basic header files
        mk_check_headers stdio.h stdlib.h string.h does-not-exist.h

        # On a reasonable system, the following variables will be set:
        #
        # HAVE_STDIO_H=external
        # HAVE_STDLIB_H=external
        # HAVE_STRING_H=external
        # HAVE_DOES_NOT_EXIST_H=no
        #
        # The following defines will be written into config.h:
        #
        # #define HAVE_STDIO_H
        # #define HAVE_STDLIB_H
        # #define HAVE_STRING_H
    }

If these side effects are undesirable, you can use the singular form `mk_check_header`.

### Checking For Libraries

To check for the presence of a library, use `mk_check_libraries`. For each library name specified, it will test for its presence and usability by attempting to compile and link a test program against it. For each library it will do the following:

- Set the variable `HAVE_LIB_varname` to the result of the test.
- If the test succeeds, export the variable `LIB_varname` with the name of the library exactly as passed to `mk_check_libraries`. Otherwise, the variable will be set as the empty string. This is useful for conditionally including the library in a `LIBDEPS=` list to `mk_program` and friends.
- If the library was found, define `HAVE_LIB_varname`.

Library names should be specified the same way as when linking to them with `mk_program`, with no `lib` prefix or file extension.

#### Using `mk_check_headers`

    configure()
    {
        mk_config_header "include/config.h"

        # Check for some common UNIX libraries
        mk_check_libraries m pthread does-not-exist

        # On a typical Linux system, the following variables will be set:
        #
        # HAVE_LIB_M=external
        # HAVE_LIB_PTHREAD=external
        # HAVE_LIB_DOES_NOT_EXIST=no
        # LIB_M=m
        # LIB_PTHREAD=pthread
        # LIB_DOES_NOT_EXIST=
        #
        # The following defines will be written into config.h:
        #
        # #define HAVE_LIB_M
        # #define HAVE_LIB_PTHREAD
    }

If these side effects are undesirable, you can use the singular form `mk_check_library`.

### Checking For Functions

`mk_check_functions` allows you to test for the availability of a list of functions by attempting to compile and link test programs that reference them. For each function it will then do the following:

- Set the variable `HAVE_varname` to the result of the test. The possible results are `yes` and `no`.
- If the function was found, define `HAVE_varname`.
- If the function was found, define `HAVE_DECL_varname` to 1. Otherwise, define it to 0.

Unlike `autoconf`, this check ensures that a declaration of the function was available in addition to the function's symbol being present in a library. You can use `HEADERDEPS=` and `LIBDEPS=` parameters to `mk_check_functions` to specify header files and libraries that are necessary for the function to be found.

Each function can be specified as a simple name, or as a full function prototype in C syntax. In the second case, MakeKit will verify that the function exists and has a compatible prototype. Bear in mind that the resulting variable/define name will reflect any whitespace in your prototype.

#### Using `mk_check_functions`

    configure()
    {
        mk_config_header "include/config.h"

        # Check for some functions in libm
        mk_check_functions \
            HEADERDEPS="math.h" \
            LIBDEPS="m" \
            sin cos "double sqrt(double)" does_not_exist

        # On a typical UNIX system, the following variables will be set:
        #
        # HAVE_SIN=yes
        # HAVE_COS=yes
        # HAVE_DOUBLE_SQRT__DOUBLE_=yes
        # HAVE_DOES_NOT_EXIST=no
        #
        # The following defines will be written into config.h:
        #
        # #define HAVE_SIN
        # #define HAVE_COS
        # #define HAVE_DOUBLE_SQRT__DOUBLE_
        # #define HAVE_DECL_SIN 1
        # #define HAVE_DECL_COS 1
        # #define HAVE_DECL_DOUBLE_SQRT__DOUBLE_ 1
        # #define HAVE_DECL_DOES_NOT_EXIST 0
    }

If these side effects are undesirable, you can use the singular form `mk_check_function`.

### Checking For Types

`mk_check_types` allows you to test for the availability of a list of types by attempting to compile test programs that reference them. For each type it will then do the following:

- Set the variable `HAVE_varname` to the result of the test. The possible results are `yes` and `no`.
- If the type was found, define `HAVE_varname`.

You can use a `HEADERDEPS=` parameter to `mk_check_types` to specify header files necessary to find the type definition.

#### Using `mk_check_types`

    configure()
    {
        mk_config_header "include/config.h"

        # Check for some types in stdlib.h and inttypes.h
        mk_check_types \
            HEADERDEPS="stdlib.h inttypes.h" \
            size_t int64_t uint64_t does_not_exist_t

        # On a typical UNIX system, the following variables will be set:
        #
        # HAVE_SIZE_T=yes
        # HAVE_INT64_T=yes
        # HAVE_UINT64_T=yes
        # HAVE_DOES_NOT_EXIST_T=no
        #
        # The following defines will be written into config.h:
        #
        # #define HAVE_SIZE_T
        # #define HAVE_INT64_T
        # #define HAVE_UINT64_T
    }

If these side effects are undesirable, you can use the singular form `mk_check_type`.

### Checking For Sizes of Types

`mk_check_sizeofs` allows you to test for the sizes of a list of types. For each type it will then do the following:

- Set the variable `SIZEOF_varname` to the result of the test.
- Define `SIZEOF_varname` to the result of the test.

You can use a `HEADERDEPS=` parameter to `mk_check_types` to specify header files necessary to find the type definitions. Note that if this function fails to determine the size of a type it will abort configuration, so you might want to first check that the types exist with `mk_check_types`.

#### Using `mk_check_sizeofs`

    configure()
    {
        mk_config_header "include/config.h"

        # Check for sizes of some types
        mk_check_types \
            HEADERDEPS="stdlib.h inttypes.h" \
            size_t int64_t "void*"

        # On a typical 32-bit UNIX system, the following variables will be set:
        #
        # SIZEOF_SIZE_T=4
        # SIZEOF_INT64_T=8
        # SIZEOF_VOIDP=4
        #
        # The following defines will be written into config.h:
        #
        # #define SIZEOF_SIZE_T 4
        # #define SIZEOF_INT64_T 8
        # #define SIZEOF_VOIDP 4
    }

If these side effects are undesirable, you can use the singular form `mk_check_sizeof`.