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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
|
<!-- $NetBSD: makefile.xml,v 1.31 2022/11/18 19:35:03 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 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.style">
<title>Caveats</title>
<itemizedlist><listitem><para>When you are creating a file as a
target of a rule, always write the data to a temporary file first
and finally rename that file. Otherwise there might occur an error
in the middle of generating the file, and when the user runs
&man.make.1; for the second time, the file exists and will not be
regenerated properly. Example:</para>
<programlisting>
wrong:
@echo "line 1" > ${.TARGET}
@echo "line 2" >> ${.TARGET}
@false
correct:
@echo "line 1" > ${.TARGET}.tmp
@echo "line 2" >> ${.TARGET}.tmp
@false
@mv ${.TARGET}.tmp ${.TARGET}
</programlisting>
<para>When you run <command>make wrong</command> twice, the file
<filename>wrong</filename> will exist, although there was an error
message in the first run. On the other hand, running <command>make
correct</command> gives an error message twice, as expected.</para>
<para>You might remember that &man.make.1; sometimes removes
<literal>${.TARGET}</literal> in case of error, but this only
happens when it is interrupted, for example by pressing
<literal>Ctrl+C</literal>. This does <emphasis>not</emphasis> happen
when one of the commands fails (like &man.false.1; above).</para>
</listitem>
</itemizedlist>
</sect1>
<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 <code>=</code>,
<code>+=</code>, <code>?=</code>, <code>:=</code> and
<code>!=</code>, 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 <code>#</code> and
the backslash character <code>\</code> are handled specially. If a
backslash is the last character in a line, that backslash is removed
from the line and the line continues with the next line of the file.</para>
<para>The <code>#</code> character starts a comment that reaches
until the end of the line. To get an actual <code>#</code> character,
such as in a URL, write <code>\#</code> instead.</para>
<para>The evaluation of variables either happens immediately or lazy.
It happens immediately when the variable occurs on the right-hand
side of the <code>:=</code> or the <code>!=</code> operator, in a
<varname>.if</varname> condition or a <varname>.for</varname> loop.
In the other cases, it is evaluated lazily.</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 split into words, double quotes and single quotes are
interpreted as delimiters, just like in &man.sh.1;.</para>
<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 packages.</para></listitem>
<listitem><para>In <command>.for</command> loops you should use
lowercase variable names for the iteration
variables.</para></listitem>
<listitem><para>All list variables should have a plural name,
such as <varname>PKG_OPTIONS</varname> or
<varname>DISTFILES</varname>.</para></listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="makefile.code">
<title>Code snippets</title>
<sect2 id="adding-to-list">
<title>Adding things to a list</title>
<para>When adding a string that possibly contains whitespace or quotes to
a list (example 1), it must be quoted using the <code>:Q</code>
modifier.</para>
<para>When adding another list to a list (example 2), it must not be
quoted, since its elements are already quoted.</para>
<programlisting>
STRING= foo * bar `date`
LIST= # empty
ANOTHER_LIST= a=b c=d
LIST+= ${STRING:Q} # 1
LIST+= ${ANOTHER_LIST} # 2
</programlisting>
</sect2>
<sect2 id="echo-literal">
<title>Echoing a string exactly as-is</title>
<para>Echoing a string containing special characters needs special
work.</para>
<programlisting>
STRING= foo bar < > * `date` $$HOME ' "
EXAMPLE_ENV= string=${STRING:Q} x=multiple\ quoted\ words
all:
echo ${STRING} # 1
echo ${STRING:Q} # 2
printf '%s\n' ${STRING:Q}'' # 3
env ${EXAMPLE_ENV} sh -c 'echo "$$string"; echo "$$x"' # 4
</programlisting>
<para>Example 1 leads to a syntax error in the shell, as the characters
are just copied.</para>
<para>Example 2 quotes the string so that the shell interprets it
correctly. But the echo command may additionally interpret strings with a
leading dash or those containing backslashes.</para>
<para>Example 3 can handle arbitrary strings, since &man.printf.1; only
interprets the format string, but not the next argument. The trailing
single quotes handle the case when the string is empty. In that case, the
:Q modifier would result in an empty string too, which would then be
skipped by the shell. For &man.printf.1; this doesn't make a difference,
but other programs may care.</para>
<para>In example 4, the <varname>EXAMPLE_ENV</varname> does not
need to be quoted because the quoting has already been done
when adding elements to the list.</para>
</sect2>
<sect2 id="cflags-gnu-configure">
<title>Passing <varname>CFLAGS</varname> to GNU configure scripts</title>
<para>When passing <varname>CFLAGS</varname> or similar variables to a
GNU-style configure script (especially those that call other configure
scripts), it must not have leading or trailing whitespace, since
otherwise the configure script gets confused. To trim leading and
trailing whitespace, use the <code>:M</code> modifier, as in the
following example:</para>
<!--
Additional details, intentionally left out of the guide to keep the text
short:
If the configure script calls 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>.
-->
<programlisting>
CPPFLAGS= # empty
CPPFLAGS+= -Wundef -DPREFIX=\"${PREFIX}\"
CPPFLAGS+= ${MY_CPPFLAGS}
CONFIGURE_ARGS+= CPPFLAGS=${CPPFLAGS:M*:Q}
all:
echo x${CPPFLAGS:Q}x # leading and trailing whitespace
echo x${CONFIGURE_ARGS:Q}x # properly trimmed
</programlisting>
<para>In this example, <varname>CPPFLAGS</varname> has both leading and
trailing whitespace because the <code>+=</code> operator always adds a
space.</para>
</sect2>
<sect2 id="empty-variables">
<title>Handling possibly empty variables</title>
<para>When a possibly empty variable is used in a shell program, it may
lead to a syntax error.</para>
<programlisting>
EGFILES= # empty
install-examples: # produces a syntax error in the shell
for egfile in ${EGFILES}; do \
echo "Installing $$egfile"; \
done
</programlisting>
<para>The shell only sees the text <code>for egfile in ; do</code>, since
<code>${EGFILES}</code> is replaced with an empty string by &man.make.1;.
To fix this syntax error, use one of the snippets below.</para>
<programlisting>
EGFILES= # empty
install-examples:
for egfile in ${EGFILES} ""; do \
[ -n "$$egfile" ] || continue; \
echo "Installing $$egfile"; \
done
</programlisting>
<para>In this case, an empty string is appended to the iteration list (to
prevent the syntax error) and filtered out later.</para>
<programlisting>
EGFILES= # empty
install-examples:
.for egfile in ${EGFILES}
echo "Installing ${egfile}"
.endfor
</programlisting>
<para>If one of the filenames contains special characters, it should be
enclosed in single or double quotes.</para>
<para>To have a shell command test whether a make variable is empty, use
the following code: <code>${TEST} -z ${POSSIBLY_EMPTY:Q}""</code>.</para>
</sect2>
<sect2 id="makefile.yesno">
<title>Testing yes/no variables in conditions</title>
<para>When a variable can have the values <literal>yes</literal> or
<literal>no</literal>, use the following pattern to test the variable:</para>
<programlisting>
.if ${VAR:U:tl} == "yes"
# do something
.endif
</programlisting>
<para>The <literal>:U</literal> modifier is only necessary if the variable
can be undefined. If the variable is guaranteed to be defined, the
<literal>:U</literal> can be omitted.</para>
<para>The <literal>:tl</literal> modifier converts the variable value to
lowercase, allowing for the values <literal>yes</literal>,
<literal>Yes</literal>, <literal>YES</literal>.</para>
</sect2>
</sect1>
</chapter>
|