Docs GODI Archive
Projects Blog Link DB

Search GODI:


More options
File doc/godi-omake/html/omake-doc.html GODI Package godi-omake
 
   omake-doc.html    Sources  

Jump to:  OMake Home • Guide Home • Guide (single-page) • Contents (short) • Contents (long)
Index:  All • Variables • Functions • Objects • Targets • Options

The OMake user guide and reference manual

Jason Hickey, Aleksey Nogin, et. al.

7thAugust, 2007

All the documentation on a single page

OMake table of contents

Contents

Chapter 1  Guide

If you are new to OMake, you the omake-quickstart presents a short introduction that describes how to set up a project. The omake-build-examples gives larger examples of build projects, and omake-language-examples presents programming examples.

Quickstart 2
A quickstart guide to using omake.
Build examples 3
Advanced build examples.
The OMake language 4
The omake language, including a description of objects, expressions, and values.
Variables and naming 5
Variables, names, and environments.
Language discussion 6
Further discussion on the language, including scoping, evaluation, and objects.
Language examples 7
Additional language examples.
Build rules 8
Defining and using rules to build programs.
Base builtin functions 9
Functions and variables in the core standard library.
System functions 10
Functions on files, input/output, and system commands.
Shell commands 11
Using the omake shell for command-line interpretation.
The standard objects 12
Pervasives defines the built-in objects.
Standard build definitions 13
The build specifications for programming languages in the OMake standard library.
Standard autoconfiguration functions and variables 14
The utilities provoded by the OMake standard library to simplify programming of autoconfiguration tests.
The interactive command interpreter 15
The osh command-line interpreter.
Appendices
OMake command-line options A
Command-line options for omake.
The OMake language grammar B
A more precise specification of the OMake language.
All the documentation on a single page
All the OMake documentation in a single page.

Chapter 2  OMake quickstart guide

2.1  Description

omake is designed for building projects that might have source files in several directories. Projects are normally specified using an OMakefile in each of the project directories, and an OMakeroot file in the root directory of the project. The OMakeroot file specifies general build rules, and the OMakefiles specify the build parameters specific to each of the subdirectories. When omake runs, it walks the configuration tree, evaluating rules from all of the OMakefiles. The project is then built from the entire collection of build rules.

2.1.1  Automatic dependency analysis

Dependency analysis has always been problematic with the make(1) program. omake addresses this by adding the .SCANNER target, which specifies a command to produce dependencies. For example, the following rule

    .SCANNER: %.o: %.c
        $(CC) $(INCLUDE) -MM $<

is the standard way to generate dependencies for .c files. omake will automatically run the scanner when it needs to determine dependencies for a file.

2.1.2  Content-based dependency analysis

Dependency analysis in omake uses MD5 digests to determine whether files have changed. After each run, omake stores the dependency information in a file called .omakedb in the project root directory. When a rule is considered for execution, the command is not executed if the target, dependencies, and command sequence are unchanged since the last run of omake. As an optimization, omake does not recompute the digest for a file that has an unchanged modification time, size, and inode number.

2.2  For users already familiar with make

For users already familiar with the make(1) command, here is a list of differences to keep in mind when using omake.

  • In omake, you are much less likely to define build rules of your own. The system provides many standard functions (like StaticCLibrary and CProgram), described in Chapter 13, to specify these builds more simply.
  • Implicit rules using .SUFFIXES and the .suf1.suf2: are not supported. You should use wildcard patterns instead %.suf2: %.suf1.
  • Scoping is significant: you should define variables and .PHONY targets (see Section 8.10) before they are used.
  • Subdirectories are incorporated into a project using the .SUBDIRS: target (see Section 8.8).

2.3  Building a small C program

To start a new project, the easiest method is to change directories to the project root and use the command omake --install to install default OMakefiles.

    $ cd ~/newproject
    $ omake --install
    *** omake: creating OMakeroot
    *** omake: creating OMakefile
    *** omake: project files OMakefile and OMakeroot have been installed
    *** omake: you should edit these files before continuing

The default OMakefile contains sections for building C and OCaml programs. For now, we'll build a simple C project.

