Rule Structure

A rule looks like

1
2
3
4
targets: prerequisites
command # must indent with tabs!
command
command

只能一个一个目标地编译(必须串行运行整块指令),优先运行被依赖的文件的编译命令

1
2
3
4
5
6
7
some_file: other_file
echo "This will run second"
touch some_file

other_file:
echo "This will run first"
touch other_file

clean is often used as a target that removes the output of other targets, but it is not a special word in make.

Variables

在makefile自己的定义域中定义变量,使用 $() 或者 ${} 调用变量。在sh中,$(func) 是执行命令的意思,在sh段使用sh域的变量时要用 $${X} 或者 $$X,在makefile段将变量交给sh,需要使用 export 关键字

1
2
3
4
5
6
7
8
9
10
11
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

Targets

The all target

Making multiple targets and you want all of them to run? Make an all target. 事实上命令行中执行make只会生成文件中第一个出现的目标

Automatic Variables

1
2
3
4
5
6
7
8
9
hey: one two
# Outputs "hey", since this is the first target
echo $@
# Outputs all prerequisites newer than the target
echo $?
# Outputs all prerequisites
echo $^
# Outputs the first prerequisite
echo $<

Wildcard

* Wildcard

* searches your filesystem for matching filenames.

  • Danger: * may not be directly used in a variable definitions
  • Danger: When * matches no files, it is left as it is (unless run in the wildcard function)
1
2
thing_wrong := *.o # Don't do this! '*' will not get expanded
thing_right := $(wildcard *.o)

Fancy Rules

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)

automatically: 只要写出target与prerequisite,不用写编译语句
The important variables used by implicit rules are:

  • CC: Program for compiling C programs; default cc
  • CXX: Program for compiling C++ programs; default g++
  • CFLAGS: Extra flags to give to the C compiler
  • CXXFLAGS: Extra flags to give to the C++ compiler
  • CPPFLAGS: Extra flags to give to the C preprocessor
  • LDFLAGS: Extra flags to give to compilers when they are supposed to invoke the linker

Static Pattern Rules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
# foo.o: foo.c
# bar.o: bar.c
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $@
clean:
rm -f *.c *.o all

将多个 n.o: n.c 的代码压在一行的语法。
The essence is that 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.

Static Pattern Rules and Filter

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
obj_files = foo.result bar.o lose.o
src_files = foo.raw bar.c lose.c

.PHONY: all
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)

Pattern Rules

Pattern rules are often used but quite confusing. You can look at them as two ways:

  • A way to define your own implicit rules
  • A simpler form of static pattern rules
1
2
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

Commands and execution

Command Echoing/Silencing

@起到silent的效果

1
2
3
all: 
@echo "This make line will not be printed"
echo "But this will"

Command Execution

每行命令都在新的命令行中执行;同一行内,可以用分号执行多条命令

Default Shell

默认shell是 /bin/sh,可以通过 SHELL 来修改

1
SHELL=/bin/bash

(unread)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

Recursive use of make

要递归调用makefile,需要使用 $(MAKE),这使得makefile确认新调用的命令也是make,因此会把它的MAKEFLAGS传过去

1
2
3
cd subdir && $(MAKE)
+ make -C subdir
$(MAKE) -C subdir

Use export for recursive make

Variables Pt.2

Flavors and modification

  • recursive (use =) 使用时展开,递归调用,可能导致死循环
  • simply expanded (use :=) 定义时直接展开,固定为字符串,不会死循环
  • ?= only sets variables if they have not yet been set
  • Use += to append, 注意会自动加空格分隔开

An undefined variable is actually an empty string!

Command line arguments and override

可以通过命令行定义变量,makefile可以override它们
You can override variables that come from the command line by using override. Here we ran make with make option_one=hi and make option_two=hi

1
2
3
4
5
6
7
# Overrides command line arguments
override option_one = did_override
# Does not override command line arguments
option_two = not_override
all:
echo $(option_one)
echo $(option_two)

You 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

1
2
3
4
5
all: 
@echo $(one)
@echo $$one
@echo $(two)
@echo $$two

.EXPORT_ALL_VARIABLES exports all variables for you. 写在第一行即可

Target-specific variables

在目标 all 的编译命令域内定义makefile的变量

1
all: one = cool

Pattern-specific variables

You can assign variables for specific target patterns

1
%.c: one = cool

注意要写在一行内

Conditional part of Makefiles

Conditional if/else

注意不需要indent

1
2
3
4
5
6
7
foo = ok
all:
ifeq ($(foo), ok)
echo "foo equals ok"
else
echo "nope"
endif

MAKEFLAGS is just a list of single characters, one per flag.

Functions

Call functions with $(fn, arguments) or ${fn, arguments}.

Text Functions

  • $(subst from,to,text)
    $(subst ee,EE,feet on the street)
  • $(patsubst pattern,replacement,text)
    Finds whitespace-separated words in text that match pattern and replaces them with replacement. Here pattern may contain a ‘%’ which acts as a wildcard, matching any number of any characters within a word. If replacement also contains a ‘%’, the ‘%’ is replaced by the text that matched the ‘%’ in pattern. 仅有第一个%按照这种方式处理
    $(patsubst pat,rep,$text)=(text:pat=rep)
  • $(strip string)
    删除开头结尾的空格
  • $(findstring str,in)
    $(findstring a,a b c)->’a’
    $(findstring a,b c)->’’
  • $(filter pattern1 pattern2...,text)
    pattern之间是或的关系,只要text中的某个word与其中一个匹配即可保留
    1
    2
    3
    sources := foo.c bar.c baz.s ugh.h
    foo: $(sources)
    cc $(filter %.c %.s,$(sources)) -o foo
  • $(filter-out pattern1 pattern2...,text)
    返回前述函数的补集
  • $(sort text)
    将text视为用空格分隔的单词列表,进行排序
  • (word n,text)
    n为整型,返回text的第n个单词,从1开始标号
  • $(wordlist s,e,text)
    s,e为整型,返回text的第s到第e个单词,从1开始标号

要在函数中使用特殊字符,需要使用变量来指代,如

1
2
3
comma := ,
empty:=
space := $(empty) $(empty)

谨记以上函数,逗号之后不可带空格,否则被视为串的一部分

$(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
7
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)

The if function

检查第一个元素是否非空,若非空运行第二个,否则运行第三个

The call function

Make supports creating basic functions. You “define” the function just by creating a variable, but use the parameters $(0), $(1), etc. You 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
5
sweet_new_fn = Variable Name: $(0) First: $(1) Second: $(2) Empty Variable: $(3)

all:
# Outputs "Variable Name: sweet_new_fn First: go Second: tigers Empty Variable:"
@echo $(call sweet_new_fn, go, tigers)

The shell function

shell - This 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

The include directive tells make to read one or more other makefiles. It’s a line in the makefile makefile that looks like this:

1
include filenames...

The vpath Directive

使用vpath来指明一些前提条件的所在,格式是 vpath <pattern> <directories, space/colon separated><pattern> 可以含有 %, 可以通过 VPATH 这一变量进行全局修改

.PHONY

.PHONY: clean all

  • 意味着 clean 在makefile中不代表文件名
  • makefile 与当前路径下的文件 clean 无关

常用方案

1
2
3
4
5
6
7
8
9
10
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
CPPFLAGS := $(INC_FLAGS) -MMD -MP
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CC) $(OBJS) -o $@ $(LDFLAGS)
$%.c.o: %.c # generates .d at the same time
mkdir -p $(dir $@)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@