Makefile
basic
- Makefiles are used to help decide which parts of a large program need to be recompiled.
- In the vast majority of cases, C or C++ files are compiled.
- Other languages typically have their own tools that serve a similar purpose as Make.
- It can be used beyond programs too, when you need a series of instructions to run depending on what files have changed. This tutorial will focus on the C/C++ compilation use case.
Here’s an example dependency graph that you might build with Make. If any file’s dependencies changes, then the file will get recompiled:
alternatives to Make
Interpreted languages like Python, Ruby, and Javascript
don’t require an analogue to Makefiles.
- The goal of Makefiles is to compile whatever files need to be compiled, based on what files have changed.
- But when files in interpreted languages change, nothing needs to get recompiled. When the program runs, the most recent version of the file is used.
Makefile Syntax
A Makefile consists of a set of rules
- The targets
- are file names, separated by spaces.
- Typically, there is only one per rule.
- 一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)
- The prerequisites / dependencies
- are also file names, separated by spaces.
- These files need to exist before the commands for the target are run.
- 要生成那个target所需要的文件或是目标。
- The commands
- a series of steps typically used to make the target(s).
- These need to start with a tab character ,
- not spaces.
- make需要执行的命令。(任意的Shell命令)
1
2
3
4
5
6
7
| # A rule generally looks like this:
targets: prerequisites
command
command
command
clean:
command
|
这是一个文件的依赖关系
- target这一个或多个的目标文件依赖于prerequisites中的文件,
- 其生成规则定义在command中。
- prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。
- 这就是Makefile的规则。也就是Makefile中最核心的内容。
- make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,
- 对于所定义的命令的错误,或是编译不成功,make根本不理。
- make只管文件的依赖性
- 如果在找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
clean不是一个文件,它只不过是一个动作名字
- 其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。
- 要执行其后的命令,就要在make命令后明显得指出这个lable的名字。
- 这样的方法可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。
- 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,
- 要make执行。即命令——“make clean”
Run Examples
put the contents in a file called Makefile
, and in that directory run the command make
.
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
|
# 1.
# Makefile
hello:
echo "hello world"
$ make
# echo "hello world"
# hello world
# 2.
# It have 3 separate rules,
# - When you run `make blah` in the terminal, it will build a program called `blah` in a series of steps:
# - Make is given `blah` as the target, so it first searches for this target
# - `blah` requires `blah.o`, so make searches for the `blah.o` target
# - `blah.o` requires `blah.c`, so make searches for the `blah.c` target
# - `blah.c` has no dependencies, so the `echo` command is run
# - The `cc -c` command is then run, because all of the `blah.o` dependencies are finished
# - The top `cc` command is run, because all the `blah` dependencies are finished
# - That's it: `blah` is a compiled c program
blah: blah.o
cc blah.o -o blah # Runs third
blah.o: blah.c
cc -c blah.c -o blah.o # Runs second
blah.c:
echo "int main() {return 0;}" > blah.c # Runs first
# echo "int main() {return 0;}" > blah.c
# cc -c blah.c -o blah.o
# cc blah.o -o blah
# 3.
# This makefile has a single target, called `some_file`.
# The default target is the first target, so in this case `some_file` will run.
some_file:
echo "This line will always print"
# first time: This file will make `some_file`
# the second time notice run the made: `make: 'some_file' is up to date.`
# 4.
# Here, the target `some_file` "depends" on `other_file`.
# When we run `make`, the default target (`some_file`) will get called.
# It will first look at its list of dependencies, and if any of them are older, it will first run the targets for those dependencies, and then run itself.
# The second time this is run, neither target will run because both targets exist.
some_file: other_file
echo "This will run second, because it depends on other_file"
touch some_file
other_file:
echo "This will run first"
touch ot
# 5.
# `clean` is often used as a target that removes the output of other targets, but it is not a special word in `make`.
some_file:
touch some_file
clean:
rm -f some_file
|
Variables
Variables can only be strings. Here’s an example of using them:
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
| files = file1 file2
some_file: $(files)
echo "Look at this variable: " $(files)
touch some_file
file1:
touch file1
file2:
touch file2
clean:
rm -f file1 file2 some_file
# touch file1
# touch file2
# echo "Look at this variable: " file1 file2
# Look at this variable: file1 file2
# touch some_file
# Reference variables using ${} or $()
x = dude
all:
echo $(x)
echo ${x}
# Bad practice, but works
echo $x
|
Targets
all
target
Make a all
target.
- Making multiple targets and you want all of them to run
1
2
3
4
5
6
7
8
9
10
11
| all: one two three
one:
touch one
two:
touch two
three:
touch three
clean:
rm -f one two three
|
Multiple targets
Multiple targets
- When there are multiple targets for a rule
- the commands will be run for each target
$@
is an automatic variable that contains the target name.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| all: f1.o f2.o
f1.o f2.o:
echo $@
# Equivalent to:
# f1.o
# echo $@
# f2.o
# echo $@
# echo f1.o
# f1.o
# echo f2.o
# f2.o
|
Automatic Variables and Wildcards
Both *
and %
are called wildcards in Make, but they mean entirely different things.
*
Wildcard
*
search your filesystem for matching filenames.- suggest that always wrap it in the
wildcard
function *
may be used in the target, prerequisites, or in the wildcard
function.- Danger:
*
may not be directly used in a variable definitions- When
*
matches no files, it is left as it is (unless run in the wildcard
function)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # Print out file information about every .c file
print: $(wildcard *.c)
ls -la $?
# Don't do this! '*' will not get expanded
thing_wrong := *.o
thing_right := $(wildcard *.o)
all: one two three four
# Fails, because $(thing_wrong) is the string "*.o"
one: $(thing_wrong)
# Stays as *.o if there are no files that match this pattern :(
two: *.o
# Works as you would expect! In this case, it does nothing.
three: $(thing_right)
# Same as rule three
four: $(wildcard *.o)
|
%
Wildcard
- “matching” mode
- it matches one or more characters in a string.
- This match is called the stem.
- “replacing” mode,
- it takes the stem that was matched and replaces that in a string.
%
is most often used in rule definitions and in some specific functions.
See these sections on examples of it being used:
- Static Pattern Rules
- Pattern Rules
- String Substitution
- The vpath Directive
Automatic Variables
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
| hey: one two
# Outputs "hey", since this is the first target
echo $@
# Outputs all prerequisites newer than the target
echo $?
# Outputs all prerequisites
echo $^
touch hey
one:
touch one
two:
touch two
clean:
rm -f hey one two
# touch one
# touch two
# # Outputs "hey", since this is the first target
# echo hey
# hey
# # Outputs all prerequisites newer than the target
# echo one two
# one two
# # Outputs all prerequisites
# echo one two
# one two
# touch hey
|
Fancy Rules
Static Pattern Rules
1
2
3
4
5
6
| targets ...: target-pattern: prereq-patterns ...
commands
# the given target is matched by the target-pattern (via a `%` wildcard).
# Whatever was matched is called the stem
# The stem is then substituted into the prereq-pattern, to generate the target's prereqs.
|
A typical use case is to compile .c
files into .o
files.
- Here’s the manual way
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| objects = foo.o bar.o all.o
all: $(objects)
# These files compile via implicit rules
foo.o: foo.c
bar.o: bar.c
all.o: all.c
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $@
clean:
rm -f *.c *.o all
# echo "int main() { return 0; }" > all.c
# cc -c -o all.o all.c
# touch foo.c
# cc -c -o foo.o foo.c
# touch bar.c
# cc -c -o bar.o bar.c
# cc all.o foo.o bar.o -o all
|
- more efficient way , using a static pattern rule:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| objects = foo.o bar.o all.o
all: $(objects)
# These files compile via implicit rules
# Syntax - targets ...: target-pattern: prereq-patterns ...
# In the case of the first target, foo.o, the target-pattern matches foo.o and sets the "stem" to be "foo".
# It then replaces the '%' in prereq-patterns with that stem
$(objects): %.o: %.c
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $@
clean:
rm -f *.c *.o all
|
Static Pattern Rules and Filter
While I introduce functions later on, I’ll forshadow what you can do with them. The filter
function can be used in Static pattern rules to match the correct files.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| In this example, I made up the `.raw` and `.result` extensions.
obj_files = foo.result bar.o lose.o
src_files = foo.raw bar.c lose.c
all: $(obj_files)
$(filter %.o,$(obj_files)): %.o: %.c
echo "target: $@ prereq: $<"
$(filter %.result,$(obj_files)): %.result: %.raw
echo "target: $@ prereq: $<"
%.c %.raw:
touch $@
clean:
rm -f $(src_files)
|
Implicit Rules
Perhaps the most confusing part of make is the magic rules and variables that are made. Here’s a list of implicit rules:
- Compiling a C program:
n.o
is made automatically from n.c
with a command of the form $(CC) -c $(CPPFLAGS) $(CFLAGS)
- Compiling a C++ program:
n.o
is made automatically from n.cc
or n.cpp
with a command of the form $(CXX) -c $(CPPFLAGS) $(CXXFLAGS)
- Linking a single object file:
n
is made automatically from n.o
by running the command $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
As such, the important variables used by implicit rules are:
CC
: Program for compiling C programs; default ccCXX
: Program for compiling C++ programs; default G++CFLAGS
: Extra flags to give to the C compilerCXXFLAGS
: Extra flags to give to the C++ compilerCPPFLAGS
: Extra flags to give to the C preprosessorLDFLAGS
: Extra flags to give to compilers when they are supposed to invoke the linker
1
2
3
4
5
6
7
8
9
10
11
12
| CC = gcc # Flag for implicit rules
CFLAGS = -g # Flag for implicit rules. Turn on debug info
# Implicit rule #1: blah is built via the C linker implicit rule
# Implicit rule #2: blah.o is built via the C compilation implicit rule, because blah.c exists
blah: blah.o
blah.c:
echo "int main() { return 0; }" > blah.c
clean:
rm -f blah*
|
Pattern Rules
Pattern rules
- A way to define your own implicit rules
- A simpler form of static pattern rules
- Pattern rules contain a
%
in the target.%
matches any nonempty string, and the other characters match themselves.%
in a prerequisite of a pattern rule stands for the same stem that was matched by the %
in the target.
1
2
3
4
5
6
7
8
| # Define a pattern rule that compiles every .c file into a .o file
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
# Define a pattern rule that has no pattern in the prerequisites.
# This just creates empty .c files when needed.
%.c:
touch $@
|
Functions for String Substitution and Analysis
$(subst from,to,text)
- textual replacement on the text text: each occurrence of from is replaced by to.
- The result is substituted for the function call
1
2
| $(subst ee,EE,feet on the street)
# produces the value `fEEt on the strEEt`.
|
$(patsubst pattern,replacement,text)
1
2
| $(patsubst %.c,%.o,x.c.c bar.c)
# produces the value `x.c.o bar.o`.
|
- Substitution references
- simpler way to get the effect of the patsubst function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| $(var:pattern=replacement)
# is equivalent to
$(patsubst pattern,replacement,$(var))
The second shorthand simplifies one of the most common uses of patsubst: replacing the suffix at the end of file names.
$(var:suffix=replacement)
# is equivalent to
$(patsubst %suffix,%replacement,$(var))
objects = foo.o bar.o baz.o
# To get the list of corresponding source files, you could simply write:
$(objects:.o=.c)
# instead of using the general form:
$(patsubst %.o,%.c,$(objects))
|
Double-Colon Rules ::
- allow multiple rules to be defined for the same target.
- If these were single colons, a warning would be printed and only the second set of commands would run.
1
2
3
4
5
6
7
| all: blah
blah::
echo "hello"
blah::
echo "hello again"
|
Double pipeline || true
- It’s the opposite of
&&
- the second command is executed only if the exit status of the preceding command is 0.
1
2
3
4
5
6
7
| $ ls this_file_does_not_exist.txt || echo KO
# ls: cannot access this_file_does_not_exist.txt: No such file or directory
# KO
$ ls this_file_exist.txt && echo OK
# this_file_exist.txt
# OK
|
Commands and execution
Command Echoing/Silencing
Add an @
before a command to stop it from being printed You can also run make with -s
to add an @
before each line
1
2
3
| all:
@echo "This make line will not be printed"
echo "But this will"
|
Command Execution
- Each command is run in a new shell (or at least the effect is as such)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| all:
cd ..
# The cd above does not affect this line, because each command is effectively run in a new shell
echo `pwd`
# This cd command affects the next because they are on the same line
cd ..; echo `pwd`
# Same as above
cd ..; \
echo `pwd`
# cd ..
# # The cd above does not affect this line
# echo `pwd`
# /Users/luo/Documents/code/maketest
# # This cd command affects the next because they are on the same line
# cd ..; echo `pwd`
# /Users/luo/Documents/code
# # Same as above
# cd ..; \
# echo `pwd`
# /Users/luo/Documents/code
|
Default Shell
- The default shell is
/bin/sh
. - change this by changing the variable SHELL:
1
2
3
4
| SHELL=/bin/bash
cool:
echo "Hello from bash"
|
Error handling with -k
, -i
, and -
- Add
-k
- when running make to continue running even in the face of errors.
- Helpful if you want to see all the errors of Make at once.
- Add a
-
- before a command to suppress the error
- Add
-i
- to make to have this happen for every command.
1
2
3
4
| one:
# This error will be printed but ignored, and make will continue to run
-false
touch one
|
Interrupting or killing make
Note only: If you ctrl+c
make, it will delete the newer targets it just made.
Recursive make
To recursively call a makefile
- use the special
$(MAKE)
instead of make
- because it will pass the make flags for you and won’t itself be affected by them.
1
2
3
4
5
6
7
| new_contents = "hello:\n\ttouch inside_file"
all:
mkdir -p subdir
printf $(new_contents) | sed -e 's/^ //' > subdir/makefile
cd subdir && $(MAKE)
clean:
rm -rf subdir
|
- Use export for recursive make
- the export directive takes a variable and makes it accessible to sub-make commands.
- In this example,
cooly
is exported such that the makefile in subdir can use it. - Note: export has the same syntax as sh, but they aren’t related (although similar in function)
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
| new_contents = "hello:\n\\techo \$$(cooly)"
all:
mkdir -p subdir
echo $(new_contents) | sed -e 's/^ //' > subdir/makefile
@echo "---MAKEFILE CONTENTS---"
@cd subdir && cat makefile
@echo "---END MAKEFILE CONTENTS---"
cd subdir && $(MAKE)
@echo "---NewLine---"
# Note that variables and exports. They are set/affected globally.
cooly = "The subdirectory can see me!"
export cooly
# This would nullify the line above: unexport cooly
clean:
rm -rf subdir
# mkdir -p subdir
# echo "hello:\n\\techo \$(cooly)" | sed -e 's/^ //' > subdir/makefile
# ---MAKEFILE CONTENTS---
# hello:
# echo $(cooly)
# ---END MAKEFILE CONTENTS---
# cd subdir && /Applications/Xcode.app/Contents/Developer/usr/bin/make
# echo "The subdirectory can see me!"
# The subdirectory can see me!
# ---NewLine---
# need to export variables to have them run in the shell as well.
one=this will only work locally
export two=we can run subcommands with this
all:
@echo $(one)
@echo $$one
@echo $(two)
@echo $$two
# this will only work locally
#
# we can run subcommands with this
# we can run subcommands with this
|
.EXPORT_ALL_VARIABLES
exports all variables for you.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| .EXPORT_ALL_VARIABLES:
new_contents = "hello:\n\techo \$$(cooly)"
cooly = "The subdirectory can see me!"
# This would nullify the line above: unexport cooly
all:
mkdir -p subdir
echo $(new_contents) | sed -e 's/^ //' > subdir/makefile
@echo "---MAKEFILE CONTENTS---"
@cd subdir && cat makefile
@echo "---END MAKEFILE CONTENTS---"
cd subdir && $(MAKE)
clean:
rm -rf subdir
# mkdir -p subdir
# echo "hello:\n\techo \$(cooly)" | sed -e 's/^ //' > subdir/makefile
# ---MAKEFILE CONTENTS---
# hello:
# echo $(cooly)
# ---END MAKEFILE CONTENTS---
# cd subdir && /Applications/Xcode.app/Contents/Developer/usr/bin/make
# echo "The subdirectory can see me!"
# The subdirectory can see me!
|
Arguments to make
- a nice list of options that can be run from make. Check out
--dry-run
, --touch
, --old-file
. - You can have multiple targets to make, i.e.
make clean run test
runs the clean
goal, then run
, and then test
.
Variables Pt. 2
Flavors of variables
- recursive
=
- only looks for the variables when the command is used
- not when it’s defined
- simply expanded
:=
- like normal imperative programming
- only those defined so far get expanded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Recursive variable. This will print "later" below
one = one ${later_variable}
later_variable = aloha
# Simply expanded variable. This will not print "later" below
two := two ${later_variable}
later_variable = later
all:
echo $(one)
echo $(two)
# echo one later
# one later
# echo two
# two
# echo one later
# one later
# echo two aloha
# two aloha
|
Simply expanded (using :=
) allows you to append to a variable. Recursive definitions will give an infinite loop error.
1
2
3
4
5
6
7
| one = hello
# one gets defined as a simply expanded variable (:=) and thus can handle appending
one := ${one} there
all:
echo $(one)
# echo hello there
# hello there
|
?=
- only sets variables if they have not yet been set
1
2
3
4
5
6
7
| one = hello
one ?= will not be set
two ?= will be set
all:
echo $(one)
echo $(two)
|
space
- Spaces at the end of a line are not stripped, but those at the start are.
- To make a variable with a single space, use
$(nullstring)
1
2
3
4
5
6
7
8
9
10
11
| with_spaces = hello # with_spaces has many spaces after "hello"
after = $(with_spaces)there
nullstring =
space = $(nullstring) # Make a variable with a single space.
all:
echo "$(after)"
echo start"$(space)"end
# echo "hello there"
# hello there
# echo start" "end
# start end
|
An undefined variable is actually an empty string!
1
2
3
| all:
# Undefined variables are just empty strings!
echo $(nowhere)
|
Use +=
to append
1
2
3
4
5
| foo := start
foo += more
all:
echo $(foo)
|
Command line arguments and override
- override variables that come from the command line by using
override
. - Here we ran make with
make option_one=hi
1
2
3
4
5
6
7
8
9
10
11
| # Overrides command line arguments
override option_one = did_override
# Does not override command line arguments
option_one = not_override
all:
echo $(option_one)
echo $(option_one)
# echo did_override
# did_override
# echo did_override
# did_override
|
List of commands and define
- “define” is actually just a list of commands.
- It has nothing to do with being a function.
- Note here that it’s a bit different than having a semi-colon between commands, because each is run in a separate shell, as expected.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| one = export blah="I was set!"; echo $$blah
define two
export blah=set
echo $$blah
endef
# One and two are different.
all:
@echo "This prints 'I was set'"
@$(one)
@echo "This does not print 'I was set' because each command runs in a separate shell"
@$(two)
# This prints 'I was set'
# I was set!
# This does not print 'I was set' because each command runs in a separate shell
#
|
Target-specific variables
Variables can be assigned for specific targets
1
2
3
4
5
| all: one = cool
all:
echo one is defined: $(one)
other:
echo one is nothing: $(one)
|
Pattern-specific variables
You can assign variables for specific target patterns
1
2
3
4
5
| %.c: one = cool
blah.c:
echo one is defined: $(one)
other:
echo one is nothing: $(one)
|
Conditional part of Makefiles
ifeq endif
Conditional if/else
1
2
3
4
5
6
7
8
| foo = ok
all:
ifeq ($(foo), ok)
echo "foo equals ok"
else
echo "nope"
endif
|
$(nullstring)
Check if a variable is empty
1
2
3
4
5
6
7
8
9
10
11
| nullstring =
foo = $(nullstring) # end of line; there is a space here
all:
ifeq ($(strip $(foo)),)
echo "foo is empty after being stripped"
endif
ifeq ($(nullstring),)
echo "nullstring doesn't even have spaces"
endif
|
ifdef endif
Check if a variable is defined
ifdef
does not expand variable references;- it just sees if something is defined at all
1
2
3
4
5
6
7
8
9
10
| bar =
foo = $(bar)
all:
ifdef foo
echo "foo is defined"
endif
ifdef bar
echo "but bar is not"
endif
|
$(makeflags)
- test make flags with
findstring
and MAKEFLAGS
. - Run this example with
make -i
to see it print out the echo statement.
1
2
3
4
5
6
7
8
9
10
11
| bar =
foo = $(bar)
all:
# Search for the "-i" flag.
# MAKEFLAGS is just a list of single characters, one per flag. So look for "i" in this case.
ifneq (,$(findstring i, $(MAKEFLAGS)))
echo "i was passed to MAKEFLAGS"
endif
# echo "i was passed to MAKEFLAGS"
# i was passed to MAKEFLAGS
|
Functions
First Functions
Functions are mainly just for text processing.
- Call functions with
$(fn, arguments)
or ${fn, arguments}
. - make your own using the call builtin function.
- Make has a decent amount of builtin functions.
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
| bar := ${subst not, totally, "I am not superman"}
all:
@echo $(bar)
# I am totally superman
# If you want to replace spaces or commas, use variables
comma := ,
empty:=
space := $(empty) $(empty)
foo := a b c
bar := $(subst $(space),$(comma),$(foo))
all:
@echo $(bar)
# Do NOT include spaces in the arguments after the first.
# That will be seen as part of the string.
comma := ,
empty:=
space := $(empty) $(empty)
foo := a b c
bar := $(subst $(space), $(comma) , $(foo))
all:
# Output is ", a , b , c". Notice the spaces introduced
@echo $(bar)
|
$(patsubst pattern,replacement, text)
- The substitution reference
$(text:pattern=replacement)
- replaces only suffixes:
$(text:suffix=replacement)
.- No
%
wildcard is used here. - Note: don’t add extra spaces for this shorthand. It will be seen as a search or replacement term.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| foo := a.o b.o l.a c.o
# change %.o to %.c
one := $(patsubst %.o,%.c,$(foo))
# This is a shorthand for the above
# change %.o to %.c
two := $(foo:%.o=%.c)
# This is the suffix-only shorthand, is also equivalent to the above.
three := $(foo:.o=.c)
all:
echo $(one)
echo $(two)
echo $(three)
# echo a.c b.c l.a c.c
# a.c b.c l.a c.c
# echo a.c b.c l.a c.c
# a.c b.c l.a c.c
# echo a.c b.c l.a c.c
# a.c b.c l.a c.c
|
$(foreach var,list,text)
- It converts one list of words (separated by spaces) to another.
var
is set to each word in list, and text
is expanded for each word.- This appends an exclamation after each word:
1
2
3
4
5
6
| foo := who are you
# For each "word" in foo, output that same word with an exclamation after
bar := $(foreach wrd, $(foo), $(wrd)!)
all:
# Output is "who! are! you!"
@echo $(bar)
|
$(if nonempty, action1 , action2)
if
checks if the first argument is nonempty.- If so runs the second argument, otherwise runs the third.
1
2
3
4
5
6
7
8
| foo := $(if this-is-not-empty,then!,else!)
empty :=
bar := $(if $(empty),then!,else!)
all:
@echo $(foo)
@echo $(bar)
# then!
# else!
|
The call function
- Make supports creating basic functions.
- “define” the function just by creating a variable, but use the parameters
$(0)
, $(1)
, etc. - then call the function with the special
call
function. - The syntax is
$(call variable,param,param)
. $(0)
is the variable, while $(1)
, $(2)
, etc. are the params.
1
2
3
4
| sweet_new_fn = Variable Name: $(0) First: $(1) Second: $(2) Empty Variable: $(3)
all:
@echo $(call sweet_new_fn, go, tigers)
# Outputs "Variable Name: sweet_new_fn First: go Second: tigers Empty Variable:"
|
$(shell ls -la)
- calls the shell, but it replaces newlines with spaces!
1
2
3
| all:
@echo $(shell ls -la)
# Very ugly because the newlines are gone!
|
Other Features
include Makefiles
- tells
make
to read one or more other makefiles
. - particularly useful when use compiler flags like
-M
that create Makefiles based on the source. - make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的位置。就好像C/C++的#include指令一样。
Example,
- if some c files includes a header, that header will be added to a Makefile that’s written by gcc.
vpath <pattern> <directories, space/colon separated>
- Use vpath to specify where some set of prerequisites exist.
- The format is
vpath <pattern> <directories, space/colon separated>
<pattern>
can have a %
, which matches any zero or more characters.- can also do this globallyish with the variable VPATH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| vpath %.h ../headers ../other-directory
some_binary: ../headers blah.h
touch some_binary
../headers:
mkdir ../headers
blah.h:
touch ../headers/blah.h
clean:
rm -rf ../headers
rm -f some_binary
|
Multiline \
- use multiple lines when the commands are too long
1
2
3
| some_file:
echo This line is too long, so \
it is broken up into multiple lines
|
.phony
- Adding
.PHONY
to a target will prevent make from confusing the phony target with a file name. - In this example, if the file
clean
is created, make clean
will still be run. .PHONY
is great to use
1
2
3
4
5
6
7
8
| some_file:
touch some_file
touch clean
.PHONY: clean
clean:
rm -f some_file
rm -f clean
|
.delete_on_error
- The make tool will stop running a rule (and will propagate back to prerequisites) if a command returns a nonzero exit status.
DELETE_ON_ERROR
will delete the target of a rule if the rule fails in this manner.- This will happen for all targets, not just the one it is before like PHONY.
- It’s a good idea to always use this, even though make does not for historical reasons.
1
2
3
4
5
6
7
8
9
10
| .DELETE_ON_ERROR:
all: one two
one:
touch one
false
two:
touch two
false
|
Makefile Cookbook
Job Vranish (https://spin.atomicobject.com/2016/08/26/makefile-c-projects/)
- put your C/C++ files in the
src/
folder.
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
| TARGET_EXEC := final_program
BUILD_DIR := ./build
SRC_DIRS := ./src
# Find all the C and C++ files we want to compile
SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c)
# String substitution for every C/C++ file.
# As an example, hello.cpp turns into ./build/hello.cpp.o
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
# String substitution (suffix version without %).
# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d
DEPS := $(OBJS:.o=.d)
# Every folder in ./src will need to be passed to GCC so that it can find header files
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# The -MMD and -MP flags together generate Makefiles for us!
# These files will have .d instead of .o as the output.
CPPFLAGS := $(INC_FLAGS) -MMD -MP
# The final build step.
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CC) $(OBJS) -o $@ $(LDFLAGS)
# Build step for C source
$(BUILD_DIR)/%.c.o: %.c
mkdir -p $(dir $@)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
# Build step for C++ source
$(BUILD_DIR)/%.cpp.o: %.cpp
mkdir -p $(dir $@)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -r $(BUILD_DIR)
# Include the .d makefiles. The - at the front suppresses the errors of missing
# Makefiles. Initially, all the .d files will be missing, and we don't want those
# errors to show up.
-include $(DEPS)
|
.
Comments powered by Disqus.