Suppose we have a C file called hello_code.c containing the following code:

    #include <stdio.h>

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

To build the program a program hello from this file, we can use the CProgram function. The OMakefile contains just one line that specifies that the program hello is to be built from the source code in the hello_code.c file (note that file suffixes are not passed to these functions).

    CProgram(hello, hello_code)

Now we can run omake to build the project. Note that the first time we run omake, it both scans the hello_code.c file for dependencies, and compiles it using the cc compiler. The status line printed at the end indicates how many files were scanned, how many were built, and how many MD5 digests were computed.

    $ omake hello
    *** omake: reading OMakefiles
    *** omake: finished reading OMakefiles (0.0 sec)
    - scan . hello_code.o
    + cc -I. -MM hello_code.c
    - build . hello_code.o
    + cc -I. -c -o hello_code.o hello_code.c
    - build . hello
    + cc -o hello hello_code.o
    *** omake: done (0.5 sec, 1/6 scans, 2/6 rules, 5/22 digests)
    $ omake
    *** omake: reading OMakefiles
    *** omake: finished reading OMakefiles (0.1 sec)
    *** omake: done (0.1 sec, 0/4 scans, 0/4 rules, 0/9 digests)

If we want to change the compile options, we can redefine the CC and CFLAGS variables before the CProgram line. In this example, we will use the gcc compiler with the -g option. In addition, we will specify a .DEFAULT target to be built by default. The EXE variable is defined to be .exe on Win32 systems; it is empty otherwise.

    CC = gcc
    CFLAGS += -g
    CProgram(hello, hello_code)
    .DEFAULT: hello$(EXE)

Here is the corresponding run for omake.

    $ omake
    *** omake: reading OMakefiles
    *** omake: finished reading OMakefiles (0.0 sec)
    - scan . hello_code.o
    + gcc -g -I. -MM hello_code.c
    - build . hello_code.o
    + gcc -g -I. -c -o hello_code.o hello_code.c
    - build . hello
    + gcc -g -o hello hello_code.o
    *** omake: done (0.4 sec, 1/7 scans, 2/7 rules, 3/22 digests)

We can, of course, include multiple files in the program. Suppose we write a new file hello_helper.c. We would include this in the project as follows.

    CC = gcc
    CFLAGS += -g
    CProgram(hello, hello_code hello_helper)
    .DEFAULT: hello$(EXE)

2.4  Larger projects

As the project grows it is likely that we will want to build libraries of code. Libraries can be built using the StaticCLibrary function. Here is an example of an OMakefile with two libraries.

    CC = gcc
    CFLAGS += -g

    FOO_FILES = foo_a foo_b
    BAR_FILES = bar_a bar_b bar_c

    StaticCLibrary(libfoo, $(FOO_FILES))
    StaticCLibrary(libbar, $(BAR_FILES))

    # The hello program is linked with both libraries
    LIBS = libfoo libbar
    CProgram(hello, hello_code hello_helper)

    .DEFAULT: hello$(EXE)

2.5  Subdirectories

As the project grows even further, it is a good idea to split it into several directories. Suppose we place the libfoo and libbar into subdirectories.

In each subdirectory, we define an OMakefile for that directory. For example, here is an example OMakefile for the foo subdirectory.

    INCLUDES += .. ../bar

    FOO_FILES = foo_a foo_b
    StaticCLibrary(libfoo, $(FOO_FILES))

Note the the INCLUDES variable is defined to include the other directories in the project.

Now, the next step is to link the subdirectories into the main project. The project OMakefile should be modified to include a .SUBDIRS: target.

    # Project configuration
    CC = gcc
    CFLAGS += -g

    # Subdirectories
    .SUBDIRS: foo bar

    # The libraries are now in subdirectories
    LIBS = foo/libfoo bar/libbar

    CProgram(hello, hello_code hello_helper)

    .DEFAULT: hello$(EXE)

Note that the variables CC and CFLAGS are defined before the .SUBDIRS target. These variables remain defined in the subdirectories, so that libfoo and libbar use gcc -g.

