3773 lines
		
	
	
		
			123 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			3773 lines
		
	
	
		
			123 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| .\"	$NetBSD: tutorial.ms,v 1.10 2004/06/27 19:12:33 uwe 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's -1u'\D't 5u'
 | ||
| .sp -1
 | ||
| \h'50u'\D'l 71u 0u'\D'l 50u 50u'\D'l 0u 71u'\D'l -50u 50u'\D'l -71u 0u'\D'l -50u -50u'\D'l 0u -71u'\D'l 50u -50u'
 | ||
| .sp -1
 | ||
| \D't 3u'
 | ||
| .sp -1
 | ||
| .sp 7u
 | ||
| \h'53u'\D'p 14 68u 0u 46u 46u 0u 68u -46u 46u -68u 0u -47u -46u 0u -68u 47u -46u'
 | ||
| .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'\D's -1u'
 | ||
| .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(%%
 | 
