Using Tools: 13.5 pmake: Advanced pmake Techniques

Up: GEOS SDK TechDocs | Up | Prev: 13.4 Contents of a Makefile | Next: 13.6 The Way Things Work

This section is devoted to those facilities in pmake that allow you to do a great deal in a makefile with very little work, as well as do some things you couldn't do in make without a great deal of work (and perhaps the use of other programs). The problem with these features is that they must be handled with care, or you will end up with a mess.

Search Paths

The pmake tool supports the dispersal of files into multiple directories by allowing you to specify places to look for sources with .PATH targets in the makefile. The directories you give as sources for these targets make up a "search path." Only those files used exclusively as sources are actually sought on a search path, the assumption being that anything listed as a target in the makefile can be created by the makefile and thus should be in the current directory.

There are two types of search paths in pmake : one is used for all types of files (including included makefiles) and is specified with a plain .PATH target (e.g. ".PATH : RCS"), while the other is specific to a certain type of file, as indicated by the file's suffix. A specific search path is indicated by immediately following the .PATH with the suffix of the file. For instance

.PATH.H : \GEOSDEV\DEVEL\APPL\WORPRO \GEOSDEV\DEVEL

would tell pmake to look in the directories \GEOSDEV\DEVEL\APPL\WORPRO and \GEOSDEV\DEVEL for any files whose suffix is .H.

The current directory is always consulted first to see if a file exists. Only if it cannot be found are the directories in the specific search path, followed by those in the general search path, consulted.

A search path is also used when expanding wildcard characters. If the pattern has a recognizable suffix on it, the path for that suffix will be used for the expansion. Otherwise the default search path is employed.

When a file is found in some directory other than the current one, all local variables that would have contained the target's name (.ALLSRC and .IMPSRC) will instead contain the path to the file, as found by pmake . Thus if you have a file ..\LIB\MUMBLE.C and a makefile

.PATH.c : ..\LIB
MUMBLE.EXE 			: MUMBLE.C
	$(CCOM) -o $(.TARGET) $(.ALLSRC)

the command executed to create MUMBLE.EXE would be "bcc -o MUMBLE ..\LIB\MUMBLE.C"

If a file exists in two directories on the same search path, the file in the first directory on the path will be the one pmake uses. So if you have a large system spread over many directories, it would behoove you to follow a naming convention that avoids such conflicts.

Something you should know about the way search paths are implemented is that each directory is read, and its contents cached, exactly once--when it is first encountered--so any changes to the directories while pmake is running will not be noted when searching for implicit sources, nor will they be found when pmake attempts to discover when the file was last modified, unless the file was created in the current directory.

Conditional Statements

Like a C compiler, pmake allows you to configure the makefile using conditional statements. A conditional looks like this:

#if <Boolean expression>
<lines>
#elif <another Boolean expression>
<more lines>
#else
<still more lines>
#endif

They may be nested to a depth of 30 and may occur anywhere (except in a comment, of course). The "#" must be the very first character on the line.

Each Boolean expression is made up of terms that look like function calls, the standard C Boolean operators &&, ||, and !, and the standard relational operators ==, !=, >, >=, <, and <=, with == and != being overloaded to allow string comparisons as well. The && operator represents logical AND; || is logical OR and ! is logical NOT. The arithmetic and string operators take precedence over all three of these operators, while NOT takes precedence over AND, which takes precedence over OR. This precedence may be overridden with parentheses, and an expression may be parenthesized to any level. Each Boolean term looks like a call on one of four functions:

make
The syntax is make(target) where target is a target in the makefile. This is true if the given target was specified on the command line or as the source for a .MAIN target (note that the sources for .MAIN are only used if no targets were given on the command line).
defined
The syntax is defined(variable) and is true if variable is defined. Certain variables are defined in the system makefile that identify the system on which pmake is being run.
exists
The syntax is exists(file) and is true if the file can be found on the global search path (i.e. that defined by .PATH targets, not by .PATHsuffix targets).
empty
This syntax is much like the others, except the string inside the parentheses is of the same form as you would put between parentheses when expanding a variable, complete with modifiers. The function returns true if the resulting string is empty. (Note: an undefined variable in this context will cause at the very least a warning message about a malformed conditional, and at the worst will cause the process to stop once it has read the makefile. If you want to check for a variable being defined or empty, use the expression
	!defined(var) || empty(var)
as the definition of || will prevent the empty() from being evaluated and causing an error, if the variable is undefined). This can be used to see if a variable contains a given word, for example:
		#if !empty(var:Mword)

The arithmetic and string operators may only be used to test the value of a variable. The left-hand side must contain the variable expansion, while the right-hand side contains either a string, enclosed in double-quotes, or a number. The standard C numeric conventions (except for specifying an octal number) apply to both sides. For example,

#if $(OS) == 4.3
#if $(MACHINE) == "sun3"
#if $(LOAD_ADDR) < 0xc000

are all valid conditionals. In addition, the numeric value of a variable can be tested as a Boolean as follows:

#if $(LOAD)

would see if LOAD contains a non-zero value and

#if !$(LOAD)

would test if LOAD contains a zero value.

In addition to the bare #if, there are other forms that apply one of the first two functions to each term. They are as follows:

ifdef		defined
ifndef		!defined
ifmake		make
ifnmake		!make

There are also the "else if" forms: elif, elifdef, elifndef, elifmake, and elifnmake.


Up: GEOS SDK TechDocs | Up | Prev: 13.4 Contents of a Makefile | Next: 13.6 The Way Things Work