2.5 Dependency Lines

Lines that include the colon (:) are called dependency lines. They specify a relationship between targets and the files on which they depend. This is the general form of a dependency line:

target [ target ... ] [ attribute ... ] : [ dependency ... ]

To the left of the colon is the target of the dependency. To the right of the colon are the dependencies needed to make the target. The target depends on the dependencies. For example, the following line states that project.exe depends on main.obj and io.obj:

project.exe : main.obj io.obj

There can be one or more targets, optionally a list of target attributes, a colon, one or more space or tab characters, and an optional list of dependencies. (Other make programs may call the dependency a source, dependent, or prerequisite.) The first target name must start in the first column of the makefile.

Dependency lines specify when to make the target. omake uses configuration lookup to determine whether to build the target or reuse an existing derived object.

Explicit and Inferred Dependencies

The dependencies that are listed explicitly on dependency lines are called explicit dependencies. For example, the line

test.exe : main.obj sub.obj

declares that test.exe depends on main.obj and sub.obj. When omake builds test.exe, it uses configuration lookup to compare test.exe with your current build configuration to see whether it must be updated.

A dependency line can declare that several targets have several dependencies. In the following line, both main.obj and sub.obj depend on both system.h and io.h:

main.obj sub.obj : system.h io.h

omake also has inference rules to infer a dependency of a particular target. Sources determined using inference rules are called inferred dependencies. For a discussion of inference rules, see Inference Rules.

Macros in Dependency Lines

The name of the target (and its root name) can be referenced in a dependency line through the use of macros. For example:

main.obj sub.obj : $*.h io.h

omake processes the target main.obj first; the expression $* evaluates to the root of the target name, main. The dependency line declares that main.obj depends on main.h and io.h. Next, sub.obj is processed, with $* evaluating to sub. Thus, this line also declares that sub.obj depends on sub.h and io.h.

The Make Process Is Recursive

A basic feature of omake is to make a target's dependencies before the configuration lookup is performed for the target. The following line instructs omake to make main.obj and io.obj before performing configuration lookup for project.exe:

project.exe : main.obj io.obj

This line instructs omake to make main.c before performing configuration lookup for main.obj:

main.obj : main.c

Detected Dependencies

Unlike standard make variants, omake does not require you to declare source-file dependencies in the makefile; omake detects dependencies automatically. This feature guarantees, for example, correct build behavior as C-language header files change, even if the header files are not listed as dependencies in the makefile. However, the list of dependencies must include build-order dependencies, for example, object modules and libraries that must be built before executables.

NOTE: You may want to include source-file dependencies in your makefile to ensure portability to another group or company that is not using omake.

Wildcards in Dependency Lines

The dependency side of a dependency line can use a wildcard specification such as this:

main.exe : *.obj

omake provides this feature, but using it can cause problems if a required file is missing. For example, the line

main.exe : *.obj
link /out:$(.TARGET) $(.SOURCES)

works fine until a required .obj file is accidentally deleted. omake works until the next update of main.exe, when the link command isn't called with all the required object names.

The Dependency Line Separator

The colon (:), which separates targets and dependencies on a dependency line, is also the character used as the drive separator in Windows NT. To distinguish this colon from the drive separator, you must put white space in front of it or put a space (target:<SPACE>), a tab (target:<TAB>), a semicolon (target:;), another colon (target::), or nothing (target:<ENTER>) after it. We suggest putting at least one space before and after it.

A Dependency Example

Assume the program test.exe is linked from main.obj and sub.obj. These object modules are compiled from source files main.c and sub.c, respectively. The makefile looks like this:

D-1

test.exe : main.obj sub.obj

link /out:test.exe main.obj sub.obj

D-2

main.obj : main.c

cl /c main.c

D-3

sub.obj : sub.c

cl /c sub.c

Line D-1 declares that test.exe depends on main.obj and sub.obj. Lines D-2 and D-3 declare that main.obj depends on main.c and sub.obj depends on sub.c.

Assume that since the last build of test.exe, main.c has been changed and sub.c has not. Running omake causes the first target in the makefile, test.exe, to be evaluated. test.exe depends on main.obj, which depends on main.c. main.c doesn't depend on anything. Comparing the configuration record of main.obj with the current view's build configuration shows that main.obj must be rebuilt using the selected version of main.c. This is done with the build script:

cl /c main.c

Next, sub.obj, which depends on sub.c, is evaluated. Configuration lookup shows that sub.obj does not need updating.

Comparing the configuration record of test.exe with the current build configuration shows that test.exe must be rebuilt with the selected version of main.obj. The following build script updates test.exe:

link /out:test.exe main.obj sub.obj

Running the command omake again causes omake to display this message:

omake: 'test.exe' is up to date.