1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
<!-- $NetBSD: makefile.xml,v 1.4 2005/05/11 20:53:27 rillig Exp $ -->
<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 before the
backslash, the backslash, the newline, and any whitespace
immediately the newline are replaced with a single space. A
backspace character followed by a 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 reaches 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
can only be constructed using the ``!='' operator.</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 this 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>There are several types of variables that must be handled
differently.</para>
<itemizedlist>
<listitem><para><emphasis>Simple values</emphasis> (which I will
call atoms) can contain any string, which does not have to be
quoted in any way. All other types are somewhat restricted in
their possible values.</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>,
<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>
</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>
ATOM= 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+= ${ATOM} # 1
INT_LIST+= ${ANOTHER_INT_LIST} # 2
EXT_LIST+= ${ATOM:Q} # 3
EXT_LIST+= ${ANOTHER_EXT_LIST} # 4
</programlisting>
<para>When you add an atom 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.</para>
</sect2>
<sect2>
<title>Passing variables to a shell command</title>
<programlisting>
ATOM= foo bar < > * `date` $$HOME ' "
EXT_LIST= atom=${ATOM:Q} x=second\ item
all:
echo ${ATOM} # 1
echo "${ATOM}" # 2
echo "${ATOM:Q}" # 3
echo ${ATOM:Q} # 4
echo x${ATOM:Q} | sed 1s,.,, # 5
env ${EXT_LIST} /bin/sh -c 'echo "$$atom"; 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>${ATOM}</varname>,
&man.date.1; would be executed. The <varname>$HOME</varname> shell
variable would be evaluated, too.</para>
<para>Example 3 would output 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>
</sect1>
</chapter>
|