doc/en_US.ISO8859-1/books/pmake/basics/chapter.xml
2014-04-29 21:58:22 +00:00

1528 lines
60 KiB
XML

<?xml version="1.0" encoding="iso-8859-1"?>
<!--
$FreeBSD$
-->
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="basics">
<title>The Basics of PMake</title>
<para><application>PMake</application> takes as input a file that
tells which files depend on which other files to be complete and
what to do about files that are <quote>out-of-date</quote>.
This file is known as a <quote>makefile</quote> and is usually
kept in the top-most directory of the system to be built.
While you can call the makefile anything you want,
<application>PMake</application> will look for
<filename>Makefile</filename> and <filename>makefile</filename>
(in that order) in the current directory if you do not tell it
otherwise. To specify a different makefile, use the
<option>-f</option> flag, e.g.</para>
<screen>&prompt.user; <userinput>pmake -f <replaceable>program.mk</replaceable></userinput></screen>
<para>A makefile has four different types of lines in it:</para>
<itemizedlist>
<listitem>
<para>File dependency specifications</para>
</listitem>
<listitem>
<para>Creation commands</para>
</listitem>
<listitem>
<para>Variable assignments</para>
</listitem>
<listitem>
<para>Comments, include statements and conditional directives</para>
</listitem>
</itemizedlist>
<para>Any line may be continued over multiple lines by ending it
with a backslash. 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
<application>PMake</application>.</para>
<section xml:id="deplines">
<title>Dependency Lines</title>
<para>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. These are dependencies and are
specified by means of dependency lines in the makefile.</para>
<para>On a dependency line, there are targets and sources,
separated by a one-&nbsp;or two-character operator. The targets
<quote>depend</quote> on the sources and are usually created
from them. 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.</para>
<para>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 <quote>operator</quote> that separates them. 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:</para>
<informaltable frame="none">
<tgroup cols="2">
<tbody>
<row valign="top">
<entry><literal>:</literal></entry>
<entry>If a colon is used, a target on the line is
considered to be <quote>out-of-date</quote> (and in need
of creation) if any of the sources has been modified
more recently than the target, or the target does not
exist. 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.</entry>
</row>
<row valign="top">
<entry><literal>!</literal></entry>
<entry>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.</entry>
</row>
<row valign="top">
<entry><literal>::</literal></entry>
<entry>If a double-colon is used, a target is
<quote>out-of-date</quote> if any of the sources has
been modified more recently than the target, or the
target does not exist, or the target has no sources.
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 will go into that
in the next section
(see <xref linkend="shellcmds"/>).</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Enough words, now for an example. Take that C program I
mentioned earlier. Say there are three C files
(<filename>a.c</filename>, <filename>b.c</filename> and
<filename>c.c</filename>) each of which includes the file
<filename>defs.h</filename>. The dependencies between the files
could then be expressed as follows:</para>
<programlisting>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</programlisting>
<para>You may be wondering at this point, where
<filename>a.o</filename>, <filename>b.o</filename> and
<filename>c.o</filename> came in and why they depend on
<filename>defs.h</filename> and the C files do not.
The reason is quite simple: <buildtarget>program</buildtarget>
cannot be made by linking together <filename>.c</filename>
files&mdash;it must be made from <filename>.o</filename> files.
Likewise, if you change <filename>defs.h</filename>, it is not
the <filename>.c</filename> files that need to be re-created,
it is the <filename>.o</filename> files. If you think of
dependencies in these terms&mdash;which files (targets) need to
be created from which files (sources)&mdash;you should have no
problems.</para>
<para>An important thing to notice about the above example, is
that all the <filename>.o</filename> 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. For example, <filename>a.o</filename> depends
on both <filename>defs.h</filename> and <filename>a.c</filename>.</para>
<para>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 do not say
otherwise. That is why program comes first in the example
makefile, above.</para>
<para>Both targets and sources may contain the standard C-Shell wildcard
characters (<literal>{</literal>, <literal>}</literal>,
<literal>*</literal>, <literal>?</literal>, <literal>[</literal>, and
<literal>]</literal>), 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:</para>
<informaltable frame="none">
<tgroup cols="2">
<tbody>
<row valign="top">
<entry><literal>{}</literal></entry>
<entry>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,
<filename>src/{whiffle,beep,fish}.c</filename> expands
to the three words <filename>src/whiffle.c</filename>,
<filename>src/beep.c</filename>, and
<filename>src/fish.c</filename>. 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 <application>PMake</application> is
started.</entry> </row>
<row valign="top">
<entry><literal>*</literal></entry>
<entry>This matches zero or more characters of any sort.
<filename>src/*.c</filename> will expand to the same
three words as above as long as src contains those three
files (and no other files that end in
<filename>.c</filename>).&gt;</entry>
</row>
<row valign="top">
<entry><literal>?</literal></entry>
<entry>Matches any single character.</entry>
</row>
<row valign="top">
<entry><literal>[]</literal></entry>
<entry>This is known as a character class and contains
either a list of single characters, or a series of
character ranges (<literal>a-z</literal>, for example
means all characters between <literal>a</literal> and
<literal>z</literal>), or both. It matches any single
character contained in the list. For example,
<literal>[A-Za-z]</literal> will match all letters,
while <literal>[0123456789]</literal> will match all
numbers.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section xml:id="shellcmds" xreflabel="Shell Commands">
<title>Shell Commands</title>
<para><quote>Is not that nice,</quote> you say to yourself,
<quote>but how are files actually ``re-created'', as he likes to
spell it?</quote>
The re-creation is accomplished by commands you place in the
makefile. These commands are passed to the Bourne shell (better
known as <filename>/bin/sh</filename>) to be executed and are
expected to do what is necessary to update the target file
(<application>PMake</application> does not actually check to see
if the target was created. It just assumes it is there).</para>
<para>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 must be preceded by at least one
tab.</para>
<para>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 <literal>::</literal>
operator was used on the dependency line.</para>
<para>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.</para>
<para>To expand on the earlier makefile, you might add commands
as follows:</para>
<programlisting>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</programlisting>
<para>Something you should remember when writing a makefile is,
the commands will be executed if the target on the dependency
line is out-of-date, not the sources. In this example, the
command <command>cc -c a.c</command> will be executed if
<filename>a.o</filename> is out-of-date. Because of the
<literal>:</literal> operator, this means that should
<filename>a.c</filename> or <filename>defs.h</filename> have
been modified more recently than <filename>a.o</filename>, the
command will be executed (<filename>a.o</filename> will be
considered out-of-date).</para>
<para>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 <filename>@</filename> and <literal>-</literal>, they cause
<application>PMake</application> to do different things.</para>
<para>In most cases, shell commands are printed before they are
actually executed. This is to keep you informed of what is
going on. If an <literal>@</literal> appears, however, this
echoing is suppressed. In the case of an echo command,
say</para>
<programlisting>echo Linking index</programlisting>
<para>it would be rather silly to see</para>
<screen>echo Linking index
Linking index</screen>
<para>so <application>PMake</application> allows you to place an
<literal>@</literal> before the command to prevent the command
from being printed:</para>
<programlisting>@echo Linking index</programlisting>
<para>The other special character is the <literal>-</literal>.
In case you did not know, shell commands finish with a certain
<quote>exit status</quote>. This status is made available by
the operating system to whatever program invoked the command.
Normally this status will be <literal>0</literal> if everything
went ok and non-zero if something went wrong. For this reason,
<application>PMake</application> will consider an error to have
occurred if one of the shells it invokes returns a non-zero
status. When it detects an error,
<application>PMake</application>'s usual action is to abort
whatever it is 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.
<application>PMake</application> will exit after the last job
finishes). This behavior can be altered, however, by placing a
<literal>-</literal> at the front of a command
(e.g. <command>-mv index index.old</command>), certain
command-line arguments, or doing other things, to be detailed
later. In such a case, the non-zero status is simply ignored
and <application>PMake</application> keeps chugging
along.</para>
<para>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 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,
<application>PMake</application> has a <option>-B</option> 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.</para>
<para>A target's shell script is fed to the shell on its (the
shell's) input stream. This means that any commands, such as
<application>ci</application> that need to get input from the
terminal will not work right &ndash; they will get the shell's
input, something they probably will not find to their liking.
A simple way around this is to give a command like this:</para>
<screen><command>ci $(SRCS) &lt; /dev/tty</command></screen>
<para>This would force the program's input to come from the
terminal. If you cannot do this for some reason, your only
other alternative is to use <application>PMake</application> in
its fullest compatibility mode.
See <quote>Compatibility</quote> in <xref linkend="gods"/>.</para>
</section>
<section xml:id="variables">
<title>Variables</title>
<para><application>PMake</application>, like
<application>Make</application> before it, has the ability to
save text in variables to be recalled later at your convenience.
Variables in <application>PMake</application> are used much like
variables in the shell and, by tradition, consist of all
upper-case letters (you do not have to use all upper-case
letters. In fact there is nothing to stop you from calling a
variable <literal>@^&amp;$%$</literal>. Just tradition). Variables
are assigned-to using lines of the form:</para>
<programlisting>VARIABLE = value</programlisting>
<para>appended-to by:</para>
<programlisting>VARIABLE += value</programlisting>
<para>conditionally assigned-to (if the variable is not already
defined) by:</para>
<programlisting>VARIABLE ?= value</programlisting>
<para>and assigned-to with expansion (i.e. the value is expanded
(see below) before being assigned to the variable&mdash;useful
for placing a value at the beginning of a variable, or other
things) by:</para>
<programlisting>VARIABLE := value</programlisting>
<para>Any whitespace before value is stripped off. When
appending, a space is placed between the old value and the stuff
being appended.</para>
<para>The final way a variable may be assigned to is using:</para>
<programlisting>VARIABLE != shell-command</programlisting>
<para>In this case, 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:</para>
<programlisting>CWD != pwd</programlisting>
<note>
<para>This is intended to be used to execute commands that
produce small amounts of output
(e.g.&nbsp;<application>pwd</application>).
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).
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.</para>
</note>
<para>For example, to set the variable <envar>CFLAGS</envar> to
the string <literal>-I/sprite/src/lib/libc -O,</literal> you
would place a line:</para>
<programlisting>CFLAGS = -I/sprite/src/lib/libc -O</programlisting>
<para>in the makefile and use the word
<literal>$(CFLAGS)</literal> wherever you would like the string
<literal>-I/sprite/src/lib/libc -O</literal> to appear. This is
called variable expansion.</para>
<note>
<para>Unlike <application>Make</application>,
<application>PMake</application> will not expand a variable
unless it knows the variable exists. E.g.&nbsp;if you have a
<literal>${i}</literal> in a shell command and you have not
assigned a value to the variable <varname>i</varname> (the
empty string is considered a value, by the way), where
<application>Make</application> would have substituted the
empty string, <application>PMake</application> will leave the
<literal>${i}</literal> alone.
To keep <application>PMake</application> from substituting for
a variable it knows, precede the dollar sign with another
dollar sign (e.g.&nbsp;to pass <literal>${HOME}</literal> to
the shell, use <literal>$${HOME}</literal>). This causes
<application>PMake</application>, in effect, to expand the
<literal>$</literal> macro, which expands to a single
<literal>$</literal>.</para>
</note>
<para>For compatibility, <application>Make</application>'s style
of variable expansion will be used if you invoke
<application>PMake</application> with any of the compatibility
flags (<option>-V</option>, <option>-B</option> or
<option>-M</option>. The <option>-V</option> flag alters just
the 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,
<application>PMake</application> 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. For
example, 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).</para>
<para>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):</para>
<itemizedlist>
<listitem>
<para>Local variables.</para>
</listitem>
<listitem>
<para>Command-line variables.</para>
</listitem>
<listitem>
<para>Global variables.</para>
</listitem>
<listitem>
<para>Environment variables.</para>
</listitem>
</itemizedlist>
<para>The classification of variables does not 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.</para>
<section xml:id="localvariables">
<title>Local Variables</title>
<para>Each target can have as many as seven local variables.
These are variables that are only <quote>visible</quote>
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:</para>
<variablelist>
<varlistentry>
<term><varname>.TARGET</varname></term>
<listitem>
<para>The name of the target.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>.OODATE</varname></term>
<listitem>
<para>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.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>.ALLSRC</varname></term>
<listitem>
<para>The list of all sources for this target in the order
in which they were given.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>.PREFIX</varname></term>
<listitem>
<para>The target without its suffix and without any
leading path. E.g.&nbsp;for the target
<filename>../../lib/compat/fsRead.c</filename>, this
variable would contain <literal>fsRead</literal>.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Three other local variables are set only for certain
targets under special circumstances. These are the
<varname>.IMPSRC,</varname> <varname>.ARCHIVE,</varname> and
<varname>.MEMBER</varname> variables. When they are set and
how they are used is described later.</para>
<para>Four of these variables may be used in sources as well as
in shell scripts. These are <varname>.TARGET</varname>,
<varname>.PREFIX</varname>, <varname>.ARCHIVE</varname> and
<varname>.MEMBER</varname>. The variables in the sources are
expanded once for each target on the dependency line,
providing what is known as a <quote>dynamic source,</quote>
allowing you to specify several dependency lines at once.
For example:</para>
<programlisting>$(OBJS) : $(.PREFIX).c</programlisting>
<para>will create a dependency between each object file and its
corresponding C source file.</para>
</section>
<section xml:id="cmdvars">
<title>Command-line Variables</title>
<para>Command-line variables are set when
<application>PMake</application> is first invoked by giving a
variable assignment as one of the arguments.
For example:</para>
<screen>pmake "CFLAGS = -I/sprite/src/lib/libc -O"</screen>
<para>would make <envar>CFLAGS</envar> be a command-line
variable with the given value. Any assignments to
<envar>CFLAGS</envar> 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 <literal>=</literal> and
<literal>?=</literal> 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.
<literal>+=</literal> is the same as <literal>=</literal>,
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 will not get into here). <literal>:=</literal>
and <literal>?=</literal> will work if the only variables
used are in the environment. <literal>!=</literal> 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).</para>
</section>
<section xml:id="globalvariables">
<title>Global Variables</title>
<para>Global variables are those set or appended-to in the
makefile. There are two classes of global variables: those
you set and those <application>PMake</application> 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 <application>PMake</application> sets (almost)
always begin with a period and always contain upper-case
letters, only. The variables are as follows:</para>
<variablelist>
<varlistentry>
<term><varname>.PMAKE</varname></term>
<listitem>
<para>The name by which <application>PMake</application>
was invoked is stored in this variable. For
compatibility, the name is also stored in the
<varname>MAKE</varname> variable.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>.MAKEFLAGS</varname></term>
<listitem>
<para>All the relevant flags with which
<application>PMake</application> was invoked.
This does not include such things as <option>-f</option>
or variable assignments. Again for compatibility, this
value is stored in the <varname>MFLAGS</varname>
variable as well.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Two other variables, <varname>.INCLUDES</varname> and
<varname>.LIBS,</varname> are covered in the section on
special targets in <xref linkend="shortcuts"/>.</para>
<para>Global variables may be deleted using lines of the
form:</para>
<programlisting>#undef variable</programlisting>
<para>The <literal>#</literal> must be the first character on
the line. Note that this may only be done on global
variables.</para>
</section>
<section xml:id="envvars">
<title>Environment Variables</title>
<para>Environment variables are passed by the shell that invoked
<application>PMake</application> and are given by
<application>PMake</application> to each shell it invokes.
They are expanded like any other variable, but they cannot be
altered in any way.</para>
<para>One special environment variable, <envar>PMAKE</envar>, is
examined by <application>PMake</application> for command-line
flags, variable assignments, etc., it should always use. This
variable is examined before the actual arguments to
<application>PMake</application> are. In addition, all flags
given to <application>PMake</application>, either through the
<envar>PMAKE</envar> variable or on the command line, are
placed in this environment variable and exported to each shell
<application>PMake</application> executes. Thus recursive
invocations of <application>PMake</application> automatically
receive the same flags as the top-most one.</para>
<para>Using all these variables, you can compress the sample
makefile even more:</para>
<programlisting>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</programlisting>
</section>
</section>
<section xml:id="comments">
<title>Comments</title>
<para>Comments in a makefile start with a <literal>#</literal>
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 <literal>#</literal> in a variable or on a
dependency line, put a backslash in front of it.
<application>PMake</application> will compress the two into a
single <literal>#</literal>.</para>
<note>
<para>This is not true if <application>PMake</application> is
operating in full-compatibility mode).</para>
</note>
</section>
<section xml:id="parellelism">
<title>Parallelism</title>
<para><application>PMake</application> 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
<application>PMake</application> was configured to not act in
parallel, in which case you will have to make use of the
<option>-L</option> and <option>-J</option> flags (see below)),
but you do have to be careful at times.</para>
<para>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 <application>xstr</application>, for example, always
modifies the files <filename>strings</filename> and
<filename>x.c</filename>. 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 <literal>$$$$</literal> to
the end of the file name to tack on the process ID of the shell
executing the command (each <literal>$$</literal> expands to a
single <literal>$</literal>, thus giving you the shell variable
<literal>$$</literal>). Since only one shell is used for all
the commands, you will get the same file name for each command
in the script.</para>
<para>The other problem comes from improperly-specified
dependencies that worked in <application>Make</application>
because of its sequential, depth-first way of examining them.
While I do not want to go into depth on how
<application>PMake</application> works (look in <xref linkend="gods"/> if you are interested), I will warn you that
files in two different levels of the dependency tree may be
examined in a different order in
<application>PMake</application> than they were in
<application>Make</application>.
For example, given the makefile:</para>
<programlisting>a :
b c b : d</programlisting>
<para><application>PMake</application> will examine the targets in
the order <buildtarget>c</buildtarget>,
<buildtarget>d</buildtarget>, <buildtarget>b</buildtarget>,
<buildtarget>a</buildtarget>. If the makefile's author expected
<application>PMake</application> to abort before making
<buildtarget>c</buildtarget> if an error occurred while making
<buildtarget>b</buildtarget>, or if <buildtarget>b</buildtarget>
needed to exist before <buildtarget>c</buildtarget> was made,
(s)he will be sorely disappointed. The dependencies are
incomplete, since in both these cases,
<buildtarget>c</buildtarget> would depend on
<buildtarget>b</buildtarget>. So watch out.</para>
<para>Another problem you may face is that, while
<application>PMake</application> 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 <filename>/dev/tty</filename> I
mentioned earlier, you must be careful not to run two of the
jobs at once.</para>
</section>
<section xml:id="writeanddebug">
<title>Writing and Debugging a Makefile</title>
<para>Now you know most of what is in a
<filename>Makefile</filename>, what do you do next? There are
two choices: use one of the uncommonly-available makefile
generators or write your own makefile (I leave out the third
choice of ignoring <application>PMake</application> and doing
everything by hand as being beyond the bounds of common
sense).</para>
<para>When faced with the writing of a makefile, it is usually
best to start from first principles: just what are you trying to
do? What do you want the makefile finally to produce? To begin
with a somewhat traditional example, let's say you need to write
a makefile to create a program, <command>expr</command>, that
takes standard infix expressions and converts them to prefix
form (for no readily apparent reason). You have got three
source files, in C, that make up the program:
<filename>main.c</filename>, <filename>parse.c</filename>, and
<filename>output.c</filename>. Harking back to my pithy advice
about dependency lines, you write the first line of the
file:</para>
<programlisting>expr : main.o parse.o output.o</programlisting>
<para>because you remember <filename>expr</filename> is made from
<filename>.o</filename> files, not <filename>.c</filename>
files. Similarly for the <filename>.o</filename> files you
produce the lines:</para>
<programlisting>main.o : main.c
parse.o : parse.c
output.o : output.c
main.o parse.o output.o : defs.h</programlisting>
<para>Great. You have 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 have listed. You remember about local variables?
Good, so it should come to you as no surprise when you
write:</para>
<programlisting>expr : main.o parse.o output.o
cc -o $(.TARGET) $(.ALLSRC)</programlisting>
<para>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 and
<filename>defs.h</filename>, thus if you said:</para>
<programlisting>cc -c $(.ALLSRC)</programlisting>
<para>you will get (for <filename>main.o</filename>):</para>
<programlisting>cc -c main.c defs.h</programlisting>
<para>which is wrong. So you round out the makefile with these
lines:</para>
<programlisting>main.o : main.c
cc -c main.c
parse.o : parse.c
cc -c parse.c
output.o : output.c
cc -c output.c</programlisting>
<para>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 will address in <xref linkend="shortcuts"/>):</para>
<orderedlist>
<listitem>
<para>The string <literal>main.o parse.o output.o</literal> is
repeated twice, necessitating two changes when you add
postfix (you were planning on that, were not you?). This is
in direct violation of de Boor's First Rule of writing
makefiles:</para>
<para>Anything that needs to be written more than once should
be placed in a variable. I cannot emphasize this enough as
being very important to the maintenance of a makefile and
its program.</para>
</listitem>
<listitem>
<para>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:</para>
<para>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.</para>
</listitem>
</orderedlist>
<para>The makefile should more properly read:</para>
<programlisting>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</programlisting>
<para>Alternatively, if you like the idea of dynamic sources
mentioned in <xref linkend="localvariables"/>, you could write it
like this:</para>
<programlisting>OBJS = main.o parse.o output.o
expr : $(OBJS)
$(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
$(OBJS) : $(.PREFIX).c defs.h
$(CC) $(CFLAGS) -c $(.PREFIX).c</programlisting>
<para>These two rules and examples lead to de Boor's First
Corollary:
<emphasis>Variables are your friends</emphasis>.</para>
<para>Once you have written the makefile comes the
sometimes-difficult task of making sure the darn thing works.
Your most helpful tool to make sure the makefile is at least
syntactically correct is the <option>-n</option> flag, which
allows you to see if <application>PMake</application> will choke
on the makefile. The second thing the <option>-n</option> flag
lets you do is see what <application>PMake</application> would
do without it actually doing it, thus you can make sure the
right commands would be executed were you to give
<application>PMake</application> its head.</para>
<para>When you find your makefile is not behaving as you hoped,
the first question that comes to mind (after <quote>What time is
it, anyway?</quote>) is <quote>Why not?</quote> In answering
this, two flags will serve you well: <literal>-d m</literal> and
<quote>-p 2</quote>.
The first causes <application>PMake</application> 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 <quote>-p 2</quote> flag makes
<application>PMake</application> 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 <quote>-p 2</quote> is intended
to resemble closely a real makefile, but with additional
information provided and with variables expanded in those
commands <application>PMake</application> actually printed or
executed.</para>
<para>Something to be especially careful about is circular
dependencies. For example:</para>
<programlisting>a : b
b : c d
d : a</programlisting>
<para>In this case,
because of how <application>PMake</application> works,
<buildtarget>c</buildtarget> is the only thing
<application>PMake</application> will examine, because
<buildtarget>d</buildtarget> and <buildtarget>a</buildtarget> will
effectively fall off the edge of the universe, making it
impossible to examine <buildtarget>b</buildtarget> (or them, for
that matter). <application>PMake</application> will tell you
(if run in its normal mode) all the targets involved in any
cycle it looked at (i.e.&nbsp;if you have two cycles in the
graph (naughty, naughty), but only try to make a target in one
of them, <application>PMake</application> will only tell you
about that one. You will have to try to make the other to find
the second cycle). When run as <application>Make</application>,
it will only print the first target in the cycle.</para>
</section>
<section xml:id="invoking">
<title>Invoking PMake</title>
<para><application>PMake</application> 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:</para>
<variablelist>
<varlistentry>
<term><option>-d <replaceable>what</replaceable></option></term>
<listitem>
<para>This causes <application>PMake</application> to spew
out debugging information that may prove useful to you.
If you cannot figure out why
<application>PMake</application> is doing what it is
doing, you might try using this flag.
The <replaceable>what</replaceable> parameter is a string
of single characters that tell
<application>PMake</application> what aspects you are
interested in. Most of what I describe will make little
sense to you, unless you have dealt with
<application>Make</application> 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:</para>
<informaltable frame="none">
<tgroup cols="2">
<tbody>
<row valign="top">
<entry><literal>a</literal></entry>
<entry>Archive searching and caching.</entry>
</row>
<row valign="top">
<entry><literal>c</literal></entry>
<entry>Conditional evaluation.</entry>
</row>
<row valign="top">
<entry><literal>d</literal></entry>
<entry>The searching and caching of
directories.</entry>
</row>
<row valign="top">
<entry><literal>j</literal></entry>
<entry>Various snippets of information related to
the running of the multiple shells. Not
particularly interesting.</entry>
</row>
<row valign="top">
<entry><literal>m</literal></entry>
<entry>The making of each target: what target is
being examined; when it was last modified; whether
it is out-of-date; etc.</entry>
</row>
<row valign="top">
<entry><literal>p</literal></entry>
<entry>Makefile parsing.</entry>
</row>
<row valign="top">
<entry><literal>r</literal></entry>
<entry>Remote execution.</entry>
</row>
<row valign="top">
<entry><literal>s</literal></entry>
<entry>The application of suffix-transformation
rules. (See <xref linkend="shortcuts"/>.)</entry>
</row>
<row valign="top">
<entry><literal>t</literal></entry>
<entry>The maintenance of the list of targets.</entry>
</row>
<row valign="top">
<entry><literal>v</literal></entry>
<entry>Variable assignment.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Of these all, the <literal>m</literal> and
<literal>s</literal> letters will be most useful to you.
If the <option>-d</option> 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 &ndash;, all of these debugging flags will
be set, resulting in massive amounts of output.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-f</option> makefile</term>
<listitem>
<para>Specify a makefile to read different from the standard
makefiles (<filename>Makefile</filename> or
<filename>makefile</filename>).
If makefile is <literal>-</literal>,
<application>PMake</application> uses the standard input.
This is useful for making quick and dirty makefiles.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<listitem>
<para>Prints out a summary of the various flags
<application>PMake</application> accepts.
It can also be used to find out what level of concurrency was
compiled into the version of <application>PMake</application> you
are using (look at <literal>-J</literal> and
<literal>-L</literal>) and various other information on how
<application>PMake</application> was configured.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-i</option></term>
<listitem>
<para>If you give this flag, <application>PMake</application> will
ignore non-zero status returned by any of its shells. It is like
placing a <literal>-</literal> before all the commands in the
makefile.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-k</option></term>
<listitem>
<para>This is similar to <option>-i</option> in that it allows
<application>PMake</application> to continue when it sees an
error, but unlike <option>-i</option>, where
<application>PMake</application> continues blithely as if nothing
went wrong, <option>-k</option> causes it to recognize the error
and only continue work on those things that do not depend on the
target, either directly or indirectly (through depending on
something that depends on it), whose creation returned the error.
The <option>k</option> is for <quote>keep going</quote>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-l</option></term>
<listitem>
<para><application>PMake</application> has the ability to lock a
directory against other people executing it in the same directory
(by means of a file called <filename>LOCK.make</filename> 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, <option>-l</option> will turn it
off, and vice versa.
Note that this locking will not prevent you from invoking
<application>PMake</application> twice in the same place&ndash;if
you own the lock file, <application>PMake</application> will warn
you about it but continue to execute.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-m <replaceable>directory</replaceable></option></term>
<listitem>
<para>Tells <application>PMake</application> another place to search
for included makefiles via the
&lt;<replaceable>filename</replaceable>&gt; style.
Several <filename>-m</filename> options can be given to form a
search path. If this construct is used the default system
makefile search path is completely overridden.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<listitem>
<para>This flag tells <application>PMake</application> not to
execute the commands needed to update the out-of-date targets in
the makefile. Rather, <application>PMake</application> will
simply print the commands it would have executed and exit.
This is particularly useful for checking the correctness of a
makefile. If <application>PMake</application> does not do what
you expect it to, it is a good chance the makefile is wrong.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-p number</option></term>
<listitem>
<para>This causes <application>PMake</application> to print its
input in a reasonable form, though not necessarily one that would
make immediate sense to anyone but me. The 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 <option>-p 3</option> would print it twice-a-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.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-q</option></term>
<listitem>
<para>If you give <application>PMake</application> 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.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-r</option></term>
<listitem>
<para>When <application>PMake</application> starts up, it reads a
default makefile that tells it what sort of system it is on and
gives it some idea of what to do if you do not tell it anything.
I will tell you about it in <xref linkend="shortcuts"/>.
If you give this flag, <application>PMake</application> will not
read the default makefile.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-s</option></term>
<listitem>
<para>This causes <application>PMake</application> to not print
commands before they are executed. It is the equivalent of
putting an <quote>@</quote> before every command in the
makefile.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-t</option></term>
<listitem>
<para>Rather than try to re-create a target,
<application>PMake</application> will simply <quote>touch</quote>
it so as to make it appear up-to-date.
If the target did not exist before, it will when
<application>PMake</application> finishes, but if the target did
exist, it will appear to have been updated.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-v</option></term>
<listitem>
<para>Targets can still be created in parallel, however.
This is the mode <application>PMake</application> will enter
if it is invoked either as <command>smake</command> or
<command>vmake</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-x</option></term>
<listitem>
<para>This tells <application>PMake</application> it is OK to export
jobs to other machines, if they are 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.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-B</option></term>
<listitem>
<para>Forces <application>PMake</application> to be as
backwards-compatible with <application>Make</application> as
possible while still being itself. This includes:</para>
<itemizedlist>
<listitem>
<para>Executing one shell per shell command</para>
</listitem>
<listitem>
<para>Expanding anything that looks even vaguely like a
variable, with the empty string replacing any variable
<application>PMake</application> does not know.</para>
</listitem>
<listitem>
<para>Refusing to allow you to escape a <literal>#</literal>
with a backslash.</para>
</listitem>
<listitem>
<para>Permitting undefined variables on dependency lines and
conditionals (see below). Normally this causes
<application>PMake</application> to abort.</para>
</listitem>
</itemizedlist>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-C</option></term>
<listitem>
<para>This nullifies any and all compatibility mode flags you may
have given or implied up to the time the <option>-C</option> is
encountered. It is useful mostly in a makefile that you wrote for
<application>PMake</application> to avoid bad things happening
when someone runs <application>PMake</application> as
<application>make</application> or has things set in the
environment that tell it to be compatible.
<option>-C</option> is not placed in the <envar>PMAKE</envar>
environment variable or the <varname>.MAKEFLAGS</varname> or
<envar>MFLAGS</envar> global variables.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-D <replaceable>variable</replaceable></option></term>
<listitem>
<para>Allows you to define a variable to have <quote>1</quote> 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 will get
into in <xref linkend="condition"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-I <replaceable>directory</replaceable></option></term>
<listitem>
<para>Tells <application>PMake</application> another place to search
for included makefiles. Yet another thing to be explained in
<xref linkend="shortcuts"/> (<xref linkend="including"/>, to be
precise).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-J <replaceable>number</replaceable></option></term>
<listitem>
<para>Gives the absolute maximum number of targets to create at once
on both local and remote machines.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-L <replaceable>number</replaceable></option></term>
<listitem>
<para>This specifies the maximum number of targets to create on the
local machine at once.
This may be <literal>0</literal>, though you should be wary of
doing this, as <application>PMake</application> may hang until a
remote machine becomes available, if one is not available when it
is started.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-M</option></term>
<listitem>
<para>This is the flag that provides absolute, complete, full
compatibility with <application>Make</application>. It still
allows you to use all but a few of the features of
<application>PMake</application>, but it is non-parallel.
This is the mode <application>PMake</application> enters if you
call it <command>make</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-P</option></term>
<listitem>
<para>When creating targets in parallel, several shells are
executing at once, each wanting to write its own two cents'-worth
to the screen.
This output must be captured by <application>PMake</application>
in some way in order to prevent the screen from being filled with
garbage even more indecipherable than you usually see.
<application>PMake</application> 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 <option>-P</option>
flag to <application>PMake</application>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-V</option></term>
<listitem>
<para>As mentioned before, the <option>-V</option> flag tells
<application>PMake</application> to use
<application>Make</application>'s style of expanding variables,
substituting the empty string for any variable it does not
know.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-W</option></term>
<listitem>
<para>There are several times when <application>PMake</application>
will print a message at you that is only a warning, i.e.&nbsp;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
<application>PMake</application> to stop bothering you. This flag
tells it to shut up about anything non-fatal.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-X</option></term>
<listitem>
<para>This flag causes <application>PMake</application> to not
attempt to export any jobs to another machine.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Several flags may follow a single <literal>-</literal>. Those flags
that require arguments take them from successive parameters.
For example:</para>
<screen>pmake -fDnI server.mk DEBUG /chip2/X/server/include</screen>
<para>will cause <application>PMake</application> to read
<filename>server.mk</filename> as the input makefile,
define the variable <varname>DEBUG</varname> as a global variable and
look for included makefiles in the directory
<filename>/chip2/X/server/include</filename>.</para>
</section>
<section xml:id="summary">
<title>Summary</title>
<para>A makefile is made of four types of lines:</para>
<itemizedlist>
<listitem>
<para>Dependency lines</para>
</listitem>
<listitem>
<para>Creation commands</para>
</listitem>
<listitem>
<para>Variable assignments</para>
</listitem>
<listitem>
<para>Comments, include statements and conditional directives</para>
</listitem>
</itemizedlist>
<para>A dependency line is a list of one or more targets, an operator
(<literal>:</literal>, <literal>::</literal>, or <literal>!</literal>),
and a list of zero or more sources. Sources may contain wildcards and
certain local variables.</para>
<para>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 <literal>@</literal> or
<literal>-</literal>, <application>PMake</application> will cause the
command to not be printed (if the character is <literal>@</literal>) or
errors from it to be ignored (if <literal>-</literal>). A blank line,
dependency line or variable assignment terminates a creation script.
There may be only one creation script for each target with a
<literal>:</literal> or <literal>!</literal> operator.</para>
<para>Variables are places to store text. They may be unconditionally
assigned-to using the <literal>=</literal> operator, appended-to using
the <literal>+=</literal> operator, conditionally (if the variable is
undefined) assigned-to with the <literal>?=</literal> operator, and
assigned-to with variable expansion with the <literal>:=</literal>
operator. The output of a shell command may be assigned to a variable
using the <literal>!=</literal> 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
<application>PMake</application> does not know about them.
There are seven local variables: <varname>.TARGET</varname>,
<varname>.ALLSRC</varname>, <varname>.OODATE</varname>,
<varname>.PREFIX</varname>, <varname>.IMPSRC</varname>,
<varname>.ARCHIVE</varname>, and <varname>.MEMBER</varname>.
Four of them (<varname>.TARGET</varname>, <varname>.PREFIX</varname>,
<varname>.ARCHIVE</varname>, and <varname>.MEMBER</varname>) may be used
to specify <quote>dynamic sources</quote>. Variables are good. Know
them. Love them. Live them.</para>
<para>Debugging of makefiles is best accomplished using the
<option>-n</option>, <option>-d m</option>, and
<option>-p 2</option> flags.</para>
</section>
</chapter>