If the two directories are to be configured differently, we have two choices. The OMakefile in each subdirectory can be modified with its configuration (this is how it would normally be done). Alternatively, we can also place the change in the root OMakefile.

    # Default project configuration
    CC = gcc
    CFLAGS += -g

    # libfoo uses the default configuration
    .SUBDIRS: foo

    # libbar uses the optimizing compiler
    CFLAGS += -O3
    .SUBDIRS: bar

    # Main program
    LIBS = foo/libfoo bar/libbar
    CProgram(hello, hello_code hello_helper)

    .DEFAULT: hello$(EXE)

Note that the way we have specified it, the CFLAGS variable also contains the -O3 option for the CProgram, and hello_code.c and hello_helper.c file will both be compiled with the -O3 option. If we want to make the change truly local to libbar, we can put the bar subdirectory in its own scope using the section form.

    # Default project configuration
    CC = gcc
    CFLAGS += -g

    # libfoo uses the default configuration
    .SUBDIRS: foo

    # libbar uses the optimizing compiler
    section
        CFLAGS += -O3
        .SUBDIRS: bar

    # Main program does not use the optimizing compiler
    LIBS = foo/libfoo bar/libbar
    CProgram(hello, hello_code hello_helper)

    .DEFAULT: hello$(EXE)

Later, suppose we decide to port this project to Win32, and we discover that we need different compiler flags and an additional library.

    # Default project configuration
    if $(equal $(OSTYPE), Win32)
        CC = cl /nologo
        CFLAGS += /DWIN32 /MT
        export
    else
        CC = gcc
        CFLAGS += -g
        export

    # libfoo uses the default configuration
    .SUBDIRS: foo

    # libbar uses the optimizing compiler
    section
        CFLAGS += $(if $(equal $(OSTYPE), Win32), $(EMPTY), -O3)
        .SUBDIRS: bar

    # Default libraries
    LIBS = foo/libfoo bar/libbar

    # We need libwin32 only on Win32
    if $(equal $(OSTYPE), Win32)
       LIBS += win32/libwin32

       .SUBDIRS: win32
       export

    # Main program does not use the optimizing compiler
    CProgram(hello, hello_code hello_helper)

    .DEFAULT: hello$(EXE)

Note the use of the export directives to export the variable definitions from the if-statements. Variables in omake are scoped—variables in nested blocks (blocks with greater indentation), are not normally defined in outer blocks. The export directive specifies that the variable definitions in the nested blocks should be exported to their parent block.

Finally, for this example, we decide to copy all libraries into a common lib directory. We first define a directory variable, and replace occurrences of the lib string with the variable.

    # The common lib directory
    LIB = $(dir lib)

    # phony target to build just the libraries
    .PHONY: makelibs

    # Default project configuration
    if $(equal $(OSTYPE), Win32)
        CC = cl /nologo
        CFLAGS += /DWIN32 /MT
        export
    else
        CC = gcc
        CFLAGS += -g
        export

    # libfoo uses the default configuration
    .SUBDIRS: foo

    # libbar uses the optimizing compiler
    section
        CFLAGS += $(if $(equal $(OSTYPE), Win32), $(EMPTY), -O3)
        .SUBDIRS: bar

    # Default libraries
    LIBS = $(LIB)/libfoo $(LIB)/libbar

    # We need libwin32 only on Win32
    if $(equal $(OSTYPE), Win32)
       LIBS += $(LIB)/libwin32

       .SUBDIRS: win32
       export

    # Main program does not use the optimizing compiler
    CProgram(hello, hello_code hello_helper)

    .DEFAULT: hello$(EXE)

In each subdirectory, we modify the OMakefiles in the library directories to install them into the $(LIB) directory. Here is the relevant change to foo/OMakefile.

    INCLUDES += .. ../bar

    FOO_FILES = foo_a foo_b
    StaticCLibraryInstall(makelib, $(LIB), libfoo, $(FOO_FILES))

Directory (and file names) evaluate to relative pathnames. Within the foo directory, the $(LIB) variable evaluates to ../lib.

