- 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 function
- (like StaticCLibrary and CProgram) 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 before they are used.
- * Subdirectories are incorporated into a project us
- ing the .SUBDIRS: target.
- 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 --in
- stall 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 continu
ing
- 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 world0);
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 ex
- ample, we will use the gcc compiler with the -g option. In addi
- tion, 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)
- LARGER PROJECTS
- As the project grows it is likely that we will want to
- build libraries of code. Libraries can be built using the Stat
- icCLibrary 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)
- 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 di
- rectory. 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 subdi
- rectories, 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 vari
- able also contains the -O3 option for the CProgram, and hel
- lo_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 sec
- tion 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 ad
- ditional 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), $(EMP
TY), -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 vari
- able definitions from the if-statements. Variables in omake are
- scoped---variables in nested blocks (blocks with greater indenta
- tion), are not normally defined in outer blocks. The export di
- rective 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 vari
- able, and replace occurrences of the lib string with the vari
- able.
# 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), $(EMP
TY), -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 li
- brary 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 vari
- able 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.
- 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.
- 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 world0
- 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))