Make
Make
$ make a.txt
但是,如果你真的输入这条命令,它并不会起作用。因为
a.txt: b.txt c.txt
cat b.txt c.txt > a.txt
也就是说,
$ make -f rules.txt
# 或者
$ make --file=rules.txt
上面代码指定
Make 文件格式
<target> : <prerequisites>
[tab] <commands>
上面第一行冒号前面的部分,叫做
目标(target)
一个目标(target)就构成一条规则。目标通常是文件名,指明
除了文件名,目标还可以是某个操作的名字,这称为
clean:
rm *.o
上面代码的目标是
$ make clean
但是,如果当前目录中,正好有一个文件叫做
.PHONY: clean
clean:
rm *.o temp
声明
$ make
上面代码执行
前置条件(prerequisites)
前置条件通常是一组文件名,之间用空格分隔。它指定了
result.txt: source.txt
cp source.txt result.txt
上面代码中,构建make result.txt
可以正常运行,否则必须再写一条规则,来生成
source.txt:
echo "this is the source" > source.txt
上面代码中,make source.txt
,它都会生成。
$ make result.txt
$ make result.txt
上面命令连续执行两次 make result.txt
。第一次执行会先新建
source: file1 file2 file3
上面代码中,
$ make source
执行 make source
命令后,就会一次性生成
$ make file1
$ make file2
$ make file3
命令(commands)
命令(commands)表示如何更新目标文件,由一行或多行的
.RECIPEPREFIX = >
all:
> echo Hello, world
上面代码用
需要注意的是,每行命令在一个单独的
var-lost:
export foo=bar
echo "foo=[$$foo]"
上面代码执行后(make var-lost
var-kept:
export foo=bar; echo "foo=[$$foo]"
另一个解决办法是在换行符前加反斜杠转义。
var-kept:
export foo=bar; \
echo "foo=[$$foo]"
最后一个方法是加上.ONESHELL:
命令。
.ONESHELL:
var-kept:
export foo=bar;
echo "foo=[$$foo]"
文件格式
注释
井号(#)在
# 这是注释
result.txt: source.txt
# 这是注释
cp source.txt result.txt # 这也是注释
回声(echoing)
正常情况下,
test:
# 这是测试
执行上面的规则,会得到下面的结果。
$ make test
# 这是测试
在命令的前面加上
test:
@# 这是测试
现在再执行 make test
,就不会有任何输出。由于在构建过程中,需要了解当前在执行哪条命令,所以通常只在注释和纯显示的
test:
@# 这是测试
@echo TODO
通配符
通配符(wildcard)用来指定一组符合条件的文件名。
clean:
rm -f *.o
模式匹配
%.o: %.c
等同于下面的写法。
f1.o: f1.c
f2.o: f2.c
使用匹配符
变量和赋值符
txt = Hello World
test:
@echo $(txt)
上面代码中,变量
调用
test:
@echo $$HOME
有时,变量的值可能指向另一个变量。
v1 = $(v2)
上面代码中,变量
# 在执行时扩展,允许递归扩展。
VARIABLE = value
# 在定义时扩展。
VARIABLE := value
# 只有在该变量为空时才设置值。
VARIABLE ?= value
# 将值追加到变量的尾端。
VARIABLE += value
内置变量(Implicit Variables)
output:
$(CC) -o output input.c
自动变量(Automatic Variables)
(1)$@
make foo
的
a.txt b.txt:
touch $@
等同于下面的写法。
a.txt:
touch a.txt
b.txt:
touch b.txt
(2)$<
a.txt: b.txt c.txt
cp $< $@
等同于下面的写法。
a.txt: b.txt c.txt
cp b.txt a.txt
(3)$?
(4)$^
(5)$*
(6)
`(7)$(
所有的自动变量清单,请看手册。下面是自动变量的一个例子。
dest/%.txt: src/%.txt
@[ -d dest ] || mkdir dest
cp $< $@
上面代码将
判断和循环
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
上面代码判断当前编译器是否
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
# 等同于
all:
for i in one two three; do \
echo $i; \
done
上面代码的运行结果。
one
two
three
函数
$(function arguments)
# 或者
${function arguments}
(1)
srcfiles := $(shell echo src/{00..99}.txt)
(2)
srcfiles := $(wildcard src/*.txt)
(3)
$(subst from,to,text)
下面的例子将字符串
$(subst ee,EE,feet on the street)
下面是一个稍微复杂的例子。
comma:= ,
empty:=
# space变量用两个空变量作为标识符,当中是一个空格
space:= $(empty) $(empty)
foo:= a b c
bar:= $(subst $(space),$(comma),$(foo))
# bar is now `a,b,c'.
(4)
$(patsubst pattern,replacement,text)
下面的例子将文件名
$(patsubst %.c,%.o,x.c.c bar.c)
(5)替换后缀名
替换后缀名函数的写法是:变量名
min: $(OUTPUT:.js=.min.js)
上面代码的意思是,将变量
案例
执行多个目标
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj
rm *.o
cleandiff
rm *.diff
上面代码可以调用不同目标,删除不同后缀名的文件,也可以调用一个目标(cleanall
编译C 语言项目
edit : main.o kbd.o command.o display.o
cc -o edit main.o kbd.o command.o display.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h
cc -c display.c
clean
rm edit main.o kbd.o command.o display.o
.PHONY: edit clean
更完整的案例参考如下:
# Makefile for the C code in concurrent-servers.
#
# This code is in the public domain.
CC = gcc
CCFLAGS = -std=gnu99 -Wall -O3 -g -DNDEBUG -pthread
LDFLAGS = -lpthread -pthread
# It's possible to compile uv-server after installing libuv. The full
# instructions for installation I used (including `make install`) are from:
# https://github.com/libuv/libuv/blob/master/README.md.
# libuv compiles into a shared library which is placed alongside the .a in the
# installation directory.
LDLIBUV = -luv -Wl,-rpath=/usr/local/lib
EXECUTABLES = \
sequential-server \
select-server \
epoll-server \
uv-server \
uv-timer-sleep-demo \
uv-timer-work-demo \
uv-isprime-server \
threadspammer \
blocking-listener \
nonblocking-listener \
threaded-server
all: $(EXECUTABLES)
sequential-server: utils.c sequential-server.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS)
select-server: utils.c select-server.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS)
threaded-server: utils.c threaded-server.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS)
epoll-server: utils.c epoll-server.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS)
uv-server: utils.c uv-server.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS) $(LDLIBUV)
uv-timer-sleep-demo: utils.c uv-timer-sleep-demo.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS) $(LDLIBUV)
uv-timer-work-demo: utils.c uv-timer-work-demo.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS) $(LDLIBUV)
uv-isprime-server: utils.c uv-isprime-server.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS) $(LDLIBUV)
threadspammer: threadspammer.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS)
blocking-listener: utils.c blocking-listener.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS)
nonblocking-listener: utils.c nonblocking-listener.c
$(CC) $(CCFLAGS) $^ -o $@ $(LDFLAGS)
.PHONY: clean format
clean:
rm -f $(EXECUTABLES) *.o
format:
clang-format -style=file -i *.c *.h