pkgsrc/doc/guide/files/makefile.xml
rillig 3ce24f9a87 Applied the quoting rules to my own examples. "Converting an internal
list to an external list" had been wrong.
2005-05-14 03:30:56 +00:00

306 lines
12 KiB
XML

<!-- $NetBSD: makefile.xml,v 1.11 2005/05/14 03:30:56 rillig Exp $ -->
<!-- based on:
pkgsrc/bootstrap/bmake/for.c 1.1.1.1
pkgsrc/bootstrap/bmake/make.1 1.3
pkgsrc/bootstrap/bmake/str.c 1.1.1.1
pkgsrc/bootstrap/bmake/var.c 1.2
-->
<chapter id="makefile"> <?dbhtml filename="makefile.html"?>
<title>Programming in <filename>Makefile</filename>s</title>
<para>Pkgsrc consists of many <filename>Makefile</filename> fragments,
each of which forms a well-defined part of the pkgsrc system. Using
the &man.make.1; system as a programming language for a big system
like pkgsrc requires some discipline to keep the code correct and
understandable.</para>
<para>The basic ingredients for <filename>Makefile</filename>
programming are variables (which are actually macros) and shell
commands. Among these shell commands may even be more complex ones
like &man.awk.1; programs. To make sure that every shell command runs
as intended it is necessary to quote all variables correctly when they
are used.</para>
<para>This chapter describes some patterns, that appear quite often in
<filename>Makefile</filename>s, including the pitfalls that come along
with them.</para>
<sect1 id="makefile.variables">
<title><filename>Makefile</filename> variables</title>
<para><filename>Makefile</filename> variables contain strings that
can be processed using the five operators ``='', ``+='', ``?='',
``:='', and ``!='', which are described in the &man.make.1; man
page.</para>
<para>When a variable's value is parsed from a
<filename>Makefile</filename>, the hash character ``#'' and the
backslash character ``\'' are handled specially. If a backslash is
followed by a newline, any whitespace immediately in front of the
backslash, the backslash, the newline, and any whitespace
immediately behind the newline are replaced with a single space. A
backspace character and an immediately following hash character are
replaced with a single hash character. Otherwise the backslash is
passed as is. In a variable assignment, any hash character that is
not preceded by a backslash starts a comment that continues upto the
end of the logical line.</para>
<para><emphasis>Note:</emphasis> Because of this parsing algorithm
the only way to create a variable consisting of a single backslash
is using the ``!='' operator, for example: <!-- FIXME
--><varname>BACKSLASH!=echo "\\"</varname>.</para>
<para>So far for defining variables. The other thing you can do with
variables is evaluating them. A variable is evaluated when it is
part of the right side of the ``:='' or the ``!='' operator, or
directly before executing a shell command which the variable is part
of. In all other cases &man.make.1; performs lazy evaluation, that
is, variables are not evaluated until there's no other way. The
``modifiers'' mentioned in the man page also evaluate the
variable.</para>
<para>Some of the modifiers split the string into words and then
operate on the words, others operate on the string as a whole. When
a string is splitted into words, it is splitted as you would expect
it from &man.sh.1;.</para>
<para>No rule without exception&mdash;the ``.for'' loop does not
follow the shell quoting rules but splits at sequences of
whitespace.</para>
<para>There are several types of variables that should be handled
differently. Strings and two types of lists.</para>
<itemizedlist>
<listitem><para><emphasis>Strings</emphasis> can contain arbitrary
characters. Nevertheless you should restrict yourself to only
using printable characters. Examples are
<varname>PREFIX</varname> and
<varname>COMMENT</varname>.</para></listitem>
<listitem><para><emphasis>Internal lists</emphasis> are lists that
are never exported to any shell command. Their elements are
separated by whitespace. Therefore the elements themselves cannot
have embedded whitespace. Any other characters are allowed.
Internal lists can be used in <!-- FIXME
--><varname>.for</varname> loops. Examples are
<varname>DEPENDS</varname> and
<varname>BUILD_DEPENDS</varname>.</para></listitem>
<listitem><para><emphasis>External lists</emphasis> are lists that
may be exported to a shell command. Their elements can contain any
characters, including whitespace. That's why they cannot be used
in <!-- FIXME --><varname>.for</varname> loops. Examples are
<varname>DISTFILES</varname> and
<varname>MASTER_SITES</varname>.</para></listitem>
</itemizedlist>
<sect2 id="makefile.variables.names">
<title>Naming conventions</title>
<itemizedlist>
<listitem><para>All variable names starting with an underscore
are reserved for use by the pkgsrc infrastructure. They shall
not be used by package
<filename>Makefile</filename>s.</para></listitem>
<listitem><para>In <!-- FIXME --><varname>.for</varname> loops
you should use lowercase variable names for the iteration
variables.</para></listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="makefile.code">
<title>Code snippets</title>
<para>This section presents you with some code snippets you should
use in your own code. If you don't find anything appropriate here,
you should test your code and add it here.</para>
<sect2>
<title>Adding things to a list</title>
<programlisting>
STRING= foo * bar `date`
INT_LIST= # empty
ANOTHER_INT_LIST= apache-[0-9]*:../../www/apache
EXT_LIST= # empty
ANOTHER_EXT_LIST= a=b c=d
INT_LIST+= ${STRING} # 1
INT_LIST+= ${ANOTHER_INT_LIST} # 2
EXT_LIST+= ${STRING:Q} # 3
EXT_LIST+= ${ANOTHER_EXT_LIST} # 4
</programlisting>
<para>When you add a string to an external list (example 3), it
must be quoted. In all other cases, you must not add a quoting
level. You must not merge internal and external lists, unless you
are sure that all entries are correctly interpreted in both
lists.</para>
</sect2>
<sect2>
<title>Converting an internal list into an external list</title>
<programlisting>
EXT_LIST= # empty
.for i in ${INT_LIST}
EXT_LIST+= ${i:Q}""
.endfor
</programlisting>
<para>This code converts the internal list
<varname>INT_LIST</varname> into the external list
<varname>EXT_LIST</varname>. As the elements of an internal list
are unquoted they must be quoted here. The reason for appending
<varname>""</varname> is explained below.</para>
</sect2>
<sect2>
<title>Passing variables to a shell command</title>
<programlisting>
STRING= foo bar < > * `date` $$HOME ' "
EXT_LIST= string=${STRING:Q} x=second\ item
all:
echo ${STRING} # 1
echo "${STRING}" # 2
echo "${STRING:Q}" # 3
echo ${STRING:Q} # 4
echo x${STRING:Q} | sed 1s,.,, # 5
env ${EXT_LIST} /bin/sh -c 'echo "$$string"; echo "$$x"'
</programlisting>
<para>Example 1 leads to a syntax error in the shell, as the
characters are just copied.</para>
<para>Example 2 leads to a syntax error too, and if you leave out
the last " character from <varname>${STRING}</varname>,
&man.date.1; will be executed. The <varname>$HOME</varname> shell
variable would be evaluated, too.</para>
<para>Example 3 outputs each space character preceded by a
backslash (or not), depending on the implementation of the
&man.echo.1; command.</para>
<para>Example 4 handles correctly every string that does not start
with a dash. In that case, the result depends on the
implementation of the &man.echo.1; command. As long as you can
guarantee that your input does not start with a dash this form is
appropriate.</para>
<para>Example 5 handles even the case of a leading dash
correctly.</para>
<para>The <varname>EXT_LIST</varname> does not need to be quoted
because the quoting has already be done when adding elements to
the list.</para>
<para>As internal lists shall not be passed to the shell, there is
no example for it.</para>
</sect2>
<sect2>
<title>Quoting guideline</title>
<para>There are many possible sources of wrongly quoted variables.
This section lists some of the commonly known ones.</para>
<itemizedlist>
<listitem><para>Whenever you use the value of a list, think
about what happens to leading or trailing whitespace. If the
list is a well-formed shell expression you can apply the
<varname>:M*</varname> modifier to strip leading and trailing
whitespace from each word. The <varname>:M</varname> operator
first splits its argument according to the rules of the shell,
and then creates a new list consisting of all words that match
the shell glob expression <varname>*</varname>, that is: all.
One class of situations where this is needed is when adding a
variable like <varname>CPPFLAGS</varname> to
<varname>CONFIGURE_ARGS</varname>. If the configure script
invokes other configure scripts it strips the leading and
trailing whitespace from the variable and then passes it to the
other configure scripts. But these configure scripts expect the
(child) <varname>CPPFLAGS</varname> variable to be the same as
the parent <varname>CPPFLAGS</varname>. That's why we better
pass the <varname>CPPFLAGS</varname> value properly trimmed. And
here is how we do it:</para>
<programlisting>
CPPFLAGS= # empty
CPPFLAGS+= -Wundef -DPREFIX=\"${PREFIX:Q}\"
CPPFLAGS+= ${MY_CPPFLAGS}
CONFIGURE_ARGS+= CPPFLAGS=${CPPFLAGS:M*:Q}
all:
echo x${CPPFLAGS:Q}x # leading and trailing whitespace
echo x${CONFIGURE_ARGS}x # properly trimmed
</programlisting></listitem>
<listitem><para>The example above contains one bug: The
<varname>${PREFIX}</varname> is a properly quoted shell
expression, but there is the C compiler after it, which also
expects a properly quoted string (this time in C syntax). The
version above is therefore only correct if
<varname>${PREFIX}</varname> does not have embedded backslashes
or double quotes. If you want to allow these, you have to add
another layer of quoting to each variable that is used as a C
string literal. You cannot use the <varname>:Q</varname>
operator for it, as this operator only works for the
shell.</para></listitem>
<listitem><para>Whenever a variable can be empty the
<varname>:Q</varname> operator can have surprising results. Here
are two completely different cases which can be solved with the
same trick.</para>
<programlisting>
EMPTY= # empty
empty_test:
for i in a ${EMPTY:Q} c; do \
echo "$$i"; \
done
for_test:
.for i in a:\ a:\test.txt
echo ${i:Q}
echo "foo"
.endfor
</programlisting>
<para>The first example will only print two of the three lines
we might have expected. This is because
<varname>${EMPTY:Q}</varname> expands to the empty string, which
the shell cannot see. The workaround is to write
<varname>${EMPTY:Q}""</varname>. This pattern can be often found
as <varname>${TEST} -z ${VAR:Q}</varname> or as <varname>${TEST}
-f ${FNAME:Q}</varname> (both of these are wrong).</para>
<para>The second example will only print three lines instead of
four. The first line looks like <varname>a:\ echo foo</varname>.
This is because the backslash of the value
<varname>a:\</varname> is interpreted as a line-continuation by
&man.make.1;, which makes the second line the arguments of the
&man.echo.1; command from the first line. To avoid this, write
<varname>${i:Q}""</varname>.</para></listitem>
</itemizedlist>
</sect2>
</sect1>
</chapter>