学习日记:如何写Makefile(二)——规则篇(上)

本文是学习Makefile的笔记,主要探讨显式规则的三个方面:通配符、伪目标和空目标,以及Makefile中的变量使用。通过实例解析,帮助读者掌握Makefile的编写技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、显式规则(Explicit Rules)

通常在写makefile时使用的都是显式规则,这需要指明target和prerequisite文件。一条规则可以包含多个target,这意味着其中每个target的prerequisite都是相同的。当其中的一个target被修改后,整个规则中的其他target文件都会被重新编译或执行。

通配符(Wildcards)

make支持的通配符与Bourne shell基本相同,包括~, *, ?, [...], [^...]。
*.*      表示了所有文件;
?        表示任意单个字符;
[...]     表示一个字符类;
[^...]   表示相反的字符类。
~        表示当前用户的/home路径,~加用户名可以表示该用户的/home路径。
注意: make会在读取makefile的时候就自动补充好通配符替代的内容,而shell则是在执行命令的时候才会进行通配符替代,在某些复杂情况,这两种方式会有极大的区别。

伪目标(Phony Targets)

多数情况下makefile中的target目标文件是像前面提到的那样带有指定的prerequisite文件,但也有一些target仅仅是作为一个标签,代表了一条命令,这种不代表任何文件的目标就被称为伪目标。常见的伪目标例如在makefile开头部分的第一个目标 all, 以及前面例子中见到的 clean:
clean:
    rm -f *.o lexer.c
但是,make本身是无法区别出目标文件和伪目标的,如果碰巧在编译路径下有一个与伪目标同名的文件存在,那么make会在依赖关系图中把这个文件与伪目标名相关联。而再运行make clean 命令则会因为clean文件存在且如果没有被更新过,则makefile中的clean对应的命令将不会被执行。

为了避免这种情况,GNU make提供了一种特殊目标: “.PHONY”,用来表示目标文件不是真正的文件,即伪目标。clean命令可以被写作:
.PHONY: clean
clean:
    rm -f *.o lexer.c
这样,即使再有名为clean的文件存在,make也会执行clean后面的命令。

通常不会将一个伪目标的prerequisite设置为真是存在的文件,因为.PHONY会让他后面的文件在每次make时都进行重新编译。伪目标可以被认为是内嵌在makefile中的shell脚本。
优势: 通过使用伪目标在编译过程中进行屏幕输出,可以使make的可读性增加。例如:
$(Program): build_msg $(OBJECTS) $(BUILTINS_DEP) $(LIBDEP)
        $(RM) $@
        $(CC) $(LDFLAGS) -o $(Program) $(OBJECTS) $(LIBS)
        ls -l $(Program)
        size $(Program)
.PHONY: build_msg
build_msg:
        @printf "#\n# Building $(Program)\n#\n"
这里将printf命令作为伪目标,可以使make在更新任何prerequisite之前就将指定的编译信息输出。

同时,伪目标也可也作为makefile的默认目标,放在文件的最前端,由于伪目标的特性,他指出的所有prerequisite都会被重新编译。这样可以用来同时生成多个目标。另外,与普通目标文件一样,伪目标也可也使用依赖关系,例如:
.PHONY: clean cleano cleanc
clean: cleano cleanc
        -rm $(program)
cleano: 
        -rm *.o
cleanc:
        -rm lexer.c
这样就可以对不同类型的文件进行单独删除。

空目标(Empty Targets)

空目标是伪目标的一种变形形式,通常情况下通过创建一个空文件来实现。例如:
size: count_words.o
        size $^
        touch size
这样,空文件size就被make当作时间戳,只有当count_words.o被更新时,size里面的命令才会再次被执行。另外,所有加载了size作为prerequisite的目标,都不会因为size被编译而强制编译,他们的其他prerequisite目标被更新。

二、变量

变量最简单的形式就是:
$(variable_name)
变量可以包含几乎所有的字符包括标点符号。一般情况下,变量名需要被$( )所包裹,但是当变量名只有一个字符时,括号可以省略。makefile可以定义很多变量,但同时make本身也定义了一些自动变量。
自动变量
自动变量是make自动根据规则生成的,不需要用户显式的指出相应的文件或目标名称。以下就是七个最核心的自动变量:
$@    目标文件的文件名;
$%     仅当目标文件为归档成员文件(.lib 或者 .a)时,显示文件名,否则为空;
$<      依赖(prerequisite)列表里面的第一个文件名;
$?      所有在prerequisite列表里面比当前目标新的文件名,用空格隔开;
$^      所有在prerequisite列表中的文件,用空格隔开; 如果有重复的文件名(包含扩展名),会自动去除重复;
$+      与$^相似,也是prerequisite列表中的文件名用空格隔开,不同的是这里包含了所有重复的文件名;
$*       显示目标文件的主干文件名,不包含后缀部分。
此外,上面的每个变量都带有两个不同的变种,用于适应不同种类的make。分别是在后面附加一个“D”或者“F”。例如,$(^D)就是代表所有依赖文件的路径,$(<F)表示依赖文件第一个的文件部分的值。使用上述内容前面的makefile可以重写为:
CC = gcc
object = lexer.o count_words.o
program = count_words
$(program): size $(object) -lfl
        $(CC) $(object) -lfl -o $@
count_words.o: count_words.c
        $(CC) -c $^
lexer.o: lexer.c
        $(CC) -c $^
lexer.c: lexer.l
        flex -t $^ > $@ 
size: count_words.o
        size $^
        touch size
.PHONY: clean cleano cleanc
clean: cleano cleanc
        -rm $(program)
cleano: 
        -rm *.o
cleanc:
        -rm lexer.c
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值