Make: Difference between revisions
Line 276: | Line 276: | ||
Function variables can either be defined by assigned or using <code>define... endef</code> construct. |
Function variables can either be defined by assigned or using <code>define... endef</code> construct. |
||
Assignments must be deferred (i.e. <code>=</code> and not <code>:-</code>), so that function is not evaluated at the time it is defined but only when used. Also if it contains comment symbol (<code>#</code), these must be escaped: |
Assignments must be deferred (i.e. <code>=</code> and not <code>:-</code>), so that function is not evaluated at the time it is defined but only when used. Also if it contains comment symbol (<code>#</code>), these must be escaped: |
||
<source lang=make> |
<source lang=make> |
||
makefilecfg = $(shell echo "Creating configuration file '$R$(MAKEFILE_CFG)$Z'." >&2; \ |
makefilecfg = $(shell echo "Creating configuration file '$R$(MAKEFILE_CFG)$Z'." >&2; \ |
Revision as of 00:17, 4 May 2012
make is an utility that determines automatically which pieces of a program need to be recompiled, and issues the commands to rebuild them. make uses a file called Makefile, which describes the relationships among files in the program.
References
- Recursive Make Considered Harmful
This papers explains that cutting big projects into smaller Makefiles that are called recursively greatly increase the build time, even when nothing changed. Instead it advices toinclude
smaller Makefiles in a master one, such that the dependency tree is complete.
Tools related to Make:
Some alternatives to Make:
- redo, a replacement for Make, based on ideas from Daniel J. Bernstein
- A post motivating why it could be the new build system that might replace Make
- Some even says redo could be the new Git of build systems (also [1])
- ... but some does not agree...
Quick Reference
Automatic Variables
$@ | Target of the rule (if multi target, the one that cause the rule's command to run) |
$% | Target member name, when target is an archive member |
$< | Name of 1st prerequisite |
$? | All newer prerequisites (more recent than the target) |
$^ | All prerequisites |
$+ | All prerequisites, incl. duplicates |
$* | The stem with which the rule matched (e.g. if target is dir/a.foo.b, a pattern like a.%.b gives a stem dir/foo) |
$(@D) | $(@F) | Resp. the directory (w/o trailing /) and filename part of the target |
$(*D) | $(*F) | Resp. the directory (w/o trailing /) and filename part of the stem |
$(<D) | $(<F) | Resp. the directory (w/o trailing /) and filename part of the 1st prerequisite |
$(^D) | $(^F) | Resp. the directory (w/o trailing /) and filename part of the all prerequisite |
$(+D) | $(+F) | Resp. the directory (w/o trailing /) and filename part of the all prerequisites, incl. duplicates |
$(?D) | $(?F) | Resp. the directory (w/o trailing /) and filename part of the newer prerequisites |
How to Use Variables
- Recursive vs. simple expansion
# Recursive expansion
CFLAGS = $(INCLUDES) -O
INCLUDES = -Ifoo -Ibar
all:
echo $(CFLAGS) # will echo '-Ifoo -Ibar -O'
# Simple expansion
x := foo
y := $(x) bar # expansion occurs here, once for all (y = foo bar)
x := later # y still = foo bar
- Substitution References
foo = a.o b.o c.o
bar = $(foo:.o=.c) # bar = a.c b.c c.c
- Using % (equiv. to
$(patsubst %.c,%.o,$(foo))
:
foo = a.o b.o c.o
bar = $(foo:%.o=%.c) # bar = a.c b.c c.c
Functions for File Names
- $(dir names...)
- Extract the directory part
- $(notdir names...)
- Extract the not-directory part
- $(suffix names...)
- Extract the suffix of each file name (incl. the period .)
- $(basename names...)
- Extract the basename of each file name
- $(addsuffix suffix,names...)
- Append the value of suffix to each file name.
$(addsuffic .c,foo bar) # Produces 'foo.c bar.c'
- $(addprefix prefix,names...)
$(addsuffic src/,foo bar) # Produces 'src/foo src/bar'
- $(join list1,list2)
- $(wildcard pattern)
- $(realpath names...)
- $(abspath names...)
Rules
all:
@echo About to make distribution files # @ : disable command echoing
-rm $(file) # - : don't stop on error (ignore)
Tips
Automatic Prerequisites
A standard solution (inspired from Makefile manual):
#----- BEGIN Automatic-Prerequisites -----
# - see http://www.gnu.org/software/make/manual/make.html#Automatic-Prerequisites
# - Note that .d files are automatically regenerated because of the include command below.
# - Only 1 .d file generated, indep. of defines. So if include files depend on defines, the .d files will *NOT* be updated.
-include $(SOURCES:.cpp=.d)
%.d: %.cpp
$(CPP) $(CFLAGS) $(INCPATHS) -MM -MT $*.o -MT $*.d -MF $@ $<
#----- END Automatic-Prerequisites -----
A more advanced solution, where object files are stored in directory $(VARDIR), and have a suffic $(VARIANT):
#----- BEGIN Automatic-Prerequisites -----
# - see http://www.gnu.org/software/make/manual/make.html#Automatic-Prerequisites
# - Note that .d files are automatically regenerated because of the include command below.
# - Only 1 .d file generated, indep. of defines. So if include files depend on defines, the .d files will *NOT* be updated.
# - [CHANGES] .o and .d files are stored in directory $(VARDIR)/, and .o have suffix $(VARIANT)
-include $(addprefix $(VARDIR)/, $(notdir $(SOURCES:.cpp=.d) ) )
$(VARDIR)/%.d: $(SRCDIR)/%.cpp
$(CPP) $(CFLAGS) $(INCPATHS) -MM -MT $(VARDIR)/$*$$"(VARIANT)".o -MT $*.d -MF $@ $<
#----- END Automatic-Prerequisites -----
Limitations:
- Only one .d file generated, indep. of $(DEFINES), so if $(DEFINES) changed, and include files depends on these defines, the .d files will not be updated.
- A solution would be to name the .d files according to relevant defines, as we do for the .o files
Adding prefixes
SOURCES = src/foo.c src/bar.c
all:
@echo $(addprefix subdir/,$(SOURCES)) # Use 'addprefix' function - clearer
@echo $(foreach V,$(SOURCES),subdir/$(V)) # Using 'foreach' - powerful but less clear
@echo $(SOURCES:%=subdir/%) # Using substitution - even more clear when...
@echo $(SOURCES:%.c=obj/%.o) # ... changing ext as well
Conditionals
There are several types of conditionals in make:
- Functions for conditionals
- These are like regular if-then instructions, and can be used along with Automatic Variables (i.e.
$@ $?
...).
main.zip:: nfc.zip $(JAVA_SRC) Makefile
$(if $(filter %.java,$?),echo 'Indenting (savagely) your code...'; uncrustify --no-backup -c indent.uncrustify.prefs $(filter %.java,$?))
- Conditional Parts of Makefiles
- These are like conditional directives in C preprocessor. From the documentation:
make evaluates conditionals when it reads a makefile. Consequently, you cannot use automatic variables in the tests of conditionals because they are not defined until recipes are run (see Automatic Variables).
#Conditianals in rule recipes:
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
#Conditionals in variable definitions:
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
foo: $(objects)
$(CC) -o foo $(objects) $(libs)
Echo while 1st pass expansion
To have make echo some information while in the 1st pass expansion phase (i.e. before running target recipe), one can use a dummy variable and $(shell ...)
function:
x := $(shell echo >&2 'ARM Based Posix system with J9DROP libraries')
However a standard echo works as well:
$(shell echo >&2 'ARM Based Posix system with J9DROP libraries')
Alternatively one can use info
:
$(info This is an information message.)
Change directory permanently
Reference: [2]
CHDIR_SHELL := $(SHELL)
$(info CHDIR_SHELL is $(CHDIR_SHELL))
define chdir
$(eval _D=$(firstword $(1) $(@D)))
$(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef
# Can change permanently directory during the 1st pass...
$(eval cd some_dir2; /bin/sh)
$(shell echo >&2 NOW PWD is $$PWD)
all:
# Or in a rule recipe...
$(call chdir,some_dir)
echo "I'm now always in some_dir"
echo gcc -Wall -o myTest myTest.c
echo $$PWD
some_dir/myTest:
$(call chdir)
echo "I'm now always in some_dir"
echo gcc -Wall -o myTest myTest.c
echo $$PWD
Using Function Variables
Function variables can either be defined by assigned or using define... endef
construct.
Assignments must be deferred (i.e. =
and not :-
), so that function is not evaluated at the time it is defined but only when used. Also if it contains comment symbol (#
), these must be escaped:
makefilecfg = $(shell echo "Creating configuration file '$R$(MAKEFILE_CFG)$Z'." >&2; \
mkdir -p $(BAD) >&2; \
echo "\# Common Platform custom configuration file." >$(MAKEFILE_CFG) ;\
echo "\# CP build system includes this file to know which components/variants to build." >>$(MAKEFILE_CFG); \
echo "\# See file 'tools/checkcfg.mk' for list of available components/variants." >>$(MAKEFILE_CFG); \
echo "CPU :=$(CPU)" >>$(MAKEFILE_CFG); \
echo "OSAL:=$(OSAL)" >>$(MAKEFILE_CFG); \
echo "JAVA:=$(JAVA)" >>$(MAKEFILE_CFG); \
echo "NFC :=$(NFC)" >>$(MAKEFILE_CFG); \
echo "" >&2; \
echo "You can now run '$Gmake$Z' to build the project with this configuration," >&2; \
echo "or first edit the configuration file to customize this build." >&2 )
all:
@(call $(makefilecfg))
The define... endef
is a bit simpler:
define makefilecfg
$(shell echo "Creating configuration file '$R$(MAKEFILE_CFG)$Z'." >&2;
mkdir -p $(BAD) >&2;
echo "# Common Platform custom configuration file." >$(MAKEFILE_CFG);
echo "# CP build system includes this file to know which components/variants to build." >>$(MAKEFILE_CFG);
echo "# See file 'tools/checkcfg.mk' for list of available components/variants." >>$(MAKEFILE_CFG);
echo "CPU :=$(CPU)" >>$(MAKEFILE_CFG);
echo "OSAL:=$(OSAL)" >>$(MAKEFILE_CFG);
echo "JAVA:=$(JAVA)" >>$(MAKEFILE_CFG);
echo "NFC :=$(NFC)" >>$(MAKEFILE_CFG) )
endef
all:
@(call $(makefilecfg))
Frequent Mistakes
- Use TABS to prefix commands in rules, *NOT* SPACES
- Each rule in a makefile gives the set of commands that must be executed to build a given target from a given set of dependencies. These commands MUST be indented with a TAB character, not with SPACES. So make sure that the editor does not automatically change these tabs into spaces.
- Don't call sub-project Makefile, include it
- Calling subproject makefiles, so-called recursive make, has a huge performance penalty however the build (see Recursive Make Considered Harmful)
- Don't add space around = (equal sign) in variable substitution
- Do
$(SOURCES:%.c=%.o)
, not$(SOURCES:%.c = %.o)
(wrong)