diff options
Diffstat (limited to '20120831/PSD.doc/tutorial.ms')
-rw-r--r-- | 20120831/PSD.doc/tutorial.ms | 3773 |
1 files changed, 0 insertions, 3773 deletions
diff --git a/20120831/PSD.doc/tutorial.ms b/20120831/PSD.doc/tutorial.ms deleted file mode 100644 index c1a6444..0000000 --- a/20120831/PSD.doc/tutorial.ms +++ /dev/null @@ -1,3773 +0,0 @@ -.\" $NetBSD: tutorial.ms,v 1.11 2011/08/18 15:19:30 sjg Exp $ -.\" Copyright (c) 1988, 1989, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" Adam de Boor. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" Copyright (c) 1988, 1989 by Adam de Boor -.\" Copyright (c) 1989 by Berkeley Softworks -.\" -.\" This code is derived from software contributed to Berkeley by -.\" Adam de Boor. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)tutorial.ms 8.1 (Berkeley) 8/18/93 -.\" -.EH 'PSD:12-%''PMake \*- A Tutorial' -.OH 'PMake \*- A Tutorial''PSD:12-%' -.\" xH is a macro to provide numbered headers that are automatically stuffed -.\" into a table-of-contents, properly indented, etc. If the first argument -.\" is numeric, it is taken as the depth for numbering (as for .NH), else -.\" the default (1) is assumed. -.\" -.\" @P The initial paragraph distance. -.\" @Q The piece of section number to increment (or 0 if none given) -.\" @R Section header. -.\" @S Indent for toc entry -.\" @T Argument to NH (can't use @Q b/c giving 0 to NH resets the counter) -.de xH -.NH \\$1 -\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 -.nr PD .1v -.XS \\n% -.ta 0.6i -\\*(SN \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 -.XE -.nr PD .3v -.. -.\" CW is used to place a string in fixed-width or switch to a -.\" fixed-width font. -.\" C is a typewriter font for a laserwriter. Use something else if -.\" you don't have one... -.de CW -.ie !\\n(.$ .ft C -.el \&\\$3\fC\\$1\fP\\$2 -.. -.\" Anything I put in a display I want to be in fixed-width -.am DS -.CW -.. -.\" The stuff in .No produces a little stop sign in the left margin -.\" that says NOTE in it. Unfortunately, it does cause a break, but -.\" hey. Can't have everything. In case you're wondering how I came -.\" up with such weird commands, they came from running grn on a -.\" gremlin file... -.de No -.br -.ne 0.5i -.po -0.5i -.br -.mk -.nr g3 \\n(.f -.nr g4 \\n(.s -.sp -1 -.\" .st cf -\D't 5u' -.sp -1 -\h'50u' -.sp -1 -\D't 3u' -.sp -1 -.sp 7u -\h'53u' -\d\D'p -0.19i 0.0i 0.0i -0.13i 0.30i 0.0i 0.0i 0.13i' -.sp -1 -.ft R -.ps 6 -.nr g8 \\n(.d -.ds g9 "NOTE -.sp 74u -\h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9 -.sp |\\n(g8u -.sp 166u -\D't 3u' -.br -.po -.rt -.ft \\n(g3 -.ps \\n(g4 -.. -.de Bp -.ie !\\n(.$ .IP \(bu 2 -.el .IP "\&" 2 -.. -.po +.3i -.TL -PMake \*- A Tutorial -.AU -Adam de Boor -.AI -Berkeley Softworks -2150 Shattuck Ave, Penthouse -Berkeley, CA 94704 -adam@bsw.uu.net -\&...!uunet!bsw!adam -.FS -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appears in all copies. -The University of California, Berkeley Softworks, and Adam de Boor make no -representations about the suitability of this software for any -purpose. It is provided "as is" without express or implied warranty. -.FE -.PP -.xH 1 Introduction -.LP -PMake is a program for creating other programs, or anything else you -can think of for it to do. The basic idea behind PMake is that, for -any given system, be it a program or a document or whatever, there -will be some files that depend on the state of other files (on when -they were last modified). PMake takes these dependencies, which you -must specify, and uses them to build whatever it is you want it to -build. -.LP -PMake is almost fully-compatible with Make, with which you may already -be familiar. PMake's most important feature is its ability to run -several different jobs at once, making the creation of systems -considerably faster. It also has a great deal more functionality than -Make. Throughout the text, whenever something is mentioned that is an -important difference between PMake and Make (i.e. something that will -cause a makefile to fail if you don't do something about it), or is -simply important, it will be flagged with a little sign in the left -margin, like this: -.No -.LP -This tutorial is divided into three main sections corresponding to basic, -intermediate and advanced PMake usage. If you already know Make well, -you will only need to skim chapter 2 (there are some aspects of -PMake that I consider basic to its use that didn't exist in Make). -Things in chapter 3 make life much easier, while those in chapter 4 -are strictly for those who know what they are doing. Chapter 5 has -definitions for the jargon I use and chapter 6 contains possible -solutions to the problems presented throughout the tutorial. -.xH 1 The Basics of PMake -.LP -PMake takes as input a file that tells a) which files depend on which -other files to be complete and b) what to do about files that are -``out-of-date.'' This file is known as a ``makefile'' and is usually -.Ix 0 def makefile -kept in the top-most directory of the system to be built. While you -can call the makefile anything you want, PMake will look for -.CW Makefile -and -.CW makefile -(in that order) in the current directory if you don't tell it -otherwise. -.Ix 0 def makefile default -To specify a different makefile, use the -.B \-f -flag (e.g. -.CW "pmake -f program.mk" ''). `` -.Ix 0 ref flags -f -.Ix 0 ref makefile other -.LP -A makefile has four different types of lines in it: -.RS -.IP \(bu 2 -File dependency specifications -.IP \(bu 2 -Creation commands -.IP \(bu 2 -Variable assignments -.IP \(bu 2 -Comments, include statements and conditional directives -.RE -.LP -Any line may be continued over multiple lines by ending it with a -backslash. -.Ix 0 def "continuation line" -The backslash, following newline and any initial whitespace -on the following line are compressed into a single space before the -input line is examined by PMake. -.xH 2 Dependency Lines -.LP -As mentioned in the introduction, in any system, there are -dependencies between the files that make up the system. For instance, -in a program made up of several C source files and one header file, -the C files will need to be re-compiled should the header file be -changed. For a document of several chapters and one macro file, the -chapters will need to be reprocessed if any of the macros changes. -.Ix 0 def "dependency" -These are dependencies and are specified by means of dependency lines in -the makefile. -.LP -.Ix 0 def "dependency line" -On a dependency line, there are targets and sources, separated by a -one- or two-character operator. -The targets ``depend'' on the sources and are usually created from -them. -.Ix 0 def target -.Ix 0 def source -.Ix 0 ref operator -Any number of targets and sources may be specified on a dependency line. -All the targets in the line are made to depend on all the sources. -Targets and sources need not be actual files, but every source must be -either an actual file or another target in the makefile. -If you run out of room, use a backslash at the end of the line to continue onto -the next one. -.LP -Any file may be a target and any file may be a source, but the -relationship between the two (or however many) is determined by the -``operator'' that separates them. -.Ix 0 def operator -Three types of operators exist: one specifies that the datedness of a -target is determined by the state of its sources, while another -specifies other files (the sources) that need to be dealt with before -the target can be re-created. The third operator is very similar to -the first, with the additional condition that the target is -out-of-date if it has no sources. These operations are represented by -the colon, the exclamation point and the double-colon, respectively, and are -mutually exclusive. Their exact semantics are as follows: -.IP ":" -.Ix 0 def operator colon -.Ix 0 def : -If a colon is used, a target on the line is considered to be -``out-of-date'' (and in need of creation) if -.RS -.IP \(bu 2 -any of the sources has been modified more recently than the target, or -.IP \(bu 2 -the target doesn't exist. -.RE -.Ix 0 def out-of-date -.IP "\&" -Under this operation, steps will be taken to re-create the target only -if it is found to be out-of-date by using these two rules. -.IP "!" -.Ix 0 def operator force -.Ix 0 def ! -If an exclamation point is used, the target will always be re-created, -but this will not happen until all of its sources have been examined -and re-created, if necessary. -.IP "::" -.Ix 0 def operator double-colon -.Ix 0 def :: -If a double-colon is used, a target is out-of-date if: -.RS -.IP \(bu 2 -any of the sources has been modified more recently than the target, or -.IP \(bu 2 -the target doesn't exist, or -.IP \(bu 2 -the target has no sources. -.RE -.IP "\&" -If the target is out-of-date according to these rules, it will be re-created. -This operator also does something else to the targets, but I'll go -into that in the next section (``Shell Commands''). -.LP -Enough words, now for an example. Take that C program I mentioned -earlier. Say there are three C files -.CW a.c , ( -.CW b.c -and -.CW c.c ) -each of which -includes the file -.CW defs.h . -The dependencies between the files could then be expressed as follows: -.DS -program : a.o b.o c.o -a.o b.o c.o : defs.h -a.o : a.c -b.o : b.c -c.o : c.c -.DE -.LP -You may be wondering at this point, where -.CW a.o , -.CW b.o -and -.CW c.o -came in and why -.I they -depend on -.CW defs.h -and the C files don't. The reason is quite simple: -.CW program -cannot be made by linking together .c files \*- it must be -made from .o files. Likewise, if you change -.CW defs.h , -it isn't the .c files that need to be re-created, it's the .o files. -If you think of dependencies in these terms \*- which files (targets) -need to be created from which files (sources) \*- you should have no problems. -.LP -An important thing to notice about the above example, is that all the -\&.o files appear as targets on more than one line. This is perfectly -all right: the target is made to depend on all the sources mentioned -on all the dependency lines. E.g. -.CW a.o -depends on both -.CW defs.h -and -.CW a.c . -.Ix 0 ref dependency -.No -.LP -The order of the dependency lines in the makefile is -important: the first target on the first dependency line in the -makefile will be the one that gets made if you don't say otherwise. -That's why -.CW program -comes first in the example makefile, above. -.LP -Both targets and sources may contain the standard C-Shell wildcard -characters -.CW { , ( -.CW } , -.CW * , -.CW ? , -.CW [ , -and -.CW ] ), -but the non-curly-brace ones may only appear in the final component -(the file portion) of the target or source. The characters mean the -following things: -.IP \fB{}\fP -These enclose a comma-separated list of options and cause the pattern -to be expanded once for each element of the list. Each expansion -contains a different element. For example, -.CW src/{whiffle,beep,fish}.c -expands to the three words -.CW src/whiffle.c , -.CW src/beep.c , -and -.CW src/fish.c . -These braces may be nested and, unlike the other wildcard characters, -the resulting words need not be actual files. All other wildcard -characters are expanded using the files that exist when PMake is -started. -.IP \fB*\fP -This matches zero or more characters of any sort. -.CW src/*.c -will expand to the same three words as above as long as -.CW src -contains those three files (and no other files that end in -.CW .c ). -.IP \fB?\fP -Matches any single character. -.IP \fB[]\fP -This is known as a character class and contains either a list of -single characters, or a series of character ranges -.CW a-z , ( -for example means all characters between a and z), or both. It matches -any single character contained in the list. E.g. -.CW [A-Za-z] -will match all letters, while -.CW [0123456789] -will match all numbers. -.xH 2 Shell Commands -.LP -``Isn't that nice,'' you say to yourself, ``but how are files -actually `re-created,' as he likes to spell it?'' -The re-creation is accomplished by commands you place in the makefile. -These commands are passed to the Bourne shell (better known as -``/bin/sh'') to be executed and are -.Ix 0 ref shell -.Ix 0 ref re-creation -.Ix 0 ref update -expected to do what's necessary to update the target file (PMake -doesn't actually check to see if the target was created. It just -assumes it's there). -.Ix 0 ref target -.LP -Shell commands in a makefile look a lot like shell commands you would -type at a terminal, with one important exception: each command in a -makefile -.I must -be preceded by at least one tab. -.LP -Each target has associated with it a shell script made up of -one or more of these shell commands. The creation script for a target -should immediately follow the dependency line for that target. While -any given target may appear on more than one dependency line, only one -of these dependency lines may be followed by a creation script, unless -the `::' operator was used on the dependency line. -.Ix 0 ref operator double-colon -.Ix 0 ref :: -.No -.LP -If the double-colon was used, each dependency line for the target -may be followed by a shell script. That script will only be executed -if the target on the associated dependency line is out-of-date with -respect to the sources on that line, according to the rules I gave -earlier. -I'll give you a good example of this later on. -.LP -To expand on the earlier makefile, you might add commands as follows: -.DS -program : a.o b.o c.o - cc a.o b.o c.o \-o program -a.o b.o c.o : defs.h -a.o : a.c - cc \-c a.c -b.o : b.c - cc \-c b.c -c.o : c.c - cc \-c c.c -.DE -.LP -Something you should remember when writing a makefile is, the -commands will be executed if the -.I target -on the dependency line is out-of-date, not the sources. -.Ix 0 ref target -.Ix 0 ref source -.Ix 0 ref out-of-date -In this example, the command -.CW "cc \-c a.c" '' `` -will be executed if -.CW a.o -is out-of-date. Because of the `:' operator, -.Ix 0 ref : -.Ix 0 ref operator colon -this means that should -.CW a.c -.I or -.CW defs.h -have been modified more recently than -.CW a.o , -the command will be executed -.CW a.o "\&" ( -will be considered out-of-date). -.Ix 0 ref out-of-date -.LP -Remember how I said the only difference between a makefile shell -command and a regular shell command was the leading tab? I lied. There -is another way in which makefile commands differ from regular ones. -The first two characters after the initial whitespace are treated -specially. -If they are any combination of `@' and `\-', they cause PMake to do -different things. -.LP -In most cases, shell commands are printed before they're -actually executed. This is to keep you informed of what's going on. If -an `@' appears, however, this echoing is suppressed. In the case of an -.CW echo -command, say -.CW "echo Linking index" ,'' `` -it would be -rather silly to see -.DS -echo Linking index -Linking index -.DE -.LP -so PMake allows you to place an `@' before the command -.CW "@echo Linking index" '') (`` -to prevent the command from being printed. -.LP -The other special character is the `\-'. In case you didn't know, -shell commands finish with a certain ``exit status.'' This status is -made available by the operating system to whatever program invoked the -command. Normally this status will be 0 if everything went ok and -non-zero if something went wrong. For this reason, PMake will consider -an error to have occurred if one of the shells it invokes returns a non-zero -status. When it detects an error, PMake's usual action is to abort -whatever it's doing and exit with a non-zero status itself (any other -targets that were being created will continue being made, but nothing -new will be started. PMake will exit after the last job finishes). -This behavior can be altered, however, by placing a `\-' at the front -of a command -.CW "\-mv index index.old" ''), (`` -certain command-line arguments, -or doing other things, to be detailed later. In such -a case, the non-zero status is simply ignored and PMake keeps chugging -along. -.No -.LP -Because all the commands are given to a single shell to execute, such -things as setting shell variables, changing directories, etc., last -beyond the command in which they are found. This also allows shell -compound commands (like -.CW for -loops) to be entered in a natural manner. -Since this could cause problems for some makefiles that depend on -each command being executed by a single shell, PMake has a -.B \-B -.Ix 0 ref compatibility -.Ix 0 ref flags -B -flag (it stands for backwards-compatible) that forces each command to -be given to a separate shell. It also does several other things, all -of which I discourage since they are now old-fashioned.\|.\|.\|. -.No -.LP -A target's shell script is fed to the shell on its (the shell's) input stream. -This means that any commands, such as -.CW ci -that need to get input from the terminal won't work right \*- they'll -get the shell's input, something they probably won't find to their -liking. A simple way around this is to give a command like this: -.DS -ci $(SRCS) < /dev/tty -.DE -This would force the program's input to come from the terminal. If you -can't do this for some reason, your only other alternative is to use -PMake in its fullest compatibility mode. See -.B Compatibility -in chapter 4. -.Ix 0 ref compatibility -.LP -.xH 2 Variables -.LP -PMake, like Make before it, has the ability to save text in variables -to be recalled later at your convenience. Variables in PMake are used -much like variables in the shell and, by tradition, consist of -all upper-case letters (you don't -.I have -to use all upper-case letters. -In fact there's nothing to stop you from calling a variable -.CW @^&$%$ . -Just tradition). Variables are assigned-to using lines of the form -.Ix 0 def variable assignment -.DS -VARIABLE = value -.DE -.Ix 0 def variable assignment -appended-to by -.DS -VARIABLE += value -.DE -.Ix 0 def variable appending -.Ix 0 def variable assignment appended -.Ix 0 def += -conditionally assigned-to (if the variable isn't already defined) by -.DS -VARIABLE ?= value -.DE -.Ix 0 def variable assignment conditional -.Ix 0 def ?= -and assigned-to with expansion (i.e. the value is expanded (see below) -before being assigned to the variable\*-useful for placing a value at -the beginning of a variable, or other things) by -.DS -VARIABLE := value -.DE -.Ix 0 def variable assignment expanded -.Ix 0 def := -.LP -Any whitespace before -.I value -is stripped off. When appending, a space is placed between the old -value and the stuff being appended. -.LP -The final way a variable may be assigned to is using -.DS -VARIABLE != shell-command -.DE -.Ix 0 def variable assignment shell-output -.Ix 0 def != -In this case, -.I shell-command -has all its variables expanded (see below) and is passed off to a -shell to execute. The output of the shell is then placed in the -variable. Any newlines (other than the final one) are replaced by -spaces before the assignment is made. This is typically used to find -the current directory via a line like: -.DS -CWD != pwd -.DE -.LP -.B Note: -this is intended to be used to execute commands that produce small amounts -of output (e.g. ``pwd''). The implementation is less than intelligent and will -likely freeze if you execute something that produces thousands of -bytes of output (8 Kb is the limit on many UNIX systems). -.LP -The value of a variable may be retrieved by enclosing the variable -name in parentheses or curly braces and preceding the whole thing -with a dollar sign. -.LP -For example, to set the variable CFLAGS to the string -.CW "\-I/sprite/src/lib/libc \-O" ,'' `` -you would place a line -.DS -CFLAGS = \-I/sprite/src/lib/libc \-O -.DE -in the makefile and use the word -.CW "$(CFLAGS)" -wherever you would like the string -.CW "\-I/sprite/src/lib/libc \-O" -to appear. This is called variable expansion. -.Ix 0 def variable expansion -.No -.LP -Unlike Make, PMake will not expand a variable unless it knows -the variable exists. E.g. if you have a -.CW "${i}" -in a shell command and you have not assigned a value to the variable -.CW i -(the empty string is considered a value, by the way), where Make would have -substituted the empty string, PMake will leave the -.CW "${i}" -alone. -To keep PMake from substituting for a variable it knows, precede the -dollar sign with another dollar sign. -(e.g. to pass -.CW "${HOME}" -to the shell, use -.CW "$${HOME}" ). -This causes PMake, in effect, to expand the -.CW $ -macro, which expands to a single -.CW $ . -For compatibility, Make's style of variable expansion will be used -if you invoke PMake with any of the compatibility flags (\c -.B \-V , -.B \-B -or -.B \-M . -The -.B \-V -flag alters just the variable expansion). -.Ix 0 ref flags -V -.Ix 0 ref flags -B -.Ix 0 ref flags -M -.Ix 0 ref compatibility -.LP -.Ix 0 ref variable expansion -There are two different times at which variable expansion occurs: -When parsing a dependency line, the expansion occurs immediately -upon reading the line. If any variable used on a dependency line is -undefined, PMake will print a message and exit. -Variables in shell commands are expanded when the command is -executed. -Variables used inside another variable are expanded whenever the outer -variable is expanded (the expansion of an inner variable has no effect -on the outer variable. I.e. if the outer variable is used on a dependency -line and in a shell command, and the inner variable changes value -between when the dependency line is read and the shell command is -executed, two different values will be substituted for the outer -variable). -.Ix 0 def variable types -.LP -Variables come in four flavors, though they are all expanded the same -and all look about the same. They are (in order of expanding scope): -.RS -.IP \(bu 2 -Local variables. -.Ix 0 ref variable local -.IP \(bu 2 -Command-line variables. -.Ix 0 ref variable command-line -.IP \(bu 2 -Global variables. -.Ix 0 ref variable global -.IP \(bu 2 -Environment variables. -.Ix 0 ref variable environment -.RE -.LP -The classification of variables doesn't matter much, except that the -classes are searched from the top (local) to the bottom (environment) -when looking up a variable. The first one found wins. -.xH 3 Local Variables -.LP -.Ix 0 def variable local -Each target can have as many as seven local variables. These are -variables that are only ``visible'' within that target's shell script -and contain such things as the target's name, all of its sources (from -all its dependency lines), those sources that were out-of-date, etc. -Four local variables are defined for all targets. They are: -.RS -.IP ".TARGET" -.Ix 0 def variable local .TARGET -.Ix 0 def .TARGET -The name of the target. -.IP ".OODATE" -.Ix 0 def variable local .OODATE -.Ix 0 def .OODATE -The list of the sources for the target that were considered out-of-date. -The order in the list is not guaranteed to be the same as the order in -which the dependencies were given. -.IP ".ALLSRC" -.Ix 0 def variable local .ALLSRC -.Ix 0 def .ALLSRC -The list of all sources for this target in the order in which they -were given. -.IP ".PREFIX" -.Ix 0 def variable local .PREFIX -.Ix 0 def .PREFIX -The target without its suffix and without any leading path. E.g. for -the target -.CW ../../lib/compat/fsRead.c , -this variable would contain -.CW fsRead . -.RE -.LP -Three other local variables are set only for certain targets under -special circumstances. These are the ``.IMPSRC,'' -.Ix 0 ref variable local .IMPSRC -.Ix 0 ref .IMPSRC -``.ARCHIVE,'' -.Ix 0 ref variable local .ARCHIVE -.Ix 0 ref .ARCHIVE -and ``.MEMBER'' -.Ix 0 ref variable local .MEMBER -.Ix 0 ref .MEMBER -variables. When they are set and how they are used is described later. -.LP -Four of these variables may be used in sources as well as in shell -scripts. -.Ix 0 def "dynamic source" -.Ix 0 def source dynamic -These are ``.TARGET'', ``.PREFIX'', ``.ARCHIVE'' and ``.MEMBER''. The -variables in the sources are expanded once for each target on the -dependency line, providing what is known as a ``dynamic source,'' -.Rd 0 -allowing you to specify several dependency lines at once. For example, -.DS -$(OBJS) : $(.PREFIX).c -.DE -will create a dependency between each object file and its -corresponding C source file. -.xH 3 Command-line Variables -.LP -.Ix 0 def variable command-line -Command-line variables are set when PMake is first invoked by giving a -variable assignment as one of the arguments. For example, -.DS -pmake "CFLAGS = -I/sprite/src/lib/libc -O" -.DE -would make -.CW CFLAGS -be a command-line variable with the given value. Any assignments to -.CW CFLAGS -in the makefile will have no effect, because once it -is set, there is (almost) nothing you can do to change a command-line -variable (the search order, you see). Command-line variables may be -set using any of the four assignment operators, though only -.CW = -and -.CW ?= -behave as you would expect them to, mostly because assignments to -command-line variables are performed before the makefile is read, thus -the values set in the makefile are unavailable at the time. -.CW += -.Ix 0 ref += -.Ix 0 ref variable assignment appended -is the same as -.CW = , -because the old value of the variable is sought only in the scope in -which the assignment is taking place (for reasons of efficiency that I -won't get into here). -.CW := -and -.CW ?= -.Ix 0 ref := -.Ix 0 ref ?= -.Ix 0 ref variable assignment expanded -.Ix 0 ref variable assignment conditional -will work if the only variables used are in the environment. -.CW != -is sort of pointless to use from the command line, since the same -effect can no doubt be accomplished using the shell's own command -substitution mechanisms (backquotes and all that). -.xH 3 Global Variables -.LP -.Ix 0 def variable global -Global variables are those set or appended-to in the makefile. -There are two classes of global variables: those you set and those PMake sets. -As I said before, the ones you set can have any name you want them to have, -except they may not contain a colon or an exclamation point. -The variables PMake sets (almost) always begin with a -period and always contain upper-case letters, only. The variables are -as follows: -.RS -.IP .PMAKE -.Ix 0 def variable global .PMAKE -.Ix 0 def .PMAKE -.Ix 0 def variable global MAKE -.Ix 0 def MAKE -The name by which PMake was invoked is stored in this variable. For -compatibility, the name is also stored in the MAKE variable. -.IP .MAKEFLAGS -.Ix 0 def variable global .MAKEFLAGS -.Ix 0 def .MAKEFLAGS variable -.Ix 0 def variable global MFLAGS -.Ix 0 def MFLAGS -All the relevant flags with which PMake was invoked. This does not -include such things as -.B \-f -or variable assignments. Again for compatibility, this value is stored -in the MFLAGS variable as well. -.RE -.LP -Two other variables, ``.INCLUDES'' and ``.LIBS,'' are covered in the -section on special targets in chapter 3. -.Ix 0 ref variable global .INCLUDES -.Ix 0 ref variable global .LIBS -.LP -Global variables may be deleted using lines of the form: -.Ix 0 def #undef -.Ix 0 def variable deletion -.DS -#undef \fIvariable\fP -.DE -The -.CW # ' ` -must be the first character on the line. Note that this may only be -done on global variables. -.xH 3 Environment Variables -.LP -.Ix 0 def variable environment -Environment variables are passed by the shell that invoked PMake and -are given by PMake to each shell it invokes. They are expanded like -any other variable, but they cannot be altered in any way. -.LP -One special environment variable, -.CW PMAKE , -.Ix 0 def variable environment PMAKE -is examined by PMake for command-line flags, variable assignments, -etc., it should always use. This variable is examined before the -actual arguments to PMake are. In addition, all flags given to PMake, -either through the -.CW PMAKE -variable or on the command line, are placed in this environment -variable and exported to each shell PMake executes. Thus recursive -invocations of PMake automatically receive the same flags as the -top-most one. -.LP -Using all these variables, you can compress the sample makefile even more: -.DS -OBJS = a.o b.o c.o -program : $(OBJS) - cc $(.ALLSRC) \-o $(.TARGET) -$(OBJS) : defs.h -a.o : a.c - cc \-c a.c -b.o : b.c - cc \-c b.c -c.o : c.c - cc \-c c.c -.DE -.Ix 0 ref variable local .ALLSRC -.Ix 0 ref .ALLSRC -.Ix 0 ref variable local .TARGET -.Ix 0 ref .TARGET -.Rd 3 -.xH 2 Comments -.LP -.Ix 0 def comments -Comments in a makefile start with a `#' character and extend to the -end of the line. They may appear -anywhere you want them, except in a shell command (though the shell -will treat it as a comment, too). If, for some reason, you need to use the `#' -in a variable or on a dependency line, put a backslash in front of it. -PMake will compress the two into a single `#' (Note: this isn't true -if PMake is operating in full-compatibility mode). -.Ix 0 ref flags -M -.Ix 0 ref compatibility -.xH 2 Parallelism -.No -.LP -PMake was specifically designed to re-create several targets at once, -when possible. You do not have to do anything special to cause this to -happen (unless PMake was configured to not act in parallel, in which -case you will have to make use of the -.B \-L -and -.B \-J -flags (see below)), -.Ix 0 ref flags -L -.Ix 0 ref flags -J -but you do have to be careful at times. -.LP -There are several problems you are likely to encounter. One is -that some makefiles (and programs) are written in such a way that it is -impossible for two targets to be made at once. The program -.CW xstr , -for example, -always modifies the files -.CW strings -and -.CW x.c . -There is no way to change it. Thus you cannot run two of them at once -without something being trashed. Similarly, if you have commands -in the makefile that always send output to the same file, you will not -be able to make more than one target at once unless you change the -file you use. You can, for instance, add a -.CW $$$$ -to the end of the file name to tack on the process ID of the shell -executing the command (each -.CW $$ -expands to a single -.CW $ , -thus giving you the shell variable -.CW $$ ). -Since only one shell is used for all the -commands, you'll get the same file name for each command in the -script. -.LP -The other problem comes from improperly-specified dependencies that -worked in Make because of its sequential, depth-first way of examining -them. While I don't want to go into depth on how PMake -works (look in chapter 4 if you're interested), I will warn you that -files in two different ``levels'' of the dependency tree may be -examined in a different order in PMake than they were in Make. For -example, given the makefile -.DS -a : b c -b : d -.DE -PMake will examine the targets in the order -.CW c , -.CW d , -.CW b , -.CW a . -If the makefile's author expected PMake to abort before making -.CW c -if an error occurred while making -.CW b , -or if -.CW b -needed to exist before -.CW c -was made, -s/he will be sorely disappointed. The dependencies are -incomplete, since in both these cases, -.CW c -would depend on -.CW b . -So watch out. -.LP -Another problem you may face is that, while PMake is set up to handle the -output from multiple jobs in a graceful fashion, the same is not so for input. -It has no way to regulate input to different jobs, -so if you use the redirection from -.CW /dev/tty -I mentioned earlier, you must be careful not to run two of the jobs at once. -.xH 2 Writing and Debugging a Makefile -.LP -Now you know most of what's in a makefile, what do you do next? There -are two choices: (1) use one of the uncommonly-available makefile -generators or (2) write your own makefile (I leave out the third choice of -ignoring PMake and doing everything by hand as being beyond the bounds -of common sense). -.LP -When faced with the writing of a makefile, it is usually best to start -from first principles: just what -.I are -you trying to do? What do you want the makefile finally to produce? -.LP -To begin with a somewhat traditional example, let's say you need to -write a makefile to create a program, -.CW expr , -that takes standard infix expressions and converts them to prefix form (for -no readily apparent reason). You've got three source files, in C, that -make up the program: -.CW main.c , -.CW parse.c , -and -.CW output.c . -Harking back to my pithy advice about dependency lines, you write the -first line of the file: -.DS -expr : main.o parse.o output.o -.DE -because you remember -.CW expr -is made from -.CW .o -files, not -.CW .c -files. Similarly for the -.CW .o -files you produce the lines: -.DS -main.o : main.c -parse.o : parse.c -output.o : output.c -main.o parse.o output.o : defs.h -.DE -.LP -Great. You've now got the dependencies specified. What you need now is -commands. These commands, remember, must produce the target on the -dependency line, usually by using the sources you've listed. -You remember about local variables? Good, so it should come -to you as no surprise when you write -.DS -expr : main.o parse.o output.o - cc -o $(.TARGET) $(.ALLSRC) -.DE -Why use the variables? If your program grows to produce postfix -expressions too (which, of course, requires a name change or two), it -is one fewer place you have to change the file. You cannot do this for -the object files, however, because they depend on their corresponding -source files -.I and -.CW defs.h , -thus if you said -.DS - cc -c $(.ALLSRC) -.DE -you'd get (for -.CW main.o ): -.DS - cc -c main.c defs.h -.DE -which is wrong. So you round out the makefile with these lines: -.DS -main.o : main.c - cc -c main.c -parse.o : parse.c - cc -c parse.c -output.o : output.c - cc -c output.c -.DE -.LP -The makefile is now complete and will, in fact, create the program you -want it to without unnecessary compilations or excessive typing on -your part. There are two things wrong with it, however (aside from it -being altogether too long, something I'll address in chapter 3): -.IP 1) -The string -.CW "main.o parse.o output.o" '' `` -is repeated twice, necessitating two changes when you add postfix -(you were planning on that, weren't you?). This is in direct violation -of de Boor's First Rule of writing makefiles: -.QP -.I -Anything that needs to be written more than once -should be placed in a variable. -.IP "\&" -I cannot emphasize this enough as being very important to the -maintenance of a makefile and its program. -.IP 2) -There is no way to alter the way compilations are performed short of -editing the makefile and making the change in all places. This is evil -and violates de Boor's Second Rule, which follows directly from the -first: -.QP -.I -Any flags or programs used inside a makefile should be placed in a variable so -they may be changed, temporarily or permanently, with the greatest ease. -.LP -The makefile should more properly read: -.DS -OBJS = main.o parse.o output.o -expr : $(OBJS) - $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC) -main.o : main.c - $(CC) $(CFLAGS) -c main.c -parse.o : parse.c - $(CC) $(CFLAGS) -c parse.c -output.o : output.c - $(CC) $(CFLAGS) -c output.c -$(OBJS) : defs.h -.DE -Alternatively, if you like the idea of dynamic sources mentioned in -section 2.3.1, -.Rm 0 2.3.1 -.Rd 4 -.Ix 0 ref "dynamic source" -.Ix 0 ref source dynamic -you could write it like this: -.DS -OBJS = main.o parse.o output.o -expr : $(OBJS) - $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC) -$(OBJS) : $(.PREFIX).c defs.h - $(CC) $(CFLAGS) -c $(.PREFIX).c -.DE -These two rules and examples lead to de Boor's First Corollary: -.QP -.I -Variables are your friends. -.LP -Once you've written the makefile comes the sometimes-difficult task of -.Ix 0 ref debugging -making sure the darn thing works. Your most helpful tool to make sure -the makefile is at least syntactically correct is the -.B \-n -.Ix 0 ref flags -n -flag, which allows you to see if PMake will choke on the makefile. The -second thing the -.B \-n -flag lets you do is see what PMake would do without it actually doing -it, thus you can make sure the right commands would be executed were -you to give PMake its head. -.LP -When you find your makefile isn't behaving as you hoped, the first -question that comes to mind (after ``What time is it, anyway?'') is -``Why not?'' In answering this, two flags will serve you well: -.CW "-d m" '' `` -.Ix 0 ref flags -d -and -.CW "-p 2" .'' `` -.Ix 0 ref flags -p -The first causes PMake to tell you as it examines each target in the -makefile and indicate why it is deciding whatever it is deciding. You -can then use the information printed for other targets to see where -you went wrong. The -.CW "-p 2" '' `` -flag makes PMake print out its internal state when it is done, -allowing you to see that you forgot to make that one chapter depend on -that file of macros you just got a new version of. The output from -.CW "-p 2" '' `` -is intended to resemble closely a real makefile, but with additional -information provided and with variables expanded in those commands -PMake actually printed or executed. -.LP -Something to be especially careful about is circular dependencies. -.Ix 0 def dependency circular -E.g. -.DS -a : b -b : c d -d : a -.DE -In this case, because of how PMake works, -.CW c -is the only thing PMake will examine, because -.CW d -and -.CW a -will effectively fall off the edge of the universe, making it -impossible to examine -.CW b -(or them, for that matter). -PMake will tell you (if run in its normal mode) all the targets -involved in any cycle it looked at (i.e. if you have two cycles in the -graph (naughty, naughty), but only try to make a target in one of -them, PMake will only tell you about that one. You'll have to try to -make the other to find the second cycle). When run as Make, it will -only print the first target in the cycle. -.xH 2 Invoking PMake -.LP -.Ix 0 ref flags -.Ix 0 ref arguments -.Ix 0 ref usage -PMake comes with a wide variety of flags to choose from. -They may appear in any order, interspersed with command-line variable -assignments and targets to create. -The flags are as follows: -.IP "\fB\-d\fP \fIwhat\fP" -.Ix 0 def flags -d -.Ix 0 ref debugging -This causes PMake to spew out debugging information that -may prove useful to you. If you can't -figure out why PMake is doing what it's doing, you might try using -this flag. The -.I what -parameter is a string of single characters that tell PMake what -aspects you are interested in. Most of what I describe will make -little sense to you, unless you've dealt with Make before. Just -remember where this table is and come back to it as you read on. -The characters and the information they produce are as follows: -.RS -.IP a -Archive searching and caching. -.IP c -Conditional evaluation. -.IP d -The searching and caching of directories. -.IP j -Various snippets of information related to the running of the multiple -shells. Not particularly interesting. -.IP m -The making of each target: what target is being examined; when it was -last modified; whether it is out-of-date; etc. -.IP p -Makefile parsing. -.IP r -Remote execution. -.IP s -The application of suffix-transformation rules. (See chapter 3) -.IP t -The maintenance of the list of targets. -.IP v -Variable assignment. -.RE -.IP "\&" -Of these all, the -.CW m -and -.CW s -letters will be most useful to you. -If the -.B \-d -is the final argument or the argument from which it would get these -key letters (see below for a note about which argument would be used) -begins with a -.B \- , -all of these debugging flags will be set, resulting in massive amounts -of output. -.IP "\fB\-f\fP \fImakefile\fP" -.Ix 0 def flags -f -Specify a makefile to read different from the standard makefiles -.CW Makefile "\&" ( -or -.CW makefile ). -.Ix 0 ref makefile default -.Ix 0 ref makefile other -If -.I makefile -is ``\-'', PMake uses the standard input. This is useful for making -quick and dirty makefiles.\|.\|. -.Ix 0 ref makefile "quick and dirty" -.IP \fB\-h\fP -.Ix 0 def flags -h -Prints out a summary of the various flags PMake accepts. It can also -be used to find out what level of concurrency was compiled into the -version of PMake you are using (look at -.B \-J -and -.B \-L ) -and various other information on how PMake was configured. -.Ix 0 ref configuration -.Ix 0 ref makefile system -.IP \fB\-i\fP -.Ix 0 def flags -i -If you give this flag, PMake will ignore non-zero status returned -by any of its shells. It's like placing a `\-' before all the commands -in the makefile. -.IP \fB\-k\fP -.Ix 0 def flags -k -This is similar to -.B \-i -in that it allows PMake to continue when it sees an error, but unlike -.B \-i , -where PMake continues blithely as if nothing went wrong, -.B \-k -causes it to recognize the error and only continue work on those -things that don't depend on the target, either directly or indirectly (through -depending on something that depends on it), whose creation returned the error. -The `k' is for ``keep going''.\|.\|. -.Ix 0 ref target -.IP \fB\-l\fP -.Ix 0 def flags -l -PMake has the ability to lock a directory against other -people executing it in the same directory (by means of a file called -``LOCK.make'' that it creates and checks for in the directory). This -is a Good Thing because two people doing the same thing in the same place -can be disastrous for the final product (too many cooks and all that). -Whether this locking is the default is up to your system -administrator. If locking is on, -.B \-l -will turn it off, and vice versa. Note that this locking will not -prevent \fIyou\fP from invoking PMake twice in the same place \*- if -you own the lock file, PMake will warn you about it but continue to execute. -.IP "\fB\-m\fP \fIdirectory\fP" -.Ix 0 def flags -m -Tells PMake another place to search for included makefiles via the <...> -style. Several -.B \-m -options can be given to form a search path. If this construct is used the -default system makefile search path is completely overridden. -To be explained in chapter 3, section 3.2. -.Rm 2 3.2 -.IP \fB\-n\fP -.Ix 0 def flags -n -This flag tells PMake not to execute the commands needed to update the -out-of-date targets in the makefile. Rather, PMake will simply print -the commands it would have executed and exit. This is particularly -useful for checking the correctness of a makefile. If PMake doesn't do -what you expect it to, it's a good chance the makefile is wrong. -.IP "\fB\-p\fP \fInumber\fP" -.Ix 0 def flags -p -.Ix 0 ref debugging -This causes PMake to print its input in a reasonable form, though -not necessarily one that would make immediate sense to anyone but me. The -.I number -is a bitwise-or of 1 and 2 where 1 means it should print the input -before doing any processing and 2 says it should print it after -everything has been re-created. Thus -.CW "\-p 3" -would print it twice\*-once before processing and once after (you -might find the difference between the two interesting). This is mostly -useful to me, but you may find it informative in some bizarre circumstances. -.IP \fB\-q\fP -.Ix 0 def flags -q -If you give PMake this flag, it will not try to re-create anything. It -will just see if anything is out-of-date and exit non-zero if so. -.IP \fB\-r\fP -.Ix 0 def flags -r -When PMake starts up, it reads a default makefile that tells it what -sort of system it's on and gives it some idea of what to do if you -don't tell it anything. I'll tell you about it in chapter 3. If you -give this flag, PMake won't read the default makefile. -.IP \fB\-s\fP -.Ix 0 def flags -s -This causes PMake to not print commands before they're executed. It -is the equivalent of putting an `@' before every command in the -makefile. -.IP \fB\-t\fP -.Ix 0 def flags -t -Rather than try to re-create a target, PMake will simply ``touch'' it -so as to make it appear up-to-date. If the target didn't exist before, -it will when PMake finishes, but if the target did exist, it will -appear to have been updated. -.IP \fB\-v\fP -.Ix 0 def flags -v -This is a mixed-compatibility flag intended to mimic the System V -version of Make. It is the same as giving -.B \-B , -and -.B \-V -as well as turning off directory locking. Targets can still be created -in parallel, however. This is the mode PMake will enter if it is -invoked either as -.CW smake '' `` -or -.CW vmake ''. `` -.IP \fB\-x\fP -.Ix 0 def flags -x -This tells PMake it's ok to export jobs to other machines, if they're -available. It is used when running in Make mode, as exporting in this -mode tends to make things run slower than if the commands were just -executed locally. -.IP \fB\-B\fP -.Ix 0 ref compatibility -.Ix 0 def flags -B -Forces PMake to be as backwards-compatible with Make as possible while -still being itself. -This includes: -.RS -.IP \(bu 2 -Executing one shell per shell command -.IP \(bu 2 -Expanding anything that looks even vaguely like a variable, with the -empty string replacing any variable PMake doesn't know. -.IP \(bu 2 -Refusing to allow you to escape a `#' with a backslash. -.IP \(bu 2 -Permitting undefined variables on dependency lines and conditionals -(see below). Normally this causes PMake to abort. -.RE -.IP \fB\-C\fP -.Ix 0 def flags -C -This nullifies any and all compatibility mode flags you may have given -or implied up to the time the -.B \-C -is encountered. It is useful mostly in a makefile that you wrote for PMake -to avoid bad things happening when someone runs PMake as -.CW make '' `` -or has things set in the environment that tell it to be compatible. -.B \-C -is -.I not -placed in the -.CW PMAKE -environment variable or the -.CW .MAKEFLAGS -or -.CW MFLAGS -global variables. -.Ix 0 ref variable environment PMAKE -.Ix 0 ref variable global .MAKEFLAGS -.Ix 0 ref variable global MFLAGS -.Ix 0 ref .MAKEFLAGS variable -.Ix 0 ref MFLAGS -.IP "\fB\-D\fP \fIvariable\fP" -.Ix 0 def flags -D -Allows you to define a variable to have -.CW 1 '' `` -as its value. The variable is a global variable, not a command-line -variable. This is useful mostly for people who are used to the C -compiler arguments and those using conditionals, which I'll get into -in section 4.3 -.Rm 1 4.3 -.IP "\fB\-I\fP \fIdirectory\fP" -.Ix 0 def flags -I -Tells PMake another place to search for included makefiles. Yet -another thing to be explained in chapter 3 (section 3.2, to be -precise). -.Rm 2 3.2 -.IP "\fB\-J\fP \fInumber\fP" -.Ix 0 def flags -J -Gives the absolute maximum number of targets to create at once on both -local and remote machines. -.IP "\fB\-L\fP \fInumber\fP" -.Ix 0 def flags -L -This specifies the maximum number of targets to create on the local -machine at once. This may be 0, though you should be wary of doing -this, as PMake may hang until a remote machine becomes available, if -one is not available when it is started. -.IP \fB\-M\fP -.Ix 0 ref compatibility -.Ix 0 def flags -M -This is the flag that provides absolute, complete, full compatibility -with Make. It still allows you to use all but a few of the features of -PMake, but it is non-parallel. This is the mode PMake enters if you -call it -.CW make .'' `` -.IP \fB\-P\fP -.Ix 0 def flags -P -.Ix 0 ref "output control" -When creating targets in parallel, several shells are executing at -once, each wanting to write its own two cent's-worth to the screen. -This output must be captured by PMake in some way in order to prevent -the screen from being filled with garbage even more indecipherable -than you usually see. PMake has two ways of doing this, one of which -provides for much cleaner output and a clear separation between the -output of different jobs, the other of which provides a more immediate -response so one can tell what is really happening. The former is done -by notifying you when the creation of a target starts, capturing the -output and transferring it to the screen all at once when the job -finishes. The latter is done by catching the output of the shell (and -its children) and buffering it until an entire line is received, then -printing that line preceded by an indication of which job produced -the output. Since I prefer this second method, it is the one used by -default. The first method will be used if you give the -.B \-P -flag to PMake. -.IP \fB\-V\fP -.Ix 0 def flags -V -As mentioned before, the -.B \-V -flag tells PMake to use Make's style of expanding variables, -substituting the empty string for any variable it doesn't know. -.IP \fB\-W\fP -.Ix 0 def flags -W -There are several times when PMake will print a message at you that is -only a warning, i.e. it can continue to work in spite of your having -done something silly (such as forgotten a leading tab for a shell -command). Sometimes you are well aware of silly things you have done -and would like PMake to stop bothering you. This flag tells it to shut -up about anything non-fatal. -.IP \fB\-X\fP -.Ix 0 def flags -X -This flag causes PMake to not attempt to export any jobs to another -machine. -.LP -Several flags may follow a single `\-'. Those flags that require -arguments take them from successive parameters. E.g. -.DS -pmake -fDnI server.mk DEBUG /chip2/X/server/include -.DE -will cause PMake to read -.CW server.mk -as the input makefile, define the variable -.CW DEBUG -as a global variable and look for included makefiles in the directory -.CW /chip2/X/server/include . -.xH 2 Summary -.LP -A makefile is made of four types of lines: -.RS -.IP \(bu 2 -Dependency lines -.IP \(bu 2 -Creation commands -.IP \(bu 2 -Variable assignments -.IP \(bu 2 -Comments, include statements and conditional directives -.RE -.LP -A dependency line is a list of one or more targets, an operator -.CW : ', (` -.CW :: ', ` -or -.CW ! '), ` -and a list of zero or more sources. Sources may contain wildcards and -certain local variables. -.LP -A creation command is a regular shell command preceded by a tab. In -addition, if the first two characters after the tab (and other -whitespace) are a combination of -.CW @ ' ` -or -.CW - ', ` -PMake will cause the command to not be printed (if the character is -.CW @ ') ` -or errors from it to be ignored (if -.CW - '). ` -A blank line, dependency line or variable assignment terminates a -creation script. There may be only one creation script for each target -with a -.CW : ' ` -or -.CW ! ' ` -operator. -.LP -Variables are places to store text. They may be unconditionally -assigned-to using the -.CW = ' ` -.Ix 0 ref = -.Ix 0 ref variable assignment -operator, appended-to using the -.CW += ' ` -.Ix 0 ref += -.Ix 0 ref variable assignment appended -operator, conditionally (if the variable is undefined) assigned-to -with the -.CW ?= ' ` -.Ix 0 ref ?= -.Ix 0 ref variable assignment conditional -operator, and assigned-to with variable expansion with the -.CW := ' ` -.Ix 0 ref := -.Ix 0 ref variable assignment expanded -operator. The output of a shell command may be assigned to a variable -using the -.CW != ' ` -.Ix 0 ref != -.Ix 0 ref variable assignment shell-output -operator. Variables may be expanded (their value inserted) by enclosing -their name in parentheses or curly braces, preceded by a dollar sign. -A dollar sign may be escaped with another dollar sign. Variables are -not expanded if PMake doesn't know about them. There are seven local -variables: -.CW .TARGET , -.CW .ALLSRC , -.CW .OODATE , -.CW .PREFIX , -.CW .IMPSRC , -.CW .ARCHIVE , -and -.CW .MEMBER . -Four of them -.CW .TARGET , ( -.CW .PREFIX , -.CW .ARCHIVE , -and -.CW .MEMBER ) -may be used to specify ``dynamic sources.'' -.Ix 0 ref "dynamic source" -.Ix 0 ref source dynamic -Variables are good. Know them. Love them. Live them. -.LP -Debugging of makefiles is best accomplished using the -.B \-n , -.B "\-d m" , -and -.B "\-p 2" -flags. -.xH 2 Exercises -.ce -\s+4\fBTBA\fP\s0 -.xH 1 Short-cuts and Other Nice Things -.LP -Based on what I've told you so far, you may have gotten the impression -that PMake is just a way of storing away commands and making sure you -don't forget to compile something. Good. That's just what it is. -However, the ways I've described have been inelegant, at best, and -painful, at worst. -This chapter contains things that make the -writing of makefiles easier and the makefiles themselves shorter and -easier to modify (and, occasionally, simpler). In this chapter, I -assume you are somewhat more -familiar with Sprite (or UNIX, if that's what you're using) than I did -in chapter 2, just so you're on your toes. -So without further ado... -.xH 2 Transformation Rules -.LP -As you know, a file's name consists of two parts: a base name, which -gives some hint as to the contents of the file, and a suffix, which -usually indicates the format of the file. -Over the years, as -.UX -has developed, -naming conventions, with regard to suffixes, have also developed that have -become almost as incontrovertible as Law. E.g. a file ending in -.CW .c -is assumed to contain C source code; one with a -.CW .o -suffix is assumed to be a compiled, relocatable object file that may -be linked into any program; a file with a -.CW .ms -suffix is usually a text file to be processed by Troff with the \-ms -macro package, and so on. -One of the best aspects of both Make and PMake comes from their -understanding of how the suffix of a file pertains to its contents and -their ability to do things with a file based solely on its suffix. This -ability comes from something known as a transformation rule. A -transformation rule specifies how to change a file with one suffix -into a file with another suffix. -.LP -A transformation rule looks much like a dependency line, except the -target is made of two known suffixes stuck together. Suffixes are made -known to PMake by placing them as sources on a dependency line whose -target is the special target -.CW .SUFFIXES . -E.g. -.DS -\&.SUFFIXES : .o .c -\&.c.o : - $(CC) $(CFLAGS) -c $(.IMPSRC) -.DE -The creation script attached to the target is used to transform a file with -the first suffix (in this case, -.CW .c ) -into a file with the second suffix (here, -.CW .o ). -In addition, the target inherits whatever attributes have been applied -to the transformation rule. -The simple rule given above says that to transform a C source file -into an object file, you compile it using -.CW cc -with the -.CW \-c -flag. -This rule is taken straight from the system makefile. Many -transformation rules (and suffixes) are defined there, and I refer you -to it for more examples (type -.CW "pmake -h" '' `` -to find out where it is). -.LP -There are several things to note about the transformation rule given -above: -.RS -.IP 1) -The -.CW .IMPSRC -variable. -.Ix 0 def variable local .IMPSRC -.Ix 0 def .IMPSRC -This variable is set to the ``implied source'' (the file from which -the target is being created; the one with the first suffix), which, in this -case, is the .c file. -.IP 2) -The -.CW CFLAGS -variable. Almost all of the transformation rules in the system -makefile are set up using variables that you can alter in your -makefile to tailor the rule to your needs. In this case, if you want -all your C files to be compiled with the -.B \-g -flag, to provide information for -.CW dbx , -you would set the -.CW CFLAGS -variable to contain -.CW -g -.CW "CFLAGS = -g" '') (`` -and PMake would take care of the rest. -.RE -.LP -To give you a quick example, the makefile in 2.3.4 -.Rm 3 2.3.4 -could be changed to this: -.DS -OBJS = a.o b.o c.o -program : $(OBJS) - $(CC) -o $(.TARGET) $(.ALLSRC) -$(OBJS) : defs.h -.DE -The transformation rule I gave above takes the place of the 6 lines\** -.FS -This is also somewhat cleaner, I think, than the dynamic source -solution presented in 2.6 -.FE -.Rm 4 2.6 -.DS -a.o : a.c - cc -c a.c -b.o : b.c - cc -c b.c -c.o : c.c - cc -c c.c -.DE -.LP -Now you may be wondering about the dependency between the -.CW .o -and -.CW .c -files \*- it's not mentioned anywhere in the new makefile. This is -because it isn't needed: one of the effects of applying a -transformation rule is the target comes to depend on the implied -source. That's why it's called the implied -.I source . -.LP -For a more detailed example. Say you have a makefile like this: -.DS -a.out : a.o b.o - $(CC) $(.ALLSRC) -.DE -and a directory set up like this: -.DS -total 4 --rw-rw-r-- 1 deboor 34 Sep 7 00:43 Makefile --rw-rw-r-- 1 deboor 119 Oct 3 19:39 a.c --rw-rw-r-- 1 deboor 201 Sep 7 00:43 a.o --rw-rw-r-- 1 deboor 69 Sep 7 00:43 b.c -.DE -While just typing -.CW pmake '' `` -will do the right thing, it's much more informative to type -.CW "pmake -d s" ''. `` -This will show you what PMake is up to as it processes the files. In -this case, PMake prints the following: -.DS -Suff_FindDeps (a.out) - using existing source a.o - applying .o -> .out to "a.o" -Suff_FindDeps (a.o) - trying a.c...got it - applying .c -> .o to "a.c" -Suff_FindDeps (b.o) - trying b.c...got it - applying .c -> .o to "b.c" -Suff_FindDeps (a.c) - trying a.y...not there - trying a.l...not there - trying a.c,v...not there - trying a.y,v...not there - trying a.l,v...not there -Suff_FindDeps (b.c) - trying b.y...not there - trying b.l...not there - trying b.c,v...not there - trying b.y,v...not there - trying b.l,v...not there ---- a.o --- -cc -c a.c ---- b.o --- -cc -c b.c ---- a.out --- -cc a.o b.o -.DE -.LP -.CW Suff_FindDeps -is the name of a function in PMake that is called to check for implied -sources for a target using transformation rules. -The transformations it tries are, naturally -enough, limited to the ones that have been defined (a transformation -may be defined multiple times, by the way, but only the most recent -one will be used). You will notice, however, that there is a definite -order to the suffixes that are tried. This order is set by the -relative positions of the suffixes on the -.CW .SUFFIXES -line \*- the earlier a suffix appears, the earlier it is checked as -the source of a transformation. Once a suffix has been defined, the -only way to change its position in the pecking order is to remove all -the suffixes (by having a -.CW .SUFFIXES -dependency line with no sources) and redefine them in the order you -want. (Previously-defined transformation rules will be automatically -redefined as the suffixes they involve are re-entered.) -.LP -Another way to affect the search order is to make the dependency -explicit. In the above example, -.CW a.out -depends on -.CW a.o -and -.CW b.o . -Since a transformation exists from -.CW .o -to -.CW .out , -PMake uses that, as indicated by the -.CW "using existing source a.o" '' `` -message. -.LP -The search for a transformation starts from the suffix of the target -and continues through all the defined transformations, in the order -dictated by the suffix ranking, until an existing file with the same -base (the target name minus the suffix and any leading directories) is -found. At that point, one or more transformation rules will have been -found to change the one existing file into the target. -.LP -For example, ignoring what's in the system makefile for now, say you -have a makefile like this: -.DS -\&.SUFFIXES : .out .o .c .y .l -\&.l.c : - lex $(.IMPSRC) - mv lex.yy.c $(.TARGET) -\&.y.c : - yacc $(.IMPSRC) - mv y.tab.c $(.TARGET) -\&.c.o : - cc -c $(.IMPSRC) -\&.o.out : - cc -o $(.TARGET) $(.IMPSRC) -.DE -and the single file -.CW jive.l . -If you were to type -.CW "pmake -rd ms jive.out" ,'' `` -you would get the following output for -.CW jive.out : -.DS -Suff_FindDeps (jive.out) - trying jive.o...not there - trying jive.c...not there - trying jive.y...not there - trying jive.l...got it - applying .l -> .c to "jive.l" - applying .c -> .o to "jive.c" - applying .o -> .out to "jive.o" -.DE -and this is why: PMake starts with the target -.CW jive.out , -figures out its suffix -.CW .out ) ( -and looks for things it can transform to a -.CW .out -file. In this case, it only finds -.CW .o , -so it looks for the file -.CW jive.o . -It fails to find it, so it looks for transformations into a -.CW .o -file. Again it has only one choice: -.CW .c . -So it looks for -.CW jive.c -and, as you know, fails to find it. At this point it has two choices: -it can create the -.CW .c -file from either a -.CW .y -file or a -.CW .l -file. Since -.CW .y -came first on the -.CW .SUFFIXES -line, it checks for -.CW jive.y -first, but can't find it, so it looks for -.CW jive.l -and, lo and behold, there it is. -At this point, it has defined a transformation path as follows: -.CW .l -\(-> -.CW .c -\(-> -.CW .o -\(-> -.CW .out -and applies the transformation rules accordingly. For completeness, -and to give you a better idea of what PMake actually did with this -three-step transformation, this is what PMake printed for the rest of -the process: -.DS -Suff_FindDeps (jive.o) - using existing source jive.c - applying .c -> .o to "jive.c" -Suff_FindDeps (jive.c) - using existing source jive.l - applying .l -> .c to "jive.l" -Suff_FindDeps (jive.l) -Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date -Examining jive.c...non-existent...out-of-date ---- jive.c --- -lex jive.l -\&.\|.\|. meaningless lex output deleted .\|.\|. -mv lex.yy.c jive.c -Examining jive.o...non-existent...out-of-date ---- jive.o --- -cc -c jive.c -Examining jive.out...non-existent...out-of-date ---- jive.out --- -cc -o jive.out jive.o -.DE -.LP -One final question remains: what does PMake do with targets that have -no known suffix? PMake simply pretends it actually has a known suffix -and searches for transformations accordingly. -The suffix it chooses is the source for the -.CW .NULL -.Ix 0 ref .NULL -target mentioned later. In the system makefile, -.CW .out -is chosen as the ``null suffix'' -.Ix 0 def suffix null -.Ix 0 def "null suffix" -because most people use PMake to create programs. You are, however, -free and welcome to change it to a suffix of your own choosing. -The null suffix is ignored, however, when PMake is in compatibility -mode (see chapter 4). -.xH 2 Including Other Makefiles -.Ix 0 def makefile inclusion -.Rd 2 -.LP -Just as for programs, it is often useful to extract certain parts of a -makefile into another file and just include it in other makefiles -somehow. Many compilers allow you say something like -.DS -#include "defs.h" -.DE -to include the contents of -.CW defs.h -in the source file. PMake allows you to do the same thing for -makefiles, with the added ability to use variables in the filenames. -An include directive in a makefile looks either like this: -.DS -#include <file> -.DE -or this -.DS -#include "file" -.DE -The difference between the two is where PMake searches for the file: -the first way, PMake will look for -the file only in the system makefile directory (or directories) -(to find out what that directory is, give PMake the -.B \-h -flag). -.Ix 0 ref flags -h -The system makefile directory search path can be overridden via the -.B \-m -option. -.Ix 0 ref flags -m -For files in double-quotes, the search is more complex: -.RS -.IP 1) -The directory of the makefile that's including the file. -.IP 2) -The current directory (the one in which you invoked PMake). -.IP 3) -The directories given by you using -.B \-I -flags, in the order in which you gave them. -.IP 4) -Directories given by -.CW .PATH -dependency lines (see chapter 4). -.IP 5) -The system makefile directory. -.RE -.LP -in that order. -.LP -You are free to use PMake variables in the filename\*-PMake will -expand them before searching for the file. You must specify the -searching method with either angle brackets or double-quotes -.I outside -of a variable expansion. I.e. the following -.DS -SYSTEM = <command.mk> - -#include $(SYSTEM) -.DE -won't work. -.xH 2 Saving Commands -.LP -.Ix 0 def ... -There may come a time when you will want to save certain commands to -be executed when everything else is done. For instance: you're -making several different libraries at one time and you want to create the -members in parallel. Problem is, -.CW ranlib -is another one of those programs that can't be run more than once in -the same directory at the same time (each one creates a file called -.CW __.SYMDEF -into which it stuffs information for the linker to use. Two of them -running at once will overwrite each other's file and the result will -be garbage for both parties). You might want a way to save the ranlib -commands til the end so they can be run one after the other, thus -keeping them from trashing each other's file. PMake allows you to do -this by inserting an ellipsis (``.\|.\|.'') as a command between -commands to be run at once and those to be run later. -.LP -So for the -.CW ranlib -case above, you might do this: -.Rd 5 -.DS -lib1.a : $(LIB1OBJS) - rm -f $(.TARGET) - ar cr $(.TARGET) $(.ALLSRC) - ... - ranlib $(.TARGET) - -lib2.a : $(LIB2OBJS) - rm -f $(.TARGET) - ar cr $(.TARGET) $(.ALLSRC) - ... - ranlib $(.TARGET) -.DE -.Ix 0 ref variable local .TARGET -.Ix 0 ref variable local .ALLSRC -This would save both -.DS -ranlib $(.TARGET) -.DE -commands until the end, when they would run one after the other -(using the correct value for the -.CW .TARGET -variable, of course). -.LP -Commands saved in this manner are only executed if PMake manages to -re-create everything without an error. -.xH 2 Target Attributes -.LP -PMake allows you to give attributes to targets by means of special -sources. Like everything else PMake uses, these sources begin with a -period and are made up of all upper-case letters. There are various -reasons for using them, and I will try to give examples for most of -them. Others you'll have to find uses for yourself. Think of it as ``an -exercise for the reader.'' By placing one (or more) of these as a source on a -dependency line, you are ``marking the target(s) with that -attribute.'' That's just the way I phrase it, so you know. -.LP -Any attributes given as sources for a transformation rule are applied -to the target of the transformation rule when the rule is applied. -.Ix 0 def attributes -.Ix 0 ref source -.Ix 0 ref target -.nr pw 12 -.IP .DONTCARE \n(pw -.Ix 0 def attributes .DONTCARE -.Ix 0 def .DONTCARE -If a target is marked with this attribute and PMake can't figure out -how to create it, it will ignore this fact and assume the file isn't -really needed or actually exists and PMake just can't find it. This may prove -wrong, but the error will be noted later on, not when PMake tries to create -the target so marked. This attribute also prevents PMake from -attempting to touch the target if it is given the -.B \-t -flag. -.Ix 0 ref flags -t -.IP .EXEC \n(pw -.Ix 0 def attributes .EXEC -.Ix 0 def .EXEC -This attribute causes its shell script to be executed while having no -effect on targets that depend on it. This makes the target into a sort -of subroutine. An example. Say you have some LISP files that need to -be compiled and loaded into a LISP process. To do this, you echo LISP -commands into a file and execute a LISP with this file as its input -when everything's done. Say also that you have to load other files -from another system before you can compile your files and further, -that you don't want to go through the loading and dumping unless one -of -.I your -files has changed. Your makefile might look a little bit -like this (remember, this is an educational example, and don't worry -about the -.CW COMPILE -rule, all will soon become clear, grasshopper): -.DS -system : init a.fasl b.fasl c.fasl - for i in $(.ALLSRC); - do - echo -n '(load "' >> input - echo -n ${i} >> input - echo '")' >> input - done - echo '(dump "$(.TARGET)")' >> input - lisp < input - -a.fasl : a.l init COMPILE -b.fasl : b.l init COMPILE -c.fasl : c.l init COMPILE -COMPILE : .USE - echo '(compile "$(.ALLSRC)")' >> input -init : .EXEC - echo '(load-system)' > input -.DE -.Ix 0 ref .USE -.Ix 0 ref attributes .USE -.Ix 0 ref variable local .ALLSRC -.IP "\&" -.CW .EXEC -sources, don't appear in the local variables of targets that depend on -them (nor are they touched if PMake is given the -.B \-t -flag). -.Ix 0 ref flags -t -Note that all the rules, not just that for -.CW system , -include -.CW init -as a source. This is because none of the other targets can be made -until -.CW init -has been made, thus they depend on it. -.IP .EXPORT \n(pw -.Ix 0 def attributes .EXPORT -.Ix 0 def .EXPORT -This is used to mark those targets whose creation should be sent to -another machine if at all possible. This may be used by some -exportation schemes if the exportation is expensive. You should ask -your system administrator if it is necessary. -.IP .EXPORTSAME \n(pw -.Ix 0 def attributes .EXPORTSAME -.Ix 0 def .EXPORTSAME -Tells the export system that the job should be exported to a machine -of the same architecture as the current one. Certain operations (e.g. -running text through -.CW nroff ) -can be performed the same on any architecture (CPU and -operating system type), while others (e.g. compiling a program with -.CW cc ) -must be performed on a machine with the same architecture. Not all -export systems will support this attribute. -.IP .IGNORE \n(pw -.Ix 0 def attributes .IGNORE -.Ix 0 def .IGNORE attribute -Giving a target the -.CW .IGNORE -attribute causes PMake to ignore errors from any of the target's commands, as -if they all had `\-' before them. -.IP .INVISIBLE \n(pw -.Ix 0 def attributes .INVISIBLE -.Ix 0 def .INVISIBLE -This allows you to specify one target as a source for another without -the one affecting the other's local variables. Useful if, say, you -have a makefile that creates two programs, one of which is used to -create the other, so it must exist before the other is created. You -could say -.DS -prog1 : $(PROG1OBJS) prog2 MAKEINSTALL -prog2 : $(PROG2OBJS) .INVISIBLE MAKEINSTALL -.DE -where -.CW MAKEINSTALL -is some complex .USE rule (see below) that depends on the -.Ix 0 ref .USE -.CW .ALLSRC -variable containing the right things. Without the -.CW .INVISIBLE -attribute for -.CW prog2 , -the -.CW MAKEINSTALL -rule couldn't be applied. This is not as useful as it should be, and -the semantics may change (or the whole thing go away) in the -not-too-distant future. -.IP .JOIN \n(pw -.Ix 0 def attributes .JOIN -.Ix 0 def .JOIN -This is another way to avoid performing some operations in parallel -while permitting everything else to be done so. Specifically it -forces the target's shell script to be executed only if one or more of the -sources was out-of-date. In addition, the target's name, -in both its -.CW .TARGET -variable and all the local variables of any target that depends on it, -is replaced by the value of its -.CW .ALLSRC -variable. -As an example, suppose you have a program that has four libraries that -compile in the same directory along with, and at the same time as, the -program. You again have the problem with -.CW ranlib -that I mentioned earlier, only this time it's more severe: you -can't just put the ranlib off to the end since the program -will need those libraries before it can be re-created. You can do -something like this: -.DS -program : $(OBJS) libraries - cc -o $(.TARGET) $(.ALLSRC) - -libraries : lib1.a lib2.a lib3.a lib4.a .JOIN - ranlib $(.OODATE) -.DE -.Ix 0 ref variable local .TARGET -.Ix 0 ref variable local .ALLSRC -.Ix 0 ref variable local .OODATE -.Ix 0 ref .TARGET -.Ix 0 ref .ALLSRC -.Ix 0 ref .OODATE -In this case, PMake will re-create the -.CW $(OBJS) -as necessary, along with -.CW lib1.a , -.CW lib2.a , -.CW lib3.a -and -.CW lib4.a . -It will then execute -.CW ranlib -on any library that was changed and set -.CW program 's -.CW .ALLSRC -variable to contain what's in -.CW $(OBJS) -followed by -.CW "lib1.a lib2.a lib3.a lib4.a" .'' `` -In case you're wondering, it's called -.CW .JOIN -because it joins together different threads of the ``input graph'' at -the target marked with the attribute. -Another aspect of the .JOIN attribute is it keeps the target from -being created if the -.B \-t -flag was given. -.Ix 0 ref flags -t -.IP .MAKE \n(pw -.Ix 0 def attributes .MAKE -.Ix 0 def .MAKE -The -.CW .MAKE -attribute marks its target as being a recursive invocation of PMake. -This forces PMake to execute the script associated with the target (if -it's out-of-date) even if you gave the -.B \-n -or -.B \-t -flag. By doing this, you can start at the top of a system and type -.DS -pmake -n -.DE -and have it descend the directory tree (if your makefiles are set up -correctly), printing what it would have executed if you hadn't -included the -.B \-n -flag. -.IP .NOEXPORT \n(pw -.Ix 0 def attributes .NOEXPORT -.Ix 0 def .NOEXPORT attribute -If possible, PMake will attempt to export the creation of all targets to -another machine (this depends on how PMake was configured). Sometimes, -the creation is so simple, it is pointless to send it to another -machine. If you give the target the -.CW .NOEXPORT -attribute, it will be run locally, even if you've given PMake the -.B "\-L 0" -flag. -.IP .NOTMAIN \n(pw -.Ix 0 def attributes .NOTMAIN -.Ix 0 def .NOTMAIN -Normally, if you do not specify a target to make in any other way, -PMake will take the first target on the first dependency line of a -makefile as the target to create. That target is known as the ``Main -Target'' and is labeled as such if you print the dependencies out -using the -.B \-p -flag. -.Ix 0 ref flags -p -Giving a target this attribute tells PMake that the target is -definitely -.I not -the Main Target. -This allows you to place targets in an included makefile and -have PMake create something else by default. -.IP .PRECIOUS \n(pw -.Ix 0 def attributes .PRECIOUS -.Ix 0 def .PRECIOUS attribute -When PMake is interrupted (you type control-C at the keyboard), it -will attempt to clean up after itself by removing any half-made -targets. If a target has the -.CW .PRECIOUS -attribute, however, PMake will leave it alone. An additional side -effect of the `::' operator is to mark the targets as -.CW .PRECIOUS . -.Ix 0 ref operator double-colon -.Ix 0 ref :: -.IP .SILENT \n(pw -.Ix 0 def attributes .SILENT -.Ix 0 def .SILENT attribute -Marking a target with this attribute keeps its commands from being -printed when they're executed, just as if they had an `@' in front of them. -.IP .USE \n(pw -.Ix 0 def attributes .USE -.Ix 0 def .USE -By giving a target this attribute, you turn it into PMake's equivalent -of a macro. When the target is used as a source for another target, -the other target acquires the commands, sources and attributes (except -.CW .USE ) -of the source. -If the target already has commands, the -.CW .USE -target's commands are added to the end. If more than one .USE-marked -source is given to a target, the rules are applied sequentially. -.IP "\&" \n(pw -The typical .USE rule (as I call them) will use the sources of the -target to which it is applied (as stored in the -.CW .ALLSRC -variable for the target) as its ``arguments,'' if you will. -For example, you probably noticed that the commands for creating -.CW lib1.a -and -.CW lib2.a -in the example in section 3.3 -.Rm 5 3.3 -were exactly the same. You can use the -.CW .USE -attribute to eliminate the repetition, like so: -.DS -lib1.a : $(LIB1OBJS) MAKELIB -lib2.a : $(LIB2OBJS) MAKELIB - -MAKELIB : .USE - rm -f $(.TARGET) - ar cr $(.TARGET) $(.ALLSRC) - ... - ranlib $(.TARGET) -.DE -.Ix 0 ref variable local .TARGET -.Ix 0 ref variable local .ALLSRC -.IP "\&" \n(pw -Several system makefiles (not to be confused with The System Makefile) -make use of these .USE rules to make your -life easier (they're in the default, system makefile directory...take a look). -Note that the .USE rule source itself -.CW MAKELIB ) ( -does not appear in any of the targets's local variables. -There is no limit to the number of times I could use the -.CW MAKELIB -rule. If there were more libraries, I could continue with -.CW "lib3.a : $(LIB3OBJS) MAKELIB" '' `` -and so on and so forth. -.xH 2 Special Targets -.LP -As there were in Make, so there are certain targets that have special -meaning to PMake. When you use one on a dependency line, it is the -only target that may appear on the left-hand-side of the operator. -.Ix 0 ref target -.Ix 0 ref operator -As for the attributes and variables, all the special targets -begin with a period and consist of upper-case letters only. -I won't describe them all in detail because some of them are rather -complex and I'll describe them in more detail than you'll want in -chapter 4. -The targets are as follows: -.nr pw 10 -.IP .BEGIN \n(pw -.Ix 0 def .BEGIN -Any commands attached to this target are executed before anything else -is done. You can use it for any initialization that needs doing. -.IP .DEFAULT \n(pw -.Ix 0 def .DEFAULT -This is sort of a .USE rule for any target (that was used only as a -source) that PMake can't figure out any other way to create. It's only -``sort of'' a .USE rule because only the shell script attached to the -.CW .DEFAULT -target is used. The -.CW .IMPSRC -variable of a target that inherits -.CW .DEFAULT 's -commands is set to the target's own name. -.Ix 0 ref .IMPSRC -.Ix 0 ref variable local .IMPSRC -.IP .END \n(pw -.Ix 0 def .END -This serves a function similar to -.CW .BEGIN , -in that commands attached to it are executed once everything has been -re-created (so long as no errors occurred). It also serves the extra -function of being a place on which PMake can hang commands you put off -to the end. Thus the script for this target will be executed before -any of the commands you save with the ``.\|.\|.''. -.Ix 0 ref ... -.IP .EXPORT \n(pw -The sources for this target are passed to the exportation system compiled -into PMake. Some systems will use these sources to configure -themselves. You should ask your system administrator about this. -.IP .IGNORE \n(pw -.Ix 0 def .IGNORE target -.Ix 0 ref .IGNORE attribute -.Ix 0 ref attributes .IGNORE -This target marks each of its sources with the -.CW .IGNORE -attribute. If you don't give it any sources, then it is like -giving the -.B \-i -flag when you invoke PMake \*- errors are ignored for all commands. -.Ix 0 ref flags -i -.IP .INCLUDES \n(pw -.Ix 0 def .INCLUDES target -.Ix 0 def variable global .INCLUDES -.Ix 0 def .INCLUDES variable -The sources for this target are taken to be suffixes that indicate a -file that can be included in a program source file. -The suffix must have already been declared with -.CW .SUFFIXES -(see below). -Any suffix so marked will have the directories on its search path -(see -.CW .PATH , -below) placed in the -.CW .INCLUDES -variable, each preceded by a -.B \-I -flag. This variable can then be used as an argument for the compiler -in the normal fashion. The -.CW .h -suffix is already marked in this way in the system makefile. -.Ix 0 ref makefile system -E.g. if you have -.DS -\&.SUFFIXES : .bitmap -\&.PATH.bitmap : /usr/local/X/lib/bitmaps -\&.INCLUDES : .bitmap -.DE -PMake will place -.CW "-I/usr/local/X/lib/bitmaps" '' `` -in the -.CW .INCLUDES -variable and you can then say -.DS -cc $(.INCLUDES) -c xprogram.c -.DE -(Note: the -.CW .INCLUDES -variable is not actually filled in until the entire makefile has been read.) -.IP .INTERRUPT \n(pw -.Ix 0 def .INTERRUPT -When PMake is interrupted, -it will execute the commands in the script for this target, if it -exists. -.IP .LIBS \n(pw -.Ix 0 def .LIBS target -.Ix 0 def .LIBS variable -.Ix 0 def variable global .LIBS -This does for libraries what -.CW .INCLUDES -does for include files, except the flag used is -.B \-L , -as required by those linkers that allow you to tell them where to find -libraries. The variable used is -.CW .LIBS . -Be forewarned that PMake may not have been compiled to do this if the -linker on your system doesn't accept the -.B \-L -flag, though the -.CW .LIBS -variable will always be defined once the makefile has been read. -.IP .MAIN \n(pw -.Ix 0 def .MAIN -If you didn't give a target (or targets) to create when you invoked -PMake, it will take the sources of this target as the targets to -create. -.IP .MAKEFLAGS \n(pw -.Ix 0 def .MAKEFLAGS target -This target provides a way for you to always specify flags for PMake -when the makefile is used. The flags are just as they would be typed -to the shell (except you can't use shell variables unless they're in -the environment), -though the -.B \-f -and -.B \-r -flags have no effect. -.IP .NULL \n(pw -.Ix 0 def .NULL -.Ix 0 ref suffix null -.Ix 0 ref "null suffix" -This allows you to specify what suffix PMake should pretend a file has -if, in fact, it has no known suffix. Only one suffix may be so -designated. The last source on the dependency line is the suffix that -is used (you should, however, only give one suffix.\|.\|.). -.IP .PATH \n(pw -.Ix 0 def .PATH -If you give sources for this target, PMake will take them as -directories in which to search for files it cannot find in the current -directory. If you give no sources, it will clear out any directories -added to the search path before. Since the effects of this all get -very complex, I'll leave it til chapter four to give you a complete -explanation. -.IP .PATH\fIsuffix\fP \n(pw -.Ix 0 ref .PATH -This does a similar thing to -.CW .PATH , -but it does it only for files with the given suffix. The suffix must -have been defined already. Look at -.B "Search Paths" -(section 4.1) -.Rm 6 4.1 -for more information. -.IP .PRECIOUS \n(pw -.Ix 0 def .PRECIOUS target -.Ix 0 ref .PRECIOUS attribute -.Ix 0 ref attributes .PRECIOUS -Similar to -.CW .IGNORE , -this gives the -.CW .PRECIOUS -attribute to each source on the dependency line, unless there are no -sources, in which case the -.CW .PRECIOUS -attribute is given to every target in the file. -.IP .RECURSIVE \n(pw -.Ix 0 def .RECURSIVE -.Ix 0 ref attributes .MAKE -.Ix 0 ref .MAKE -This target applies the -.CW .MAKE -attribute to all its sources. It does nothing if you don't give it any sources. -.IP .SHELL \n(pw -.Ix 0 def .SHELL -PMake is not constrained to only using the Bourne shell to execute -the commands you put in the makefile. You can tell it some other shell -to use with this target. Check out -.B "A Shell is a Shell is a Shell" -(section 4.4) -.Rm 7 4.4 -for more information. -.IP .SILENT \n(pw -.Ix 0 def .SILENT target -.Ix 0 ref .SILENT attribute -.Ix 0 ref attributes .SILENT -When you use -.CW .SILENT -as a target, it applies the -.CW .SILENT -attribute to each of its sources. If there are no sources on the -dependency line, then it is as if you gave PMake the -.B \-s -flag and no commands will be echoed. -.IP .SUFFIXES \n(pw -.Ix 0 def .SUFFIXES -This is used to give new file suffixes for PMake to handle. Each -source is a suffix PMake should recognize. If you give a -.CW .SUFFIXES -dependency line with no sources, PMake will forget about all the -suffixes it knew (this also nukes the null suffix). -For those targets that need to have suffixes defined, this is how you do it. -.LP -In addition to these targets, a line of the form -.DS -\fIattribute\fP : \fIsources\fP -.DE -applies the -.I attribute -to all the targets listed as -.I sources . -.xH 2 Modifying Variable Expansion -.LP -.Ix 0 def variable expansion modified -.Ix 0 ref variable expansion -.Ix 0 def variable modifiers -Variables need not always be expanded verbatim. PMake defines several -modifiers that may be applied to a variable's value before it is -expanded. You apply a modifier by placing it after the variable name -with a colon between the two, like so: -.DS -${\fIVARIABLE\fP:\fImodifier\fP} -.DE -Each modifier is a single character followed by something specific to -the modifier itself. -You may apply as many modifiers as you want \*- each one is applied to -the result of the previous and is separated from the previous by -another colon. -.LP -There are seven ways to modify a variable's expansion, most of which -come from the C shell variable modification characters: -.RS -.IP "M\fIpattern\fP" -.Ix 0 def :M -.Ix 0 def modifier match -This is used to select only those words (a word is a series of -characters that are neither spaces nor tabs) that match the given -.I pattern . -The pattern is a wildcard pattern like that used by the shell, where -.CW * -means 0 or more characters of any sort; -.CW ? -is any single character; -.CW [abcd] -matches any single character that is either `a', `b', `c' or `d' -(there may be any number of characters between the brackets); -.CW [0-9] -matches any single character that is between `0' and `9' (i.e. any -digit. This form may be freely mixed with the other bracket form), and -`\\' is used to escape any of the characters `*', `?', `[' or `:', -leaving them as regular characters to match themselves in a word. -For example, the system makefile -.CW <makedepend.mk> -uses -.CW "$(CFLAGS:M-[ID]*)" '' `` -to extract all the -.CW \-I -and -.CW \-D -flags that would be passed to the C compiler. This allows it to -properly locate include files and generate the correct dependencies. -.IP "N\fIpattern\fP" -.Ix 0 def :N -.Ix 0 def modifier nomatch -This is identical to -.CW :M -except it substitutes all words that don't match the given pattern. -.IP "S/\fIsearch-string\fP/\fIreplacement-string\fP/[g]" -.Ix 0 def :S -.Ix 0 def modifier substitute -Causes the first occurrence of -.I search-string -in the variable to be replaced by -.I replacement-string , -unless the -.CW g -flag is given at the end, in which case all occurrences of the string -are replaced. The substitution is performed on each word in the -variable in turn. If -.I search-string -begins with a -.CW ^ , -the string must match starting at the beginning of the word. If -.I search-string -ends with a -.CW $ , -the string must match to the end of the word (these two may be -combined to force an exact match). If a backslash precedes these two -characters, however, they lose their special meaning. Variable -expansion also occurs in the normal fashion inside both the -.I search-string -and the -.I replacement-string , -.B except -that a backslash is used to prevent the expansion of a -.CW $ , -not another dollar sign, as is usual. -Note that -.I search-string -is just a string, not a pattern, so none of the usual -regular-expression/wildcard characters have any special meaning save -.CW ^ -and -.CW $ . -In the replacement string, -the -.CW & -character is replaced by the -.I search-string -unless it is preceded by a backslash. -You are allowed to use any character except -colon or exclamation point to separate the two strings. This so-called -delimiter character may be placed in either string by preceding it -with a backslash. -.IP T -.Ix 0 def :T -.Ix 0 def modifier tail -Replaces each word in the variable expansion by its last -component (its ``tail''). For example, given -.DS -OBJS = ../lib/a.o b /usr/lib/libm.a -TAILS = $(OBJS:T) -.DE -the variable -.CW TAILS -would expand to -.CW "a.o b libm.a" .'' `` -.IP H -.Ix 0 def :H -.Ix 0 def modifier head -This is similar to -.CW :T , -except that every word is replaced by everything but the tail (the -``head''). Using the same definition of -.CW OBJS , -the string -.CW "$(OBJS:H)" '' `` -would expand to -.CW "../lib /usr/lib" .'' `` -Note that the final slash on the heads is removed and -anything without a head is replaced by the empty string. -.IP E -.Ix 0 def :E -.Ix 0 def modifier extension -.Ix 0 def modifier suffix -.Ix 0 ref suffix "variable modifier" -.CW :E -replaces each word by its suffix (``extension''). So -.CW "$(OBJS:E)" '' `` -would give you -.CW ".o .a" .'' `` -.IP R -.Ix 0 def :R -.Ix 0 def modifier root -.Ix 0 def modifier base -This replaces each word by everything but the suffix (the ``root'' of -the word). -.CW "$(OBJS:R)" '' `` -expands to `` -.CW "../lib/a b /usr/lib/libm" .'' -.RE -.LP -In addition, the System V style of substitution is also supported. -This looks like: -.DS -$(\fIVARIABLE\fP:\fIsearch-string\fP=\fIreplacement\fP) -.DE -It must be the last modifier in the chain. The search is anchored at -the end of each word, so only suffixes or whole words may be replaced. -.xH 2 More on Debugging -.xH 2 More Exercises -.IP (3.1) -You've got a set programs, each of which is created from its own -assembly-language source file (suffix -.CW .asm ). -Each program can be assembled into two versions, one with error-checking -code assembled in and one without. You could assemble them into files -with different suffixes -.CW .eobj \& ( -and -.CW .obj , -for instance), but your linker only understands files that end in -.CW .obj . -To top it all off, the final executables -.I must -have the suffix -.CW .exe . -How can you still use transformation rules to make your life easier -(Hint: assume the error-checking versions have -.CW ec -tacked onto their prefix)? -.IP (3.2) -Assume, for a moment or two, you want to perform a sort of -``indirection'' by placing the name of a variable into another one, -then you want to get the value of the first by expanding the second -somehow. Unfortunately, PMake doesn't allow constructs like -.DS I -$($(FOO)) -.DE -What do you do? Hint: no further variable expansion is performed after -modifiers are applied, thus if you cause a $ to occur in the -expansion, that's what will be in the result. -.xH 1 PMake for Gods -.LP -This chapter 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 they must be handled with care, or you will end up with a mess. -.LP -Once more, I assume a greater familiarity with -.UX -or Sprite than I did in the previous two chapters. -.xH 2 Search Paths -.Rd 6 -.LP -PMake supports the dispersal of files into multiple directories by -allowing you to specify places to look for sources with -.CW .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. -.LP -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 -.CW .PATH -target (e.g. -.CW ".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 -.CW .PATH -with the suffix of the file. For instance -.DS -\&.PATH.h : /sprite/lib/include /sprite/att/lib/include -.DE -would tell PMake to look in the directories -.CW /sprite/lib/include -and -.CW /sprite/att/lib/include -for any files whose suffix is -.CW .h . -.LP -The current directory is always consulted first to see if a file -exists. Only if it cannot be found there are the directories in the -specific search path, followed by those in the general search path, -consulted. -.LP -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. -.LP -When a file is found in some directory other than the current one, all -local variables that would have contained the target's name -.CW .ALLSRC , ( -and -.CW .IMPSRC ) -will instead contain the path to the file, as found by PMake. -Thus if you have a file -.CW ../lib/mumble.c -and a makefile -.DS -\&.PATH.c : ../lib -mumble : mumble.c - $(CC) -o $(.TARGET) $(.ALLSRC) -.DE -the command executed to create -.CW mumble -would be -.CW "cc -o mumble ../lib/mumble.c" .'' `` -(As an aside, the command in this case isn't strictly necessary, since -it will be found using transformation rules if it isn't given. This is because -.CW .out -is the null suffix by default and a transformation exists from -.CW .c -to -.CW .out . -Just thought I'd throw that in.) -.LP -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. -.LP -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. While people have suggested that PMake should read -the directories each time, my experience suggests that the caching seldom -causes problems. In addition, not caching the directories slows things -down enormously because of PMake's attempts to apply transformation -rules through non-existent files \*- the number of extra file-system -searches is truly staggering, especially if many files without -suffixes are used and the null suffix isn't changed from -.CW .out . -.xH 2 Archives and Libraries -.LP -.UX -and Sprite allow you to merge files into an archive using the -.CW ar -command. Further, if the files are relocatable object files, you can -run -.CW ranlib -on the archive and get yourself a library that you can link into any -program you want. The main problem with archives is they double the -space you need to store the archived files, since there's one copy in -the archive and one copy out by itself. The problem with libraries is -you usually think of them as -.CW -lm -rather than -.CW /usr/lib/libm.a -and the linker thinks they're out-of-date if you so much as look at -them. -.LP -PMake solves the problem with archives by allowing you to tell it to -examine the files in the archives (so you can remove the individual -files without having to regenerate them later). To handle the problem -with libraries, PMake adds an additional way of deciding if a library -is out-of-date: -.IP \(bu 2 -If the table of contents is older than the library, or is missing, the -library is out-of-date. -.LP -A library is any target that looks like -.CW \-l name'' `` -or that ends in a suffix that was marked as a library using the -.CW .LIBS -target. -.CW .a -is so marked in the system makefile. -.LP -Members of an archive are specified as -``\fIarchive\fP(\fImember\fP[ \fImember\fP...])''. -Thus -.CW libdix.a(window.o) '' ``' -specifies the file -.CW window.o -in the archive -.CW libdix.a . -You may also use wildcards to specify the members of the archive. Just -remember that most the wildcard characters will only find -.I existing -files. -.LP -A file that is a member of an archive is treated specially. If the -file doesn't exist, but it is in the archive, the modification time -recorded in the archive is used for the file when determining if the -file is out-of-date. When figuring out how to make an archived member target -(not the file itself, but the file in the archive \*- the -\fIarchive\fP(\fImember\fP) target), special care is -taken with the transformation rules, as follows: -.IP \(bu 2 -\&\fIarchive\fP(\fImember\fP) is made to depend on \fImember\fP. -.IP \(bu 2 -The transformation from the \fImember\fP's suffix to the -\fIarchive\fP's suffix is applied to the \fIarchive\fP(\fImember\fP) target. -.IP \(bu 2 -The \fIarchive\fP(\fImember\fP)'s -.CW .TARGET -variable is set to the name of the \fImember\fP if \fImember\fP is -actually a target, or the path to the member file if \fImember\fP is -only a source. -.IP \(bu 2 -The -.CW .ARCHIVE -variable for the \fIarchive\fP(\fImember\fP) target is set to the name -of the \fIarchive\fP. -.Ix 0 def variable local .ARCHIVE -.Ix 0 def .ARCHIVE -.IP \(bu 2 -The -.CW .MEMBER -variable is set to the actual string inside the parentheses. In most -cases, this will be the same as the -.CW .TARGET -variable. -.Ix 0 def variable local .MEMBER -.Ix 0 def .MEMBER -.IP \(bu 2 -The \fIarchive\fP(\fImember\fP)'s place in the local variables of the -targets that depend on it is taken by the value of its -.CW .TARGET -variable. -.LP -Thus, a program library could be created with the following makefile: -.DS -\&.o.a : - ... - rm -f $(.TARGET:T) -OBJS = obj1.o obj2.o obj3.o -libprog.a : libprog.a($(OBJS)) - ar cru $(.TARGET) $(.OODATE) - ranlib $(.TARGET) -.DE -This will cause the three object files to be compiled (if the -corresponding source files were modified after the object file or, if -that doesn't exist, the archived object file), the out-of-date ones -archived in -.CW libprog.a , -a table of contents placed in the archive and the newly-archived -object files to be removed. -.LP -All this is used in the -.CW makelib.mk -system makefile to create a single library with ease. This makefile -looks like this: -.DS -.SM -# -# Rules for making libraries. The object files that make up the library -# are removed once they are archived. -# -# To make several libraries in parallel, you should define the variable -# "many_libraries". This will serialize the invocations of ranlib. -# -# To use, do something like this: -# -# OBJECTS = <files in the library> -# -# fish.a: fish.a($(OBJECTS)) MAKELIB -# -# - -#ifndef _MAKELIB_MK -_MAKELIB_MK = - -#include <po.mk> - -\&.po.a .o.a : - ... - rm -f $(.MEMBER) - -ARFLAGS ?= crl - -# -# Re-archive the out-of-date members and recreate the library's table of -# contents using ranlib. If many_libraries is defined, put the ranlib -# off til the end so many libraries can be made at once. -# -MAKELIB : .USE .PRECIOUS - ar $(ARFLAGS) $(.TARGET) $(.OODATE) -#ifndef no_ranlib -# ifdef many_libraries - ... -# endif /* many_libraries */ - ranlib $(.TARGET) -#endif /* no_ranlib */ - -#endif /* _MAKELIB_MK */ -.DE -.xH 2 On the Condition... -.Rd 1 -.LP -Like the C compiler before it, PMake allows you to configure the makefile, -based on the current environment, using conditional statements. A -conditional looks like this: -.DS -#if \fIboolean expression\fP -\fIlines\fP -#elif \fIanother boolean expression\fP -\fImore lines\fP -#else -\fIstill more lines\fP -#endif -.DE -They may be nested to a maximum depth of 30 and may occur anywhere -(except in a comment, of course). The -.CW # '' `` -must the very first character on the line. -.LP -Each -.I "boolean expression" -is made up of terms that look like function calls, the standard C -boolean operators -.CW && , -.CW || , -and -.CW ! , -and the standard relational operators -.CW == , -.CW != , -.CW > , -.CW >= , -.CW < , -and -.CW <= , -with -.CW == -and -.CW != -being overloaded to allow string comparisons as well. -.CW && -represents logical AND; -.CW || -is logical OR and -.CW ! -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 -your heart's content. Each term looks like a call on one of four -functions: -.nr pw 9 -.Ix 0 def make -.Ix 0 def conditional make -.Ix 0 def if make -.IP make \n(pw -The syntax is -.CW make( \fItarget\fP\c -.CW ) -where -.I 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 -.CW .MAIN -target (note that the sources for -.CW .MAIN -are only used if no targets were given on the command line). -.IP defined \n(pw -.Ix 0 def defined -.Ix 0 def conditional defined -.Ix 0 def if defined -The syntax is -.CW defined( \fIvariable\fP\c -.CW ) -and is true if -.I variable -is defined. Certain variables are defined in the system makefile that -identify the system on which PMake is being run. -.IP exists \n(pw -.Ix 0 def exists -.Ix 0 def conditional exists -.Ix 0 def if exists -The syntax is -.CW exists( \fIfile\fP\c -.CW ) -and is true if the file can be found on the global search path -(i.e. that defined by -.CW .PATH -targets, not by -.CW .PATH \fIsuffix\fP -targets). -.IP empty \n(pw -.Ix 0 def empty -.Ix 0 def conditional empty -.Ix 0 def if 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 and everything. 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 -.CW !defined( \fIvar\fP\c `` -.CW ") || empty(" \fIvar\fP\c -.CW ) '' -as the definition of -.CW || -will prevent the -.CW 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: -.DS -#if !empty(\fIvar\fP:M\fIword\fP) -.DE -.LP -The arithmetic and string operators may only be used to test the value -of a variable. The lefthand side must contain the variable expansion, -while the righthand 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. E.g. -.DS -#if $(OS) == 4.3 - -#if $(MACHINE) == "sun3" - -#if $(LOAD_ADDR) < 0xc000 -.DE -are all valid conditionals. In addition, the numeric value of a -variable can be tested as a boolean as follows: -.DS -#if $(LOAD) -.DE -would see if -.CW LOAD -contains a non-zero value and -.DS -#if !$(LOAD) -.DE -would test if -.CW LOAD -contains a zero value. -.LP -In addition to the bare -.CW #if ,'' `` -there are other forms that apply one of the first two functions to each -term. They are as follows: -.DS - ifdef \fRdefined\fP - ifndef \fR!defined\fP - ifmake \fRmake\fP - ifnmake \fR!make\fP -.DE -There are also the ``else if'' forms: -.CW elif , -.CW elifdef , -.CW elifndef , -.CW elifmake , -and -.CW elifnmake . -.LP -For instance, if you wish to create two versions of a program, one of which -is optimized (the production version) and the other of which is for debugging -(has symbols for dbx), you have two choices: you can create two -makefiles, one of which uses the -.CW \-g -flag for the compilation, while the other uses the -.CW \-O -flag, or you can use another target (call it -.CW debug ) -to create the debug version. The construct below will take care of -this for you. I have also made it so defining the variable -.CW DEBUG -(say with -.CW "pmake -D DEBUG" ) -will also cause the debug version to be made. -.DS -#if defined(DEBUG) || make(debug) -CFLAGS += -g -#else -CFLAGS += -O -#endif -.DE -There are, of course, problems with this approach. The most glaring -annoyance is that if you want to go from making a debug version to -making a production version, you have to remove all the object files, -or you will get some optimized and some debug versions in the same -program. Another annoyance is you have to be careful not to make two -targets that ``conflict'' because of some conditionals in the -makefile. For instance -.DS -#if make(print) -FORMATTER = ditroff -Plaser_printer -#endif -#if make(draft) -FORMATTER = nroff -Pdot_matrix_printer -#endif -.DE -would wreak havoc if you tried -.CW "pmake draft print" '' `` -since you would use the same formatter for each target. As I said, -this all gets somewhat complicated. -.xH 2 A Shell is a Shell is a Shell -.Rd 7 -.LP -In normal operation, the Bourne Shell (better known as -.CW sh '') `` -is used to execute the commands to re-create targets. PMake also allows you -to specify a different shell for it to use when executing these -commands. There are several things PMake must know about the shell you -wish to use. These things are specified as the sources for the -.CW .SHELL -.Ix 0 ref .SHELL -.Ix 0 ref target .SHELL -target by keyword, as follows: -.IP "\fBpath=\fP\fIpath\fP" -PMake needs to know where the shell actually resides, so it can -execute it. If you specify this and nothing else, PMake will use the -last component of the path and look in its table of the shells it -knows and use the specification it finds, if any. Use this if you just -want to use a different version of the Bourne or C Shell (yes, PMake knows -how to use the C Shell too). -.IP "\fBname=\fP\fIname\fP" -This is the name by which the shell is to be known. It is a single -word and, if no other keywords are specified (other than -.B path ), -it is the name by which PMake attempts to find a specification for -it (as mentioned above). You can use this if you would just rather use -the C Shell than the Bourne Shell -.CW ".SHELL: name=csh" '' (`` -will do it). -.IP "\fBquiet=\fP\fIecho-off command\fP" -As mentioned before, PMake actually controls whether commands are -printed by introducing commands into the shell's input stream. This -keyword, and the next two, control what those commands are. The -.B quiet -keyword is the command used to turn echoing off. Once it is turned -off, echoing is expected to remain off until the echo-on command is given. -.IP "\fBecho=\fP\fIecho-on command\fP" -The command PMake should give to turn echoing back on again. -.IP "\fBfilter=\fP\fIprinted echo-off command\fP" -Many shells will echo the echo-off command when it is given. This -keyword tells PMake in what format the shell actually prints the -echo-off command. Wherever PMake sees this string in the shell's -output, it will delete it and any following whitespace, up to and -including the next newline. See the example at the end of this section -for more details. -.IP "\fBechoFlag=\fP\fIflag to turn echoing on\fP" -Unless a target has been marked -.CW .SILENT , -PMake wants to start the shell running with echoing on. To do this, it -passes this flag to the shell as one of its arguments. If either this -or the next flag begins with a `\-', the flags will be passed to the -shell as separate arguments. Otherwise, the two will be concatenated -(if they are used at the same time, of course). -.IP "\fBerrFlag=\fP\fIflag to turn error checking on\fP" -Likewise, unless a target is marked -.CW .IGNORE , -PMake wishes error-checking to be on from the very start. To this end, -it will pass this flag to the shell as an argument. The same rules for -an initial `\-' apply as for the -.B echoFlag . -.IP "\fBcheck=\fP\fIcommand to turn error checking on\fP" -Just as for echo-control, error-control is achieved by inserting -commands into the shell's input stream. This is the command to make -the shell check for errors. It also serves another purpose if the -shell doesn't have error-control as commands, but I'll get into that -in a minute. Again, once error checking has been turned on, it is -expected to remain on until it is turned off again. -.IP "\fBignore=\fP\fIcommand to turn error checking off\fP" -This is the command PMake uses to turn error checking off. It has -another use if the shell doesn't do error-control, but I'll tell you -about that.\|.\|.\|now. -.IP "\fBhasErrCtl=\fP\fIyes or no\fP" -This takes a value that is either -.B yes -or -.B no . -Now you might think that the existence of the -.B check -and -.B ignore -keywords would be enough to tell PMake if the shell can do -error-control, but you'd be wrong. If -.B hasErrCtl -is -.B yes , -PMake uses the check and ignore commands in a straight-forward manner. -If this is -.B no , -however, their use is rather different. In this case, the check -command is used as a template, in which the string -.B %s -is replaced by the command that's about to be executed, to produce a -command for the shell that will echo the command to be executed. The -ignore command is also used as a template, again with -.B %s -replaced by the command to be executed, to produce a command that will -execute the command to be executed and ignore any error it returns. -When these strings are used as templates, you must provide newline(s) -.CW \en '') (`` -in the appropriate place(s). -.LP -The strings that follow these keywords may be enclosed in single or -double quotes (the quotes will be stripped off) and may contain the -usual C backslash-characters (\en is newline, \er is return, \eb is -backspace, \e' escapes a single-quote inside single-quotes, \e" -escapes a double-quote inside double-quotes). Now for an example. -.LP -This is actually the contents of the -.CW <shx.mk> -system makefile, and causes PMake to use the Bourne Shell in such a -way that each command is printed as it is executed. That is, if more -than one command is given on a line, each will be printed separately. -Similarly, each time the body of a loop is executed, the commands -within that loop will be printed, etc. The specification runs like -this: -.DS -# -# This is a shell specification to have the Bourne shell echo -# the commands just before executing them, rather than when it reads -# them. Useful if you want to see how variables are being expanded, etc. -# -\&.SHELL : path=/bin/sh \e - quiet="set -" \e - echo="set -x" \e - filter="+ set - " \e - echoFlag=x \e - errFlag=e \e - hasErrCtl=yes \e - check="set -e" \e - ignore="set +e" -.DE -.LP -It tells PMake the following: -.Bp -The shell is located in the file -.CW /bin/sh . -It need not tell PMake that the name of the shell is -.CW sh -as PMake can figure that out for itself (it's the last component of -the path). -.Bp -The command to stop echoing is -.CW "set -" . -.Bp -The command to start echoing is -.CW "set -x" . -.Bp -When the echo off command is executed, the shell will print -.CW "+ set - " -(The `+' comes from using the -.CW \-x -flag (rather than the -.CW \-v -flag PMake usually uses)). PMake will remove all occurrences of this -string from the output, so you don't notice extra commands you didn't -put there. -.Bp -The flag the Bourne Shell will take to start echoing in this way is -the -.CW \-x -flag. The Bourne Shell will only take its flag arguments concatenated -as its first argument, so neither this nor the -.B errFlag -specification begins with a \-. -.Bp -The flag to use to turn error-checking on from the start is -.CW \-e . -.Bp -The shell can turn error-checking on and off, and the commands to do -so are -.CW "set +e" -and -.CW "set -e" , -respectively. -.LP -I should note that this specification is for Bourne Shells that are -not part of Berkeley -.UX , -as shells from Berkeley don't do error control. You can get a similar -effect, however, by changing the last three lines to be: -.DS - hasErrCtl=no \e - check="echo \e"+ %s\e"\en" \e - ignore="sh -c '%s || exit 0\en" -.DE -.LP -This will cause PMake to execute the two commands -.DS -echo "+ \fIcmd\fP" -sh -c '\fIcmd\fP || true' -.DE -for each command for which errors are to be ignored. (In case you are -wondering, the thing for -.CW ignore -tells the shell to execute another shell without error checking on and -always exit 0, since the -.B || -causes the -.CW "exit 0" -to be executed only if the first command exited non-zero, and if the -first command exited zero, the shell will also exit zero, since that's -the last command it executed). -.xH 2 Compatibility -.Ix 0 ref compatibility -.LP -There are three (well, 3 \(12) levels of backwards-compatibility built -into PMake. Most makefiles will need none at all. Some may need a -little bit of work to operate correctly when run in parallel. Each -level encompasses the previous levels (e.g. -.B \-B -(one shell per command) implies -.B \-V ) -The three levels are described in the following three sections. -.xH 3 DEFCON 3 \*- Variable Expansion -.Ix 0 ref compatibility -.LP -As noted before, PMake will not expand a variable unless it knows of a -value for it. This can cause problems for makefiles that expect to -leave variables undefined except in special circumstances (e.g. if -more flags need to be passed to the C compiler or the output from a -text processor should be sent to a different printer). If the -variables are enclosed in curly braces -.CW ${PRINTER} ''), (`` -the shell will let them pass. If they are enclosed in parentheses, -however, the shell will declare a syntax error and the make will come -to a grinding halt. -.LP -You have two choices: change the makefile to define the variables -(their values can be overridden on the command line, since that's -where they would have been set if you used Make, anyway) or always give the -.B \-V -flag (this can be done with the -.CW .MAKEFLAGS -target, if you want). -.xH 3 DEFCON 2 \*- The Number of the Beast -.Ix 0 ref compatibility -.LP -Then there are the makefiles that expect certain commands, such as -changing to a different directory, to not affect other commands in a -target's creation script. You can solve this is either by going -back to executing one shell per command (which is what the -.B \-B -flag forces PMake to do), which slows the process down a good bit and -requires you to use semicolons and escaped newlines for shell constructs, or -by changing the makefile to execute the offending command(s) in a subshell -(by placing the line inside parentheses), like so: -.DS -install :: .MAKE - (cd src; $(.PMAKE) install) - (cd lib; $(.PMAKE) install) - (cd man; $(.PMAKE) install) -.DE -.Ix 0 ref operator double-colon -.Ix 0 ref variable global .PMAKE -.Ix 0 ref .PMAKE -.Ix 0 ref .MAKE -.Ix 0 ref attribute .MAKE -This will always execute the three makes (even if the -.B \-n -flag was given) because of the combination of the ``::'' operator and -the -.CW .MAKE -attribute. Each command will change to the proper directory to perform -the install, leaving the main shell in the directory in which it started. -.xH 3 "DEFCON 1 \*- Imitation is the Not the Highest Form of Flattery" -.Ix 0 ref compatibility -.LP -The final category of makefile is the one where every command requires -input, the dependencies are incompletely specified, or you simply -cannot create more than one target at a time, as mentioned earlier. In -addition, you may not have the time or desire to upgrade the makefile -to run smoothly with PMake. If you are the conservative sort, this is -the compatibility mode for you. It is entered either by giving PMake -the -.B \-M -flag (for Make), or by executing PMake as -.CW make .'' `` -In either case, PMake performs things exactly like Make (while still -supporting most of the nice new features PMake provides). This -includes: -.IP \(bu 2 -No parallel execution. -.IP \(bu 2 -Targets are made in the exact order specified by the makefile. The -sources for each target are made in strict left-to-right order, etc. -.IP \(bu 2 -A single Bourne shell is used to execute each command, thus the -shell's -.CW $$ -variable is useless, changing directories doesn't work across command -lines, etc. -.IP \(bu 2 -If no special characters exist in a command line, PMake will break the -command into words itself and execute the command directly, without -executing a shell first. The characters that cause PMake to execute a -shell are: -.CW # , -.CW = , -.CW | , -.CW ^ , -.CW ( , -.CW ) , -.CW { , -.CW } , -.CW ; , -.CW & , -.CW < , -.CW > , -.CW * , -.CW ? , -.CW [ , -.CW ] , -.CW : , -.CW $ , -.CW ` , -and -.CW \e . -You should notice that these are all the characters that are given -special meaning by the shell (except -.CW ' -and -.CW " , -which PMake deals with all by its lonesome). -.IP \(bu 2 -The use of the null suffix is turned off. -.Ix 0 ref "null suffix" -.Ix 0 ref suffix null -.xH 2 The Way Things Work -.LP -When PMake reads the makefile, it parses sources and targets into -nodes in a graph. The graph is directed only in the sense that PMake -knows which way is up. Each node contains not only links to all its -parents and children (the nodes that depend on it and those on which -it depends, respectively), but also a count of the number of its -children that have already been processed. -.LP -The most important thing to know about how PMake uses this graph is -that the traversal is breadth-first and occurs in two passes. -.LP -After PMake has parsed the makefile, it begins with the nodes the user -has told it to make (either on the command line, or via a -.CW .MAIN -target, or by the target being the first in the file not labeled with -the -.CW .NOTMAIN -attribute) placed in a queue. It continues to take the node off the -front of the queue, mark it as something that needs to be made, pass -the node to -.CW Suff_FindDeps -(mentioned earlier) to find any implicit sources for the node, and -place all the node's children that have yet to be marked at the end of -the queue. If any of the children is a -.CW .USE -rule, its attributes are applied to the parent, then its commands are -appended to the parent's list of commands and its children are linked -to its parent. The parent's unmade children counter is then decremented -(since the -.CW .USE -node has been processed). You will note that this allows a -.CW .USE -node to have children that are -.CW .USE -nodes and the rules will be applied in sequence. -If the node has no children, it is placed at the end of -another queue to be examined in the second pass. This process -continues until the first queue is empty. -.LP -At this point, all the leaves of the graph are in the examination -queue. PMake removes the node at the head of the queue and sees if it -is out-of-date. If it is, it is passed to a function that will execute -the commands for the node asynchronously. When the commands have -completed, all the node's parents have their unmade children counter -decremented and, if the counter is then 0, they are placed on the -examination queue. Likewise, if the node is up-to-date. Only those -parents that were marked on the downward pass are processed in this -way. Thus PMake traverses the graph back up to the nodes the user -instructed it to create. When the examination queue is empty and no -shells are running to create a target, PMake is finished. -.LP -Once all targets have been processed, PMake executes the commands -attached to the -.CW .END -target, either explicitly or through the use of an ellipsis in a shell -script. If there were no errors during the entire process but there -are still some targets unmade (PMake keeps a running count of how many -targets are left to be made), there is a cycle in the graph. PMake does -a depth-first traversal of the graph to find all the targets that -weren't made and prints them out one by one. -.xH 1 Answers to Exercises -.IP (3.1) -This is something of a trick question, for which I apologize. The -trick comes from the UNIX definition of a suffix, which PMake doesn't -necessarily share. You will have noticed that all the suffixes used in -this tutorial (and in UNIX in general) begin with a period -.CW .ms , ( -.CW .c , -etc.). Now, PMake's idea of a suffix is more like English's: it's the -characters at the end of a word. With this in mind, one possible -.Ix 0 def suffix -solution to this problem goes as follows: -.DS I -\&.SUFFIXES : ec.exe .exe ec.obj .obj .asm -ec.objec.exe .obj.exe : - link -o $(.TARGET) $(.IMPSRC) -\&.asmec.obj : - asm -o $(.TARGET) -DDO_ERROR_CHECKING $(.IMPSRC) -\&.asm.obj : - asm -o $(.TARGET) $(.IMPSRC) -.DE -.IP (3.2) -The trick to this one lies in the ``:='' variable-assignment operator -and the ``:S'' variable-expansion modifier. -.Ix 0 ref variable assignment expanded -.Ix 0 ref variable expansion modified -.Ix 0 ref modifier substitute -.Ix 0 ref :S -.Ix 0 ref := -Basically what you want is to take the pointer variable, so to speak, -and transform it into an invocation of the variable at which it -points. You might try something like -.DS I -$(PTR:S/^/\e$(/:S/$/)) -.DE -which places -.CW $( '' `` -at the front of the variable name and -.CW ) '' `` -at the end, thus transforming -.CW VAR ,'' `` -for example, into -.CW $(VAR) ,'' `` -which is just what we want. Unfortunately (as you know if you've tried -it), since, as it says in the hint, PMake does no further substitution -on the result of a modified expansion, that's \fIall\fP you get. The -solution is to make use of ``:='' to place that string into yet -another variable, then invoke the other variable directly: -.DS I -*PTR := $(PTR:S/^/\e$(/:S/$/)/) -.DE -You can then use -.CW $(*PTR) '' `` -to your heart's content. -.de Gp -.XP -\&\fB\\$1:\fP -.. -.xH 1 Glossary of Jargon -.Gp "attribute" -A property given to a target that causes PMake to treat it differently. -.Gp "command script" -The lines immediately following a dependency line that specify -commands to execute to create each of the targets on the dependency -line. Each line in the command script must begin with a tab. -.Gp "command-line variable" -A variable defined in an argument when PMake is first executed. -Overrides all assignments to the same variable name in the makefile. -.Gp "conditional" -A construct much like that used in C that allows a makefile to be -configured on the fly based on the local environment, or on what is being -made by that invocation of PMake. -.Gp "creation script" -Commands used to create a target. See ``command script.'' -.Gp "dependency" -The relationship between a source and a target. This comes in three -flavors, as indicated by the operator between the target and the -source. `:' gives a straight time-wise dependency (if the target is -older than the source, the target is out-of-date), while `!' provides -simply an ordering and always considers the target out-of-date. `::' -is much like `:', save it creates multiple instances of a target each -of which depends on its own list of sources. -.Gp "dynamic source" -This refers to a source that has a local variable invocation in it. It -allows a single dependency line to specify a different source for each -target on the line. -.Gp "global variable" -Any variable defined in a makefile. Takes precedence over variables -defined in the environment, but not over command-line or local variables. -.Gp "input graph" -What PMake constructs from a makefile. Consists of nodes made of the -targets in the makefile, and the links between them (the -dependencies). The links are directed (from source to target) and -there may not be any cycles (loops) in the graph. -.Gp "local variable" -A variable defined by PMake visible only in a target's shell script. -There are seven local variables, not all of which are defined for -every target: -.CW .TARGET , -.CW .ALLSRC , -.CW .OODATE , -.CW .PREFIX , -.CW .IMPSRC , -.CW .ARCHIVE , -and -.CW .MEMBER . -.CW .TARGET , -.CW .PREFIX , -.CW .ARCHIVE , -and -.CW .MEMBER -may be used on dependency lines to create ``dynamic sources.'' -.Gp "makefile" -A file that describes how a system is built. If you don't know what it -is after reading this tutorial.\|.\|.\|. -.Gp "modifier" -A letter, following a colon, used to alter how a variable is expanded. -It has no effect on the variable itself. -.Gp "operator" -What separates a source from a target (on a dependency line) and specifies -the relationship between the two. There are three: -.CW : ', ` -.CW :: ', ` -and -.CW ! '. ` -.Gp "search path" -A list of directories in which a file should be sought. PMake's view -of the contents of directories in a search path does not change once -the makefile has been read. A file is sought on a search path only if -it is exclusively a source. -.Gp "shell" -A program to which commands are passed in order to create targets. -.Gp "source" -Anything to the right of an operator on a dependency line. Targets on -the dependency line are usually created from the sources. -.Gp "special target" -A target that causes PMake to do special things when it's encountered. -.Gp "suffix" -The tail end of a file name. Usually begins with a period, -.CW .c -or -.CW .ms , -e.g. -.Gp "target" -A word to the left of the operator on a dependency line. More -generally, any file that PMake might create. A file may be (and often -is) both a target and a source (what it is depends on how PMake is -looking at it at the time \*- sort of like the wave/particle duality -of light, you know). -.Gp "transformation rule" -A special construct in a makefile that specifies how to create a file -of one type from a file of another, as indicated by their suffixes. -.Gp "variable expansion" -The process of substituting the value of a variable for a reference to -it. Expansion may be altered by means of modifiers. -.Gp "variable" -A place in which to store text that may be retrieved later. Also used -to define the local environment. Conditionals exist that test whether -a variable is defined or not. -.bp -.\" Output table of contents last, with an entry for the index, making -.\" sure to save and restore the last real page number for the index... -.nr @n \n(PN+1 -.\" We are not generating an index -.\" .XS \n(@n -.\" Index -.\" .XE -.nr %% \n% -.PX -.nr % \n(%% |