文章目录
- 跟我一起写Makefile细节总结学习笔记
-
- 第一,二章
- [此篇仅为方便查阅记忆,详细的请看[seisman/how-to-write-makefile: 跟我一起写Makefile重制版 (github.com)](https://github.com/seisman/how-to-write-makefile)](#此篇仅为方便查阅记忆,详细的请看seisman/how-to-write-makefile: 跟我一起写Makefile重制版 (github.com))
- Makefile介绍
-
- ,\,Tab键,使用变量,-减号,
- [Makefile 里主要包含了五个东西:](#Makefile 里主要包含了五个东西:)
- 书写规则
跟我一起写Makefile细节总结学习笔记
第一,二章
此篇仅为方便查阅记忆,详细的请看seisman/how-to-write-makefile: 跟我一起写Makefile重制版 (github.com)
Makefile介绍
,,Tab键,使用变量,-减号,
规则
shell
target ... : prerequisites ...
recipe
...
...
原始Makefile
shell
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.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 buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
稍微简化后的
shell
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
-rm edit $(objects)
注意
命令的Tab键位不可用空个代替。
在我们的 makefile 中以 $(objects) 的方式来使用这个变量
反斜杠(\ )是换行符的意思。
.PHONY 表示 clean 是一个"伪目标"。
一个小减号的意思就是,也 许某些文件出现问题,但不要管,继续做后面的事
不成文的规矩是------"clean 从来都是放在文件的最 后"。
Makefile 里主要包含了五个东西:
显式规则、隐式规则、变量定义、指令和注释。
- 显式规则。显式规则说明了如何生成一个或多个目标文件。这是由 Makefile 的书写者明显指出要 生成的文件、文件的依赖文件和生成的命令。
- 隐式规则。由于我们的 make 有自动推导的功能,所以隐式规则可以让我们比较简略地书写 Makefile,这是由 make 所支持的。 8 Chapter 2. makefile 介绍 , 发行版本 1.0
- 变量的定义。在 Makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点像你 C 语 言中的宏,当 Makefile 被执行时,其中的变量都会被扩展到相应的引用位置上。
- 指令。其包括了三个部分,一个是在一个 Makefile 中引用另一个 Makefile,就像 C 语言中的 include 一样;另一个是指根据某些情况指定 Makefile 中的有效部分,就像 C 语言中的预编译 #if 一样; 还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
- 注释。Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用 # 字符,这个就像 C/C++ 中的 // 一样。如果你要在你的 Makefile 中使用 # 字符,可以用反斜杠进行转义,如:# 。 最后,还值得一提的是,在 Makefile 中的命令,必须要以 Tab 键开始。
书写规则
在规则中使用通配符
另给一个变量使用通配符的例子:
-
列出一确定文件夹中的所有 .c 文件。
c++objects := $(wildcard *.c)
-
列出 (1) 中所有文件对应的 .o 文件,在(3)中我们可以看到它是由 make 自动编译出的:
c++$(patsubst %.c,%.o,$(wildcard *.c))
-
由 (1)(2) 两步,可写出编译并链接所有 .c 和 .o 文件
c++objects := $(patsubst %.c,%.o,$(wildcard *.c)) foo : $(objects) cc -o foo $(objects)文件搜寻
文件搜寻
vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>
vpath <pattern>
清除符合模式 <pattern> 的文件的搜索目录。
vpath
清除所有已被设置好了的文件搜索目录。
vpath 使用方法中的 <pattern> 需要包含 % 字符。% 的意思是匹配零或若干字符,(需引用 % ,使 用 \ )例如,%.h 表示所有以 .h 结尾的文件。<pattern>指定了要搜索的文件集,而<directories> 则 指定了 < pattern> 的文件集的搜索的目录。例如:
c++
vpath %.h ../headers
伪目标
当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记".PHONY"来显式地指明 一个目标是"伪目标",向 make 说明,不管是否有这个文件,这个目标就是"伪目标"。 .PHONY : clean 只要有这个声明,不管是否有"clean"文件,要运行"clean"这个目标,只有"make clean"这样。 于是整个过程可以这样写:
c++
.PHONY : clean
clean :
rm *.o temp
多目标(自动变量)
c++
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
等价于
c++
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
静态模式
c++
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
上面的例子中,指明了我们的目标从 $object 中获取,%.o 表明要所有以 .o 结尾的目标,也就是 foo.o bar.o ,也就是变量 $object 集合的模式,而依赖模式 %.c 则取模式 %.o 的 % ,也就是 foo bar ,并为其加下 .c 的后缀,于是,我们的依赖目标就是 foo.c bar.c 。而命令中的 \< 和 @ 则是自动化变量, @ 则是自动化 变量, @则是自动化变量,\< 表示第一个依赖文件,@ 表示目标集(也就是"foo.o bar.o")。于是,上面的规则展开后等价 于下面的规则:
c++
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
试想,如果我们的 %.o 有几百个,那么我们只要用这种很简单的"静态模式规则"就可以写完一堆 规则,实在是太有效率了。"静态模式规则"的用法很灵活,如果用得好,那会是一个很强大的功能。再 看一个例子:
c++
files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
( f i l t e r (filter %.o, (filter(files)) 表示调用 Makefile 的 filter 函数,过滤"$files"集,只要其中模式为"%.o"的 内容。其它的内容,我就不用多说了吧。这个例子展示了 Makefile 中更大的弹性。
自动生成依赖性
在 Makefile 中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的 main.c 中有一 句 #include "defs.h" ,那么我们的依赖关系应该是:
c++
main.o : main.c defs.h
但是,如果是一个比较大型的工程,你必需清楚哪些 C 文件包含了哪些头文件,并且,你在加入或 删除头文件时,也需要小心地修改 Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容 易出错的事情,我们可以使用 C/C++ 编译的一个功能。大多数的 C/C++ 编译器都支持一个"-M"的 选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。例如,如果我们执行下面的命令:
c++
cc -M main.c
其输出是:
c++
main.o : main.c defs.h
于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编 译器自动生成了。需要提醒一句的是,如果你使用 GNU 的 C/C++ 编译器,你得用 -MM 参数,不然,-M 参数会把一些标准库的头文件也包含进来。
gcc -M main.c 的输出是
c++
main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \
/usr/include/bits/sched.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/bits/wchar.h /usr/include/gconv.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h
gcc -MM main.c 的输出则是:
c++
main.o: main.c defs.h