Makefile基础

本文介绍了Makefile的基本概念,包括编写规则和格式,如clean规则的使用和命令修饰符。讨论了Makefile中的变量赋值类型,如`=`、`:=`、`?=`和`+=`。还解析了Makefile中的一些常用特殊变量,如`$@`、`$<`、`$?`和`$^`。最后,提到了如何利用gcc的-M选项自动生成依赖关系,并将其纳入Makefile。

编写规则和格式

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的语法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值