As another example, instead of defining the INCLUDES variable separately in each subdirectory, we can define it in the toplevel as follows.

    INCLUDES = $(ROOT) $(dir foo bar win32)

In the foo directory, the INCLUDES variable will evaluate to the string .. . ../bar ../win32. In the bar directory, it would be .. ../foo . ../win32. In the root directory it would be . foo bar win32.

2.6  Other things to consider

omake also handles recursive subdirectories. For example, suppose the foo directory itself contains several subdirectories. The foo/OMakefile would then contain its own .SUBDIRS target, and each of its subdirectories would contain its own OMakefile.

2.7  Building OCaml programs

By default, omake is also configured with functions for building OCaml programs. The functions for OCaml program use the OCaml prefix. For example, suppose we reconstruct the previous example in OCaml, and we have a file called hello_code.ml that contains the following code.

   open Printf

   let () = printf "Hello world\n"

An example OMakefile for this simple project would contain the following.

    # Use the byte-code compiler
    BYTE_ENABLED = true
    NATIVE_ENABLED = false
    OCAMLCFLAGS += -g

    # Build the program
    OCamlProgram(hello, hello_code)
    .DEFAULT: hello.run

Next, suppose the we have two library subdirectories: the foo subdirectory is written in C, the bar directory is written in OCaml, and we need to use the standard OCaml Unix module.

    # Default project configuration
    if $(equal $(OSTYPE), Win32)
        CC = cl /nologo
        CFLAGS += /DWIN32 /MT
        export
    else
        CC = gcc
        CFLAGS += -g
        export

    # Use the byte-code compiler
    BYTE_ENABLED = true
    NATIVE_ENABLED = false
    OCAMLCFLAGS += -g

    # library subdirectories
    INCLUDES += $(dir foo bar)
    OCAMLINCLUDES += $(dir foo bar)
    .SUBDIRS: foo bar

    # C libraries
    LIBS = foo/libfoo

    # OCaml libraries
    OCAML_LIBS = bar/libbar

    # Also use the Unix module
    OCAML_OTHER_LIBS = unix

    # The main program
    OCamlProgram(hello, hello_code hello_helper)

    .DEFAULT: hello

The foo/OMakefile would be configured as a C library.

    FOO_FILES = foo_a foo_b
    StaticCLibrary(libfoo, $(FOO_FILES))

The bar/OMakefile would build an ML library.

   BAR_FILES = bar_a bar_b bar_c
   OCamlLibrary(libbar, $(BAR_FILES))

2.8  The OMakefile and OMakeroot files

OMake uses the OMakefile and OMakeroot files for configuring a project. The syntax of these files is the same, but their role is slightly different. For one thing, every project must have exactly one OMakeroot file in the project root directory. This file serves to identify the project root, and it contains code that sets up the project. In contrast, a multi-directory project will often have an OMakefile in each of the project subdirectories, specifying how to build the files in that subdirectory.

Normally, the OMakeroot file is boilerplate. The following listing is a typical example.

    include $(STDLIB)/build/Common
    include $(STDLIB)/build/C
    include $(STDLIB)/build/OCaml
    include $(STDLIB)/build/LaTeX

    # Redefine the command-line variables
    DefineCommandVars(.)

    # The current directory is part of the project
    .SUBDIRS: .

The include lines include the standard configuration files needed for the project. The $(STDLIB) represents the omake library directory. The only required configuration file is Common. The others are optional; for example, the $(STDLIB)/build/OCaml file is needed only when the project contains programs written in OCaml.

The DefineCommandVars function defines any variables specified on the command line (as arguments of the form VAR=<value>). The .SUBDIRS line specifies that the current directory is part of the project (so the OMakefile should be read).

Normally, the OMakeroot file should be small and project-independent. Any project-specific configuration should be placed in the OMakefiles of the project.

2.9  Multiple version support

OMake version 0.9.6 introduced preliminary support for multiple, simultaneous versions of a project. Versioning uses the vmount(dir1, dir2) function, which defines a “virtual mount” of directory dir1 over directory dir2. A “virtual mount” is like a transparent mount in Unix, where the files from dir1 appear in the dir2 namespace, but new files are created in dir2. More precisely, the filename dir2/foo refers to: a) the file dir1/foo if it exists, or b) dir2/foo otherwise.

