Programming in Makefiles
Pkgsrc consists of many Makefile 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.
The basic ingredients for Makefile
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.
This chapter describes some patterns, that appear quite often in
Makefiles, including the pitfalls that come along
with them.
Caveats
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:
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}
When you run make wrong twice, the file
wrong will exist, although there was an error
message in the first run. On the other hand, running make
correct gives an error message twice, as expected.
You might remember that &man.make.1; sometimes removes
${.TARGET} in case of error, but this only
happens when it is interrupted, for example by pressing
^C. This does not happen
when one of the commands fails (like &man.false.1; above).
Makefile variables
Makefile variables contain strings that
can be processed using the five operators ``='', ``+='', ``?='',
``:='', and ``!='', which are described in the &man.make.1; man
page.
When a variable's value is parsed from a
Makefile, 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
backslash 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.
The evaluation of variables either happens immediately or lazy.
It happens immediately when the variable occurs
on the right-hand side of the ``:='' or the ``!='' operator, in a
.if condition or a .for loop.
In the other cases, it is evaluated lazily.
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, it is split like in &man.sh.1;.
There are several types of variables that should be handled
differently. Strings and two types of lists.
Strings can contain arbitrary
characters. Nevertheless, you should restrict yourself to only
using printable characters. Examples are
PREFIX and
COMMENT.
Internal lists 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 .for loops.
Examples are DEPENDS and
BUILD_DEPENDS.
External lists 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 .for loops. Examples are
DISTFILES and
MASTER_SITES.
Naming conventions
All variable names starting with an underscore
are reserved for use by the pkgsrc infrastructure. They shall
not be used by package
Makefiles.
In .for loops you should use
lowercase variable names for the iteration
variables.
All list variables should have a ``plural''
name, e.g. PKG_OPTIONS or
DISTFILES.
Code snippets
Adding things to a list
When adding a string that possibly contains whitespace or quotes to
a list (example 1), it must be quoted using the :Q
modifier.
When adding another list to a list (example 2), it must not be
quoted, since its elements are already quoted.
STRING= foo * bar `date`
LIST= # empty
ANOTHER_LIST= a=b c=d
LIST+= ${STRING:Q} # 1
LIST+= ${ANOTHER_LIST} # 2
Echoing a string exacty as-is
Echoing a string containing special characters needs special
work.
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
Example 1 leads to a syntax error in the shell, as the characters
are just copied.
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.
Example 3 can handle arbitrary strings, since &man.printf.1; only
interprets the format string, but not the next argument.
In example 4, the EXAMPLE_ENV does not
need to be quoted because the quoting has already been done
when adding elements to the list.
Passing CFLAGS to GNU configure scripts
When passing CFLAGS 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 :M
modifier, as in the
following example:
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
In this example, CPPFLAGS has both leading and
trailing whitespace because the +=
operator always adds a
space.
Handling possibly empty variables
When a possibly empty variable is used in a shell program, it may
lead to a syntax error.
EGFILES= # empty
install-examples: # produces a syntax error in the shell
for egfile in ${EGFILES}; do \
echo "Installing $$egfile"; \
done
The shell only sees the text for egfile in ; do
, since
${EGFILES}
is replaced with an empty string by &man.make.1;.
To fix this syntax error, use one of the snippets below.
EMPTY= # empty
install-examples:
for egfile in ${EGFILES} ""; do \
[ -n "$$egfile" ] || continue; \
echo "Installing $$egfile"; \
done
In this case, an empty string is appended to the iteration list (to
prevent the syntax error) and filtered out later.
EGFILES= # empty
install-examples:
.for egfile in ${EGFILES}
echo "Installing ${egfile}"
.endfor
This variant only works when EGFILES does not
contain filenames with spaces, since the .for
loop splits on
simple whitespace.
To have a shell command test whether a make variable is empty, use
the following code: ${TEST} -z ${POSSIBLY_EMPTY:Q}""
.