3.11 Search Directories

When omake looks for a file that has no path component in its name, omake's default behavior is to search only the current directory. The search can be tailored to include other directories, a useful feature when your project is spread over multiple directories.

With omake's search directory support you write dependency lines, such as

main.obj : main.c io.h

and have omake figure out where main.c and io.h are actually located.

Implied Location of Missing Files

omake uses the search directories to locate files. It is clear that if the file exists, the location of the file is the directory it was found in. What happens if the file is missing? In this case omake assumes that the missing file is located in the first directory of the appropriate search directory, or . (the current directory) if none is appropriate.

Search Directory Macros

Search directories are set up with macro definitions. omake supports .PATH macros and VPATH macros, but issues a warning if you use both.

The .PATH Macros

The .PATH[.ext] macros define the directories omake uses to find files that don't have a path component. The optional .ext makes the .PATH.ext macro extension-specific. That is, .PATH.ext defines the search directories only for files with extension .ext. The .PATH macro (with no extension) controls the search for all files that aren't handled by a specific .PATH.ext.

The value of the .PATH macros is a list of directory names separated by semicolons (;). Here are two examples:

.PATH = .;..
.PATH.obj = ..\obj

The first definition tells omake that all files can be found in . (the current directory) or .. (the parent directory). The second definition tells omake that files with extension .obj can be found in directory ..\obj (and in no other directory). Note that we have defined both a nonspecific .PATH and an extension-specific .PATH.obj. omake uses directories defined in .PATH.obj to search for files with the .obj extension, and uses .PATH for all other files.

Here is an example of a makefile that uses search directories:

OBJS = main.obj sub.obj io.obj
.PATH.c = ..

project.exe : $(OBJS)
link $(.SOURCES), $(.TARGET), $(.TARGET,B);

The .c files are located only in the parent directory (..). Because no .PATH macro is defined, omake searches for all other files only in the current directory.

The VPATH Macros

The VPATH macros are similar to the .PATH macros with one major exception: the VPATH macros specify search directories in addition to the current directory. That is, the VPATH macros always have . as the first directory.

Search Directories and Run-Time Macros

Macros in omake usually serve as a simple text replacement. However, the run-time macros (.TARGET, .SOURCE, .SOURCES, and so on) include the location the target or source was found. For example,

.PATH.obj = objs

main.obj : main.c
%echo $(.TARGET)

displays objs\main.obj because the main.obj target is located in the objs directory, and the value of the .TARGET macro is the pathname of the target.

Here is a %.obj : %.c rule for Borland C that uses two run-time macros:

%.obj : %.c
$(CC) $(CFLAGS) -o$(.TARGET) -c $(.SOURCE)

The -o option names the output object file. $(.TARGET) is the name of the .obj file to be created, including its path component, according to where omake found the file. If the .obj file was not found, .TARGET is the implied location, either the first directory in .PATH.obj, if defined. Otherwise, it is the first directory in .PATH, if defined, or finally, in the current directory.

Search Directories and File Lookup

When omake searches for a file that has no directory component, it looks in the appropriate search directories. After the file has been located, omake assumes the file's location is permanent. Occasionally, this behavior is in error. For example, suppose a project's current .c and .obj files reside in a remote directory that .PATH references. You want to change a local copy of the project's main.c and then compile and link the resulting local main.obj with the remote .obj files, to produce a local .exe file.

Here is your makefile:

%.obj : %.c
$(CC) $(CFLAGS) -c $(.SOURCE)

OBJS = main.obj io.obj keyboard.obj
.PATH = .;c:\remote

project.exe : $(OBJS)
link $(.SOURCES), $(.TARGET);

You change the local copy of main.c. If there is a remote main.obj but no local main.obj, when you run omake, these commands are executed:

cl -c main.c
link c:\remote\main.obj c:\remote\io.obj c:\remote\keyboard.obj, project.exe;

The compilation uses the local main.c to produce a local main.obj, but the link uses c:\remote\main.obj rather than the local main.obj. This is because there was no main.obj in the current directory when omake started, and omake found main.obj in the c:\remote directory.

This example illustrates a general problem with omake. For reasons of speed, omake looks for a file (target) one time only. After that, omake assumes the location of the target is constant. As you have seen, this assumption can be wrong.

You can use the .REREAD target attribute to change this behavior. After executing the build scripts that update the target, omake searches again for any targets with the .REREAD attribute on disk. In the example, the make works correctly if the inference rule is given the .REREAD attribute:

%.obj .REREAD : %.c
$(CC) $(CFLAGS) -c $(.SOURCE)

The inference rule has the .REREAD attribute because main.obj inherits the build scripts and attributes of the matched inference rule.

Search Directories and Inference Rules

When omake tries to find an inferred dependency for an inference rule, it constructs a particular file name. If the file name has no path component, omake tries the file name in each search directory in order. Because each search directory is searched for each possible inferred dependency, omake runs more slowly with a large number of search directories. The -D command-line option (keep directory) speeds up this search.

Debugging the Search Directories

To debug your search directory choices, use the -p command-line option to print a section titled Search directories.

Compatibility with Other Make Utilities

For PM/CB compatibility, we support the .SOURCE directive, which is an additional way to specify search directories. The .SOURCE directive can specify the search directory for specific files, rather than for file extensions only.

For NMAKE compatibility, we support their search paths for dependents. See Microsoft NMAKE Compatibility.