The vmount function makes it easy to specify multiple versions of a project. Suppose we have a project where the source files are in the directory src/, and we want to compile two versions, one with debugging support and one optimized. We create two directories, debug and opt, and mount the src directory over them.

    section
        CFLAGS += -g
        vmount(-l, src, debug)
        .SUBDIRS: debug

    section
        CFLAGS += -O3
        vmount(-l, src, opt)
        .SUBDIRS: opt

Here, we are using section blocks to define the scope of the vmount—you may not need them in your project.

The -l option is optional. It specifies that files form the src directory should be linked into the target directories (or copied, if the system is Win32). The links are added as files are referenced. If no options are given, then files are not copied or linked, but filenames are translated to refer directly to the src/ files.

Now, when a file is referenced in the debug directory, it is linked from the src directory if it exists. For example, when the file debug/OMakefile is read, the src/OMakefile is linked into the debug/ directory.

The vmount model is fairly transparent. The OMakefiles can be written as if referring to files in the src/ directory—they need not be aware of mounting. However, there are a few points to keep in mind.

2.10  Notes

  • When using the vmount function for versioning, it wise to keep the source files distinct from the compiled versions. For example, suppose the source directory contained a file src/foo.o. When mounted, the foo.o file will be the same in all versions, which is probably not what you want. It is better to keep the src/ directory pristine, containing no compiled code.
  • When using the vmount -l option, files are linked into the version directory only if they are referenced in the project. Functions that examine the filesystem (like $(ls ...)) may produce unexpected results.

Chapter 3  Additional build examples

Let's explain the OMake build model a bit more. One issue that dominates this discussion is that OMake is based on global project analysis. That means you define a configuration for the entire project, and you run one instance of omake.

For single-directory projects this doesn't mean much. For multi-directory projects it means a lot. With GNU make, you would usually invoke the make program recursively for each directory in the project. For example, suppose you had a project with some project root directory, containing a directory of sources src, which in turn contains subdirectories lib and main. So your project looks like this nice piece of ASCII art.

    my_project/
    |--> Makefile
    `--> src/
         |---> Makefile
         |---> lib/
         |     |---> Makefile
         |     `---> source files...
         `---> main/
               |---> Makefile
               `---> source files...

Typically, with GNU make, you would start an instance of make in my_project/; this would in term start an instance of make in the src/ directory; and this would start new instances in lib/ and main/. Basically, you count up the number of Makefiles in the project, and that is the number of instances of make processes that will be created.

The number of processes is no big deal with today's machines (sometimes contrary the the author's opinion, we no longer live in the 1970s). The problem with the scheme was that each make process had a separate configuration, and it took a lot of work to make sure that everything was consistent. Furthermore, suppose the programmer runs make in the main/ directory, but the lib/ is out-of-date. In this case, make would happily crank away, perhaps trying to rebuild files in lib/, perhaps just giving up.

With OMake this changes entirely. Well, not entirely. The source structure is quite similar, we merely add some Os to the ASCII art.

    my_project/
    |--> OMakeroot   (or Root.om)
    |--> OMakefile
    `--> src/
         |---> OMakefile
         |---> lib/
         |     |---> OMakefile
         |     `---> source files...
         `---> main/
               |---> OMakefile
               `---> source files...

The role of each <dir>/OMakefile plays the same role as each <dir>/Makefile: it describes how to build the source files in <dir>. The OMakefile retains much of syntax and structure of the Makefile, but in most cases it is much simpler.

One minor difference is the presence of the OMakeroot in the project root. The main purpose of this file is to indicate where the project root is in the first place (in case omake is invoked from a subdirectory). The OMakeroot serves as the bootstrap file; omake starts by reading this file first. Otherwise, the syntax and evaluation of OMakeroot is no different from any other OMakefile.

