Makefile 速查表 Cheat Sheet

基本结构

target: prerequisites
    command

变量赋值

foo  = "bar"
bar  = $(foo) foo  # 动态(更新)赋值
foo := "boo"       # 一次性赋值,现在 $(bar) 是 "boo foo"
foo ?= /usr/local  # 安全赋值,$(foo) 和 $(bar) 依然保持不变
bar += world       # 追加,"boo foo world"
foo != echo fooo   # 执行 shell 命令并赋值给 foo
# $(bar) 现在是 "fooo foo world"

命令前缀

前缀说明
-忽略错误
@不打印命令
+即使 Make 处于“不执行”模式,也运行该命令
build:
   @echo "compiling"
   -gcc $< $@

-include .depend

查找文件

js_files  := $(wildcard test/*.js)
all_files := $(shell find images -name "*")

替换

file     = $(SOURCE:.cpp=.o)   # foo.cpp => foo.o
outputs  = $(files:src/%.coffee=lib/%.js)

outputs  = $(patsubst %.c, %.o, $(wildcard *.c))
assets   = $(patsubst images/%, assets/%, $(wildcard images/*))

包含文件

-include foo.make

单美元符号和双美元符号

美元符号描述
$使用 $ 引用 Make 变量
$$使用 $$ 引用 shell 变量

Makefile

LIST = one two three

.PHONY: all single_dollar double_dollar

all: single_dollar double_dollar

double_dollar:
        @echo "=== 双美元符号例子 ==="
        @for i in $(LIST); do \
                echo $$i;     \
        done

single_dollar:
        @echo "=== 单美元符号例子 ==="
        @for i in $(LIST); do  \
                echo $i;     \
        done

输出

$ make
=== 单美元符号例子 ===



=== 双美元符号例子 ===
one
two
three

自动变量

自动变量描述
$@目标文件的文件名
$<第一个依赖文件的名字
$^所有依赖文件的名字
$+所有依赖文件的名字(包括重复的,按顺序)

Makefile

.PHONY: all

all: hello world

hello world: foo foo foo bar bar
        @echo "== 目标: $@ =="
        @echo $<
        @echo $^
        @echo $+

foo:
        @echo "Hello foo"

bar:
        @echo "Hello Bar"

输出

Hello foo
Hello Bar
== 目标: hello ==
foo
foo bar
foo foo foo bar bar
== 目标: world ==
foo
foo bar
foo foo foo bar bar

使用 $(warning text) 检查 Make 规则(用于调试)

$(warning 顶层警告)

FOO := $(warning FOO 变量)foo
BAR  = $(warning BAR 变量)bar

$(warning 目标)target: $(warning 依赖文件列表)Makefile $(BAR)
        $(warning 目标脚本)
        @ls
$(BAR):

输出

Makefile:1: 顶层警告
Makefile:3: FOO 变量
Makefile:6: 目标
Makefile:6: 依赖文件列表
Makefile:6: BAR 变量
Makefile:9: BAR 变量
Makefile:7: 目标脚本
Makefile

分别编译可执行文件

目录结构

.
|-- Makefile
|-- bar.c
|-- bar.h
|-- foo.c
`-- foo.h

Makefile

# CFLAGS: 传递给 C 编译器的额外标志
CFLAGS   += -Werror -Wall -O2 -g
SRC       = $(wildcard *.c)
OBJ       = $(SRC:.c=.o)
EXE       = $(subst .c,,$(SRC))

.PHONY: all clean

all: $(OBJ) $(EXE)

clean:
    rm -rf *.o *.so *.a *.la $(EXE)

输出

$ make
cc -Werror -Wall -O2 -g   -c -o foo.o foo.c
cc -Werror -Wall -O2 -g   -c -o bar.o bar.c
cc   foo.o   -o foo
cc   bar.o   -o bar

使用 $(eval) 预定义变量

不使用 $(eval)

SRC = $(wildcard *.c)
EXE = $(subst .c,,$(SRC))

define PROGRAM_template
$1_SHARED = lib$(strip $1).so
endef

.PHONY: all

$(foreach exe, $(EXE), $(call PROGRAM_template, $(exe)))

all:
        @echo $(foo_SHARED)
        @echo $(bar_SHARED)

输出

$ make
Makefile:11: *** missing separator.  Stop.

使用 $(eval)

CFLAGS  += -Wall -g -O2 -I./include
SRC = $(wildcard *.c)
EXE = $(subst .c,,$(SRC))

define PROGRAM_template
$1_SHARED = lib$(strip $1).so
endef

.PHONY: all

$(foreach exe, $(EXE), $(eval $(call PROGRAM_template, $(exe))))

all:
        @echo $(foo_SHARED)
        @echo $(bar_SHARED)

输出

$ make
libfoo.so
libbar.so

编译子目录并链接

目录结构

.
|-- Makefile
|-- include
|   `-- foo.h
`-- src
    |-- foo.c
    `-- main.c

Makefile

CFLAGS  += -Wall -g -O2 -I./include
SRC     = $(wildcard src/*.c)
OBJ     = $(SRC:.c=.o)
EXE     = main

.PHONY: all clean

all: $(OBJ) $(EXE)

$(EXE): $(OBJ)
        $(CC) $(LDFLAGS) -o $@ $^

%.o: %.c
        $(CC) $(CFLAGS) -c $< -o $@

clean:
        rm -rf *.o *.so *.a *.la $(EXE) src/*.o src/*.so src/*a

输出

$ make
cc -Wall -g -O2 -I./include -c src/foo.c -o src/foo.o
cc -Wall -g -O2 -I./include -c src/main.c -o src/main.o
cc  -o main src/foo.o src/main.o

编译共享库

目录结构

.
|-- Makefile
|-- include
|   `-- common.h
`-- src
    |-- bar.c
    `-- foo.c

Makefile

SONAME    = libfoobar.so.1
SHARED    = src/libfoobar.so.1.0.0
SRC       = $(wildcard src/*.c)
OBJ       = $(SRC:.c=.o)

CFLAGS    += -Wall -Werror -fPIC -O2 -g -I./include
LDFLAGS   += -shared -Wl,-soname,$(SONAME)

.PHONY: all clean

all: $(SHARED) $(OBJ)

$(SHARED): $(OBJ)
        $(CC) $(LDFLAGS) -o $@ $^

%.o: %.c
        $(CC) $(CFLAGS) -c $^ -o $@

clean:
        rm -rf src/*.o src/*.so.* src/*.a src/*.la

输出

$ make
cc -Wall -Werror -fPIC -O2 -g -I./include -c src/foo.c -o src/foo.o
cc -Wall -Werror -fPIC -O2 -g -I./include -c src/bar.c -o src/bar.o
cc -shared -Wl,-soname,libfoobar.so.1 -o src/libfoobar.so.1.0.0 src/foo.o src/bar.o

编译共享库和静态库

目录结构

.
|-- Makefile
|-- include
|   |-- bar.h
|   `-- foo.h
`-- src
    |-- Makefile
    |-- bar.c
    `-- foo.c

Makefile

SUBDIR = src

.PHONY: all clean $(SUBDIR)

all: $(SUBDIR)

clean: $(SUBDIR)

$(SUBDIR):
        make -C $@ $(MAKECMDGOALS)

src/Makefile

SRC      = $(wildcard *.c)
OBJ      = $(SRC:.c=.o)
LIB      = libfoobar

STATIC   = $(LIB).a
SHARED   = $(LIB).so.1.0.0
SONAME   = $(LIB).so.1
SOFILE   = $(LIB).so

CFLAGS  += -Wall -Werror -g -O2 -fPIC -I../include
LDFLAGS += -shared -Wl,-soname,$(SONAME)

.PHONY: all clean

all: $(STATIC) $(SHARED) $(SONAME) $(SOFILE)

$(SOFILE): $(SHARED)
        ln -sf $(SHARED) $(SOFILE)

$(SONAME): $(SHARED)
        ln -sf $(SHARED) $(SONAME)

$(SHARED): $(STATIC)
        $(CC) $(LDFLAGS) -o $@ $<

$(STATIC): $(OBJ)
        $(AR) $(ARFLAGS) $@ $^

%.o: %.c
        $(CC) $(CFLAGS) -c -o $@ $<

clean:
        rm -rf *.o *.a *.so *.so.*

输出

$ make
make -C src
make[1]: 进入目录 '/root/test/src'
cc -Wall -Werror -g -O2 -fPIC -I../include -c -o foo.o foo.c
cc -Wall -Werror -g -O2 -fPIC -I../include -c -o bar.o bar.c
ar rv libfoobar.a foo.o bar.o
ar: 创建 libfoobar.a
a - foo.o
a - bar.o
cc -shared -Wl,-soname,libfoobar.so.1 -o libfoobar.so.1.0.0 libfoobar.a
ln -sf libfoobar.so.1.0.0 libfoobar.so.1
ln -sf libfoobar.so.1.0.0 libfoobar.so
make[1]: 离开目录 '/root/test/src'

递归构建

目录结构

.
|-- Makefile
|-- include
|   `-- common.h
|-- src
|   |-- Makefile
|   |-- bar.c
|   `-- foo.c
`-- test
    |-- Makefile
    `-- test.c

Makefile

SUBDIR = src test

.PHONY: all clean $(SUBDIR)

all: $(SUBDIR)

clean: $(SUBDIR)

$(SUBDIR):
        $(MAKE) -C $@ $(MAKECMDGOALS)

src/Makefile

SONAME   = libfoobar.so.1
SHARED   = libfoobar.so.1.0.0
SOFILE   = libfoobar.so

CFLAGS  += -Wall -g -O2 -Werror -fPIC -I../include
LDFLAGS += -shared -Wl,-soname,$(SONAME)

SRC      = $(wildcard *.c)
OBJ      = $(SRC:.c=.o)

.PHONY: all clean

all: $(SHARED) $(OBJ)

$(SHARED): $(OBJ)
        $(CC) $(LDFLAGS) -o $@ $^
        ln -sf $(SHARED) $(SONAME)
        ln -sf $(SHARED) $(SOFILE)

%.o: %.c
        $(CC) $(CFLAGS) -c $< -o $@

clean:
        rm -rf *.o *.so.* *.a *.so

test/Makefile

CFLAGS    += -Wall -Werror -g -I../include
LDFLAGS   += -Wall -L../src -lfoobar

SRC        = $(wildcard *.c)
OBJ        = $(SRC:.c=.o)
EXE        = test_main

.PHONY: all clean

all: $(OBJ) $(EXE)

$(EXE): $(OBJ)
        $(CC) -o $@ $^ $(LDFLAGS)

%.o: %.c
        $(CC) $(CFLAGS) -c $< -o $@

clean:
        rm -rf *.so *.o *.a $(EXE)

输出

$ make
make -C src
make[

1]: 进入目录 '/root/proj/src'
cc -Wall -g -O2 -Werror -fPIC -I../include -c foo.c -o foo.o
cc -Wall -g -O2 -Werror -fPIC -I../include -c bar.c -o bar.o
cc -shared -Wl,-soname,libfoobar.so.1 -o libfoobar.so.1.0.0 foo.o bar.o
ln -sf libfoobar.so.1.0.0 libfoobar.so.1
ln -sf libfoobar.so.1.0.0 libfoobar.so
make[1]: 离开目录 '/root/proj/src'
make -C test
make[1]: 进入目录 '/root/proj/test'
cc -Wall -Werror -g -I../include -c test.c -o test.o
cc -o test_main test.o -Wall -L../src -lfoobar
make[1]: 离开目录 '/root/proj/test'
$ tree .
.
|-- Makefile
|-- include
|   `-- common.h
|-- src
|   |-- Makefile
|   |-- bar.c
|   |-- bar.o
|   |-- foo.c
|   |-- foo.o
|   |-- libfoobar.so -> libfoobar.so.1.0.0
|   |-- libfoobar.so.1 -> libfoobar.so.1.0.0
|   `-- libfoobar.so.1.0.0
`-- test
    |-- Makefile
    |-- test.c
    |-- test.o
    `-- test_main

3 个目录, 14 个文件

替换当前 Shell

OLD_SHELL := $(SHELL)
SHELL = /usr/bin/python

.PHONY: all

all:
        @import os; print os.uname()[0]

输出

$ make
Linux

一行条件语句

语法:$(if 条件, 然后部分, 否则部分)

Makefile

VAR =
IS_EMPTY = $(if $(VAR), $(info not empty), $(info empty))

.PHONY: all

all:
        @echo $(IS_EMPTY)

输出

$ make
empty

$ make VAR=true
not empty

使用 define 控制 CFLAGS

Makefile

CFLAGS += -Wall -Werror -g -O2
SRC     = $(wildcard *.c)
OBJ     = $(SRC:.c=.o)
EXE     = $(subst .c,,$(SRC))

ifdef DEBUG
CFLAGS += -DDEBUG
endif

.PHONY: all clean

all: $(OBJ) $(EXE)

clean:
        rm -rf $(OBJ) $(EXE)

输出

$ make
cc -Wall -Werror -g -O2   -c -o foo.o foo.c
cc   foo.o   -o foo
$ make DEBUG=1
cc -Wall -Werror -g -O2 -DDEBUG   -c -o foo.o foo.c
cc   foo.o   -o foo

内置函数

函数描述
$(subst from,to,text)text 中用 to 替换 from
$(patsubst pattern,replacement,text)text 中用 replacement 替换匹配 pattern 的单词。
$(strip string)移除 string 中多余的空白字符。
$(findstring find,text)text 中找到 find
$(filter pattern…,text)选择 text 中匹配 pattern 的单词。
$(filter-out pattern…,text)选择 text 中不匹配 pattern 的单词。
$(sort list)按字典序对 list 中的单词进行排序,并去重。
$(word n,text)提取 text 中第 n 个单词(一基索引)。
$(words text)计算 text 中的单词数量。
$(wordlist s,e,text)返回 text 中从 se 的单词列表。
$(firstword names…)提取 names 中的第一个单词。
$(lastword names…)提取 names 中的最后一个单词。
$(dir names…)提取每个文件名的目录部分。
$(notdir names…)提取每个文件名的非目录部分。
$(suffix names…)提取每个文件名的后缀(最后一个 . 及其后的字符)。
$(basename names…)提取每个文件名的基名(不含后缀)。
$(addsuffix suffix,names…)suffix 添加到 names 中每个单词的末尾。
$(addprefix prefix,names…)prefix 添加到 names 中每个单词的前面。
$(join list1,list2)将两个平行的单词列表连接起来。
$(wildcard pattern…)查找与 shell 文件名模式(不是 `%’ 模式)匹配的文件名。
$(realpath names…)names 中的每个文件名进行扩展,生成不包含任何 ... 及符号链接的绝对路径。
$(abspath names…)names 中的每个文件名进行扩展,生成不包含 ... 组件的绝对路径,但保留符号链接。
$(error text…)在评估此函数时,make 生成一个带有 text 信息的致命错误。
$(warning text…)在评估此函数时,make 生成一个带有 text 信息的警告。
$(shell command)执行一个 shell 命令并返回其输出。
$(origin variable)返回一个字符串,描述 make 变量 variable 是如何定义的。
$(flavor variable)返回一个字符串,描述 make 变量 variable 的类型。
$(foreach var,words,text)var 依次绑定到 words 中的每个单词,并评估 text,将结果连接起来。
$(if cond,then-part[,else-part])评估 cond;如果非空,则替换为 then-part 的扩展,否则替换为 else-part 的扩展。
$(or cond1[,cond2[,cond3…]])依次评估 condN;替换为第一个非空扩展。如果所有扩展都是空的,则替换为空字符串。
$(and cond1[,cond2[,cond3…]])依次评估 condN;如果任何一个结果为空字符串,则替换为空字符串。否则,替换为最后一个 condN 的扩展。
$(call var,param,…)评估变量 var,将对 $(1)$(2) 的引用替换为第一个、第二个等参数值。
$(eval text)评估 text,然后将结果作为 makefile 命令读取;扩展为空字符串。
$(file op filename,text)扩展参数,然后以模式 op 打开文件 filename 并将 text 写入该文件。
$(value var)评估为变量 var 的内容,不进行任何扩展。

常用make命令参数

参数说明示例
-f指定要使用的 Makefile 文件make -f custom_makefile
-C在指定的目录中执行 make 命令make -C /path/to/directory
-j指定同时执行的任务数量(并行编译)make -j4
-k即使遇到错误也继续编译剩余的目标make -k
-s静默模式,不打印命令执行信息make -s
-i忽略所有命令的错误make -i
-n打印将要执行的命令,但不实际执行make -n
-p打印所有的 Makefile 信息,包括变量和规则make -p
-q仅检查文件是否是最新的,如果是最新的则返回 0 否则返回 1make -q
-r禁用内置规则make -r
-t仅更新目标文件的时间戳而不执行命令make -t
-v显示 make 的版本信息make -v
--no-print-directory禁止 make 在进入子目录时打印目录信息make --no-print-directory
--debug打印调试信息,可选参数有 a(all)、b(basic)、v(verbose)、i(implicit)等make --debug=b
VAR=value通过命令行传递变量值到 Makefile 中make CC=gcc

特殊变量

变量描述
MAKEFILES在每次调用 make 时读取的 Makefile。
VPATH未在当前目录中找到的文件的目录搜索路径。
SHELL系统默认命令解释器的名字,通常是 /bin/sh
MAKESHELLmake 使用的命令解释器的名字,优先于 SHELL(仅 MS-DOS)。
MAKE调用 make 的名字(在配方中使用此变量具有特殊含义)。
MAKE_VERSION内置变量 MAKE_VERSION 扩展为 GNU make 程序的版本号。
MAKE_HOST内置变量 MAKE_HOST 扩展为一个字符串,表示构建 GNU make 的主机。
MAKELEVEL递归级别的数量(子 make)。
MAKEFLAGS传递给 make 的标志。可以在环境或 makefile 中设置它以设置标志。在配方行中直接使用 MAKEFLAGS 是不合适的:其内容可能未正确引用以用于 shell。始终允许递归 make 从其父进程通过环境获取这些值。
GNUMAKEFLAGSmake 解析的其他标志。可以在环境或 makefile 中设置它以设置 make 命令行标志。GNU make 永远不会自己设置这个变量。仅当你希望在 POSIX 兼容的 makefile 中设置 GNU make 特定的标志时才需要这个变量。GNU make 将看到这个变量,而其他 make 实现将忽略它。如果你只使用 GNU make,则不需要它;只需直接使用 MAKEFLAGS。参见“向子 make 传递选项”。
MAKECMDGOALS在命令行中给定的目标。设置此变量对 make 的操作没有影响。
CURDIR设置为当前工作目录的绝对路径名(在处理所有 -C 选项之后,如果有的话)。设置此变量对 make 的操作没有影响。
SUFFIXES在 make 读取任何 makefile 之前的默认后缀列表。
.LIBPATTERNS定义 make 搜索库的命名及其顺序。
  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值