makefile中的规则
概述
规则的一般形式:
target: <normal-prerequisites> | <order-only-prerequisites> <; commands>
<commands>
makefile的构成主体是各种规则。无论是什么形式的规则,都会描述一个三元组:目标、必要条件、命令。一个规则可以同时描述一个或者多个甚至无数个工作目标(其实是无数个规则构成的模式),多目标的规则可以视为多个规则的叠加描述,都可拆分成单一工作目标的规则。工作目标是某个文件名,或者某个“动作”,但不能为空。必要条件也可以是具体的文件或者“动作”,可以为空。命令是发生更新时交给子shell执行的命令,可以多条,每条交给一个shell,如果要把所有的命令都给一个shell执行,要把它们整成一行或用连行符号\
。
目标和必要条件中的变量会立即扩展,而命令不会扩展。(make会在所有的变量和命令都被加载到数据库之后,在将要执行命令之前扩展命令中的变量。)如果在定义了.SECONDEXPANSION
这个目标,之后的规则中的必要条件都会进行两次扩展。第二次扩展发生在make读入所有的变量和规则之后,在执行规则之前。
当make读入makefile后,需要根据必要条件的类型和时间戳与目标的时间戳,决定是否采取更新动作(把命令交给子shell执行)。
必要条件分为两种:
普通必要条件。更新目标之前先更新普通必要条件。只要该必要条件的时间戳大于或等于目标的时间戳(尚不存在的文件时间戳可以认为是负无穷或者某个古老的时间点),就会导致更新动作;
“order-only“必要条件,(只执行一次的必要条件,|
之后的必要条件)。Make要更新这个必要条件时会检查是否存在该文件,如果存在,就不会继续更新这个必要条件(不管他的时间戳)也不会因此更新目标;如果不存在,则检查在此次执行make的过程中是否执行过以该必要条件为目标的规则,如果没有则执行一次,否则结束对此必要条件的更新。
下面是经常用到的例子,跟新目标文件之间要求目标文件夹已存在,但总不能每条跟新目标文件的规则里都执行mkdir -p,这样虽然也能干活,但看着难受。更好的办法是让目标文件order-only依赖与文件夹,然后针对文件夹的更新动作是mkdir -p,这样mkdir就只执行一次了。
OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
all: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
mkdir $(OBJDIR)
具体规则,指定具体的工作目标,如果必要条件中有时间戳靠后的文件,则执行命令。这是最常见的规则。模式规则中,目标使用的是通配符而不是具体的文件名,这让make对与模式相符的工作目标应用该规则。隐含规则可以是模式规则,也可以是内置于make的后缀规则。
具体规则
具体规则以特定的文件作为工作目标和必要条件。每个规则可以由多个工作目标。意味着每个工作目标都依赖后面的必要条件。
规则不必一次性定义完全。每当make看到一个工作目标,就会将该工作目标和必要条件加入依存图。如果工作目标已经存在,任何额外的必要条件会被添加到依存图中。如果一个工作目标多次制定了命令(不是一次命令由多条命令组成),则取最后一次命令作为更新动作,忽略之前的命令,make会给出警告。
假想工作目标:工作目标可以不是真实的文件,表示一个动作,成为假想工作目标。它总是未更新的,很“旧”,所以每次该规则的命令都会被执行。为了防止该动作可能与某个文件重名,可用.PHONY:
声明。
模式规则
背景:
- 在包含很多文件的大型程序中,手动指定每条规则将会不切实际。而且,这些命令很可能是重复的代码,如果修改某处可能引起多处修改,产生难以维护的问题。
- 许多程序在读取输入文件以及产生输出文件时都会依照惯例。
模式
模式规则中的百分号字符%
大体上等效于unix中的星号*
,它可以代表任意多个字符。百分比字符可以放在模式的任何地方,但只能出现一次。当make搜索要使用的模式规则时,它首先会查找符合的模式规则工作目标,将百分号代表的部分替换到必要条件中来检查模式规则的必要条件。
例如:
%: %.mod
$(COMPILE.mod) -o $@ -e $@ $^
%: %.cpp
$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
%: %.sh
cat $< >$@
chmod a+x $@
静态模式规则
静态模式规则与一般模式规则的唯一差别是指定了工作目标的匹配集合。
$(OBJECTS): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
后缀规则
后缀规则是旧时没有模式规则时的替代品,已弃用。
隐含规则
隐含规则是内置于make的模式规则。要查看make内置了那些规则,可以使用-p, --print-data-base
命令显示。此命令还可以看到内置的变量定义。
当make检查一个工作目标时,如果找不到它的具体规则,就会使用隐含规则。
使用不带命令的模式规则可以屏蔽相应的隐含规则。
例如:
hello:
hello: hello.c
都会自动执行相应的编译过程。
%: %.o
可以屏蔽从.o文件链接可执行程序的规则。
杂项
规则中可以使用shell的模式匹配,而其他地方要使用wildcard
函数。
规则中的必要条件不必一次描述完全,可以分为多次,但是对于同一规则的命令,只取最后一次描述中的命令。比如:
hello : hello.c
echo hello
hello : include.h
gcc -o hello hello.c
以hello为目标的规则中有两个必要条件hello.c
和include.h
,此规则的命令只有gcc -o hello hello.c
。