The big difference is that OMake performs a global analysis. Here is what happens when omake starts.

  1. omake locates that OMakeroot file, and reads it.
  2. Each OMakefile points to its subdirectory OMakefiles using the .SUBDIRS target. For example, my_project/OMakefile has a rule,
        .SUBDIRS: src
    

    and the my_project/src/OMakefile has a rule,

        .SUBDIRS: lib main
    

    omake uses these rules to read and evaluate every OMakefile in the project. Reading and evaluation is fast. This part of the process is cheap.

  3. Now that the entire configuration is read, omake determines which files are out-of-date (using a global analysis), and starts the build process. This may take a while, depending on what exactly needs to be done.

There are several advantages to this model. First, since analysis is global, it is much easier to ensure that the build configuration is consistent–after all, there is only one configuration. Another benefit is that the build configuration is inherited, and can be re-used, down the hierarchy. Typically, the root OMakefile defines some standard boilerplate and configuration, and this is inherited by subdirectories that tweak and modify it (but do not need to restate it entirely). The disadvantage of course is space, since this is global analysis after all. In practice rarely seems to be a concern; omake takes up much less space than your web browser even on large projects.

Some notes to the GNU/BSD make user.

  • OMakefiles are a lot like Makefiles. The syntax is similar, and there many of the builtin functions are similar. However, the two build systems are not the same. Some evil features (in the authors' opinions) have been dropped in OMake, and some new features have been added.
  • OMake works the same way on all platforms, including Win32. The standard configuration does the right thing, but if you care about porting your code to multiple platforms, and you use some tricky features, you may need to condition parts of your build config on the $(OSTYPE) variable.
  • A minor issue is that OMake dependency analysis is based on MD5 file digests. That is, dependencies are based on file contents, not file modification times. Say goodbye to false rebuilds based on spurious timestamp changes and mismatches between local time and fileserver time.

3.1  OMakeroot vs. OMakefile

Before we begin with examples, let's ask the first question, “What is the difference between the project root OMakeroot and OMakefile?” A short answer is, there is no difference, but you must have an OMakeroot file (or Root.om file).

However, the normal style is that OMakeroot is boilerplate and is more-or-less the same for all projects. The OMakefile is where you put all your project-specific stuff.

To get started, you don't have to do this yourself. In most cases you just perform the following step in your project root directory.

  • Run omake --install in your project root.

This will create the initial OMakeroot and OMakefile files that you can edit to get started.

3.2  An example C project

To begin, let's start with a simple example. Let's say that we have a full directory tree, containing the following files.

    my_project/
    |--> OMakeroot
    |--> OMakefile
    `--> src/
         |---> OMakefile
         |---> lib/
         |     |---> OMakefile
         |     |---> ouch.c
         |     |---> ouch.h
         |     `---> bandaid.c
         `---> main/
               |---> OMakefile
               |---> horsefly.c
               |---> horsefly.h
               `---> main.c

Here is an example listing.

my_project/OMakeroot:
    # Include the standard configuration for C applications
    open build/C
    
    # Process the command-line vars
    DefineCommandVars()
    
    # Include the OMakefile in this directory.
    .SUBDIRS: .

my_project/OMakefile:
    # Set up the standard configuration
    CFLAGS += -g

    # Include the src subdirectory
    .SUBDIRS: src

my_project/src/OMakefile:
    # Add any extra options you like
    CFLAGS += -O2

    # Include the subdirectories
    .SUBDIRS: lib main

my_project/src/lib/OMakefile:
    # Build the library as a static library.
    # This builds libbug.a on Unix/OSX, or libbug.lib on Win32.
    # Note that the source files are listed _without_ suffix.
    StaticCLibrary(libbug, ouch bandaid)

my_project/src/main/OMakefile:
    # Some files include the .h files in ../lib
    INCLUDES += ../lib

    # Indicate which libraries we want to link against.
    LIBS[] +=
        ../lib/libbug

    # Build the program.
    # Builds horsefly.exe on Win32, and horsefly on Unix.
    # The first argument is the name of the executable.
    # The second argument is an array of object files (without suffix)
    # that are part of the program.
    CProgram(horsefly, horsefly main)

    # Build the program by default