diff options
Diffstat (limited to 'en_US.ISO8859-1/books/pmake/basics/chapter.sgml')
-rw-r--r-- | en_US.ISO8859-1/books/pmake/basics/chapter.sgml | 1539 |
1 files changed, 1539 insertions, 0 deletions
diff --git a/en_US.ISO8859-1/books/pmake/basics/chapter.sgml b/en_US.ISO8859-1/books/pmake/basics/chapter.sgml new file mode 100644 index 0000000000..b1ad209b32 --- /dev/null +++ b/en_US.ISO8859-1/books/pmake/basics/chapter.sgml @@ -0,0 +1,1539 @@ +<!-- + $FreeBSD$ +--> + +<chapter 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 <option>-f</option> <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 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- 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: <maketarget>program</maketarget> + cannot be made by linking together <filename>.c</filename> + files—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—which files (targets) need to + be created from which files (sources)—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>).></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 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 – 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) < <devicename>/dev/tty</devicename></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 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>@^&$%$</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—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. <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. 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. 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 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><makevar>.TARGET</makevar></term> + + <listitem> + <para>The name of the target.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><makevar>.OODATE</makevar></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><makevar>.ALLSRC</makevar></term> + + <listitem> + <para>The list of all sources for this target in the order + in which they were given.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><makevar>.PREFIX</makevar></term> + + <listitem> + <para>The target without its suffix and without any + leading path. E.g. 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 + <makevar>.IMPSRC,</makevar> <makevar>.ARCHIVE,</makevar> and + <makevar>.MEMBER</makevar> 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 <makevar>.TARGET</makevar>, + <makevar>.PREFIX</makevar>, <makevar>.ARCHIVE</makevar> and + <makevar>.MEMBER</makevar>. 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 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 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><makevar>.PMAKE</makevar></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 + <makevar>MAKE</makevar> variable.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><makevar>.MAKEFLAGS</makevar></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 <makevar>MFLAGS</makevar> + variable as well.</para> + </listitem> + </varlistentry> + </variablelist> + + <para>Two other variables, <makevar>.INCLUDES</makevar> and + <makevar>.LIBS,</makevar> 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 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 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 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 <maketarget>c</maketarget>, + <maketarget>d</maketarget>, <maketarget>b</maketarget>, + <maketarget>a</maketarget>. If the makefile's author expected + <application>PMake</application> to abort before making + <maketarget>c</maketarget> if an error occurred while making + <maketarget>b</maketarget>, or if <maketarget>b</maketarget> + needed to exist before <maketarget>c</maketarget> was made, + (s)he will be sorely disappointed. The dependencies are + incomplete, since in both these cases, + <maketarget>c</maketarget> would depend on + <maketarget>b</maketarget>. 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 <devicename>/dev/tty</devicename> I + mentioned earlier, you must be careful not to run two of the + jobs at once.</para> + </section> + + <section 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, + <maketarget>c</maketarget> is the only thing + <application>PMake</application> will examine, because + <maketarget>d</maketarget> and <maketarget>a</maketarget> will + effectively fall off the edge of the universe, making it + impossible to examine <maketarget>b</maketarget> (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. 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 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 –, 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–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 + <<replaceable>filename</replaceable>> 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 <makevar>.MAKEFLAGS</makevar> 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. 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 <makevar>DEBUG</makevar> as a global variable and + look for included makefiles in the directory + <filename>/chip2/X/server/include</filename>.</para> + </section> + + <section 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: <makevar>.TARGET</makevar>, + <makevar>.ALLSRC</makevar>, <makevar>.OODATE</makevar>, + <makevar>.PREFIX</makevar>, <makevar>.IMPSRC</makevar>, + <makevar>.ARCHIVE</makevar>, and <makevar>.MEMBER</makevar>. + Four of them (<makevar>.TARGET</makevar>, <makevar>.PREFIX</makevar>, + <makevar>.ARCHIVE</makevar>, and <makevar>.MEMBER</makevar>) 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> + +<!-- + Local Variables: + mode: sgml + sgml-indent-data: t + sgml-omittag: nil + sgml-always-quote-attributes: t + sgml-parent-document: ("../book.sgml" "part" "chapter") + End: +--> |