编写规则和格式
main: main.o stack.o maze.o
gcc main.o stack.o maze.o
main.o: main.h stack.h maze.h
stack.o: stack.h main.h
maze.o: maze.h main.h
clean:
-rm main *.o
.PHONY: clean
clean讲解
通常Makefile都会有一个 clean 规则,用于清除编译过程中产生的二进制文件,保留源文件
clean:
@echo "cleaning project"
-rm main *.o
@echo "clean completed"
.PHONY: clean
如果 make 执行的命令前面加了 @ 字符,则不显示命令本身而只显示它的结果
通常 make 执行的命令如果出错(该命令的退出状态非0)就立刻终止,不再执行后续命令,但如果命令前面加了 - 号,即使这条命令出错, make 也会继续执行后续命令。
通常 rm 命令和 mkdir 命令前面要加 - 号,因为 rm 要删除的文件可能不存在, mkdir 要创建的目录可能已存在,这两个命令都有可能出错,但这种错误是应该忽略的
.PHONY是make内置特殊目标,把 clean 声明为一个伪目标
clean 目标是一个约定俗成的名字,在所有软件项目的Makefile中都表示清除编译生成的文件,类似这样的约定俗成的目标名字有:
- all ,执行主要的编译工作,通常用作缺省目标。
- install ,执行编译后的安装工作,把可执行文件、配置文件、文档等分别拷到不同的安装目录。
- clean ,删除编译生成的二进制文件。
- distclean ,不仅删除编译生成的二进制文件,也删除其它生成的文件,例如配置文件和格式转换后的文档,执行 make distclean 之后应该清除所有这些文件,只留下源文件
Makefile中变量
使用=来赋值
goo = $(foo)
foo = bar
all:
@echo $(goo)
上面中定义的变量goo = $(foo),读到该句时不会立即展开,等到执行make命令时才会展开,所以会输出:
bar
使用:=来赋值
goo := $(foo)
foo := bar
all:
@echo $(goo)
上面定义变量使用:=符号,读到该句时会立即展开,所以goo的值是空,输出也是空
使用?=来赋值
例如 foo ?= $(bar) 的意思是:
如果 foo 没有定义过,那么 ?= 相当于 = ,定义 foo 的值是 $(bar) ,但不立即展开;
如果先前已经定义了 foo ,则什么也不做,不会给 foo 重新赋值
使用+= 来赋值
+= 运算符可以给变量追加值,例如:
objects = main.o
objects += $(foo)
foo = foo.o bar.o
常用的特殊变量
- $@ ,表示规则中的目标。
- $< ,表示规则中的第一个条件。
- $? ,表示规则中所有比目标新的条件,组成一个列表,以空格分隔。
- $^ ,表示规则中的所有条件,组成一个列表,以空格分隔。
例如上面写的
main: main.o stack.o maze.o
gcc main.o stack.o maze.o -o main
可以改写为
main: main.o stack.o maze.o
gcc $^ -o $@
最后,我们的Makefile可以写成下面这样
all: main
main: main.o stack.o maze.o
gcc $^ -o $@
main.o: main.h stack.h maze.h
stack.o: stack.h main.h
maze.o: maze.h main.h
clean:
-rm main *.o
.PHONY: clean
在实际编写时,在写 main.o 、 stack.o 和 maze.o 这三个目标的规则时要查看源代码,找出它们依赖于哪些头文件,这很容易出错,一是因为有的头文件包含在另一个头文件中,在写规则时很容易遗漏,二是如果以后修改源代码改变了依赖关系,很可能忘记修改Makefile的规则。
为了解决这个问题,可以用 gcc 的 -M 选项自动生成目标文件和源文件的依赖关系:
$ gcc -M main.c
-M 选项把 stdio.h 以及它所包含的系统头文件也找出来了,如果我们不需要输出系统头文件的依赖关系,可以用 -MM 选项
$ gcc -MM *.c
main.o: main.c main.h stack.h maze.h
maze.o: maze.c maze.h main.h
stack.o: stack.c stack.h main.h
接下来如何将依赖文件写入Makefile呢,GNU make官方手册建议这样写
all: main
main: main.o stack.o maze.o
gcc $^ -o $@
clean:
-rm main *.o
.PHONY: clean
sources = main.c stack.c maze.c
include $(sources:.c=.d)
%.d: %.c
set -e; rm -f $@; \
$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
sources 变量包含我们要编译的所有 .c 文件, $(sources:.c=.d) 是一个变量替换语法,把 sources 变量中每一项的 .c 替换成 .d ,所以 include 这一句相当于:
include main.d stack.d maze.d
类似于C语言的 #include 指示,这里的 include 表示包含三个文件 main.d 、 stack.d 和 maze.d ,这三个文件也应该符合Makefile的语法。
本文介绍了Makefile的基本概念,包括编写规则和格式,如clean规则的使用和命令修饰符。讨论了Makefile中的变量赋值类型,如`=`、`:=`、`?=`和`+=`。还解析了Makefile中的一些常用特殊变量,如`$@`、`$<`、`$?`和`$^`。最后,提到了如何利用gcc的-M选项自动生成依赖关系,并将其纳入Makefile。
2800

被折叠的 条评论
为什么被折叠?



