Makefile编写规则

Makefile在Linux环境下非常有用,它规定了整个工程的编译规则,即哪些文件先编译、哪些文件后编译、相互之间依赖关系是什么,是编写大型工程必不可少的工具。 而了解了Makefile的规则,你会发现其实它能做的不仅仅是C/C++的编译,它更是提供了一个强大的批处理工具。 下面的叙述以GNU的make为主。

由于网上给出makefile的教程都比较冗长,可能看完自己都不会写。 而本文主要以我自己的实操体验为主,想到什么就补充什么。

基本语法

make的语法规则如下,注意每个recipe前面都需要有tab键。 意思是,要完成target,需要先完成prerequisites;如果prerequisites都满足,则执行recipe指令(对由于这里是一系列指令,所以只要是Linux的指令,什么都可以,因而makefile不仅仅可以用来做编译,用来做其他批处理也是可以的)。

target ... : prerequisites ...
	recipe
	...
	...

每次执行make,程序都会自动检测prerequisites中的内容有无变化(即文件是否比原来的新),如果有则重新执行prerequisites中的目标;否则就直接跳过。

常见的C编译命令写成makefile见下,代表的是要生成test文件,首先要有test.c文件,然后通过gcc test.c -o test指令就能生成。

test : test.c
	gcc test.c -o test

变量

但上面的方法显然太过麻烦了,当文件一多起来就manage不住了。 因而可以通过以下方式进行编写,使得makefile更加易写。

首先是声明变量,如

CC = gcc
FLAGS = -O3

下文要用到时就可以直接用$(CC)$(FLAGS)等等调用了。

然后是自动变量(automatic variables),以下面的语句为例

all: library.cpp main.cpp
  • $@代表目标文件,上例中$@等价于all
  • $^代表所有的依赖文件,上例中$^等价于library.cpp main.cpp
  • $<代表第一个依赖文件,上例中$<等价于library.cpp

自动变量结合通配符%就能写出下面的这种指令了

% : %.c
	$(CC) $(FLAGS) $< -o $@

代表的是文件夹中要通过某一.c文件(这里不妨设它就是X.c了)生成对应的执行文件X,需要取出第一个依赖文件($<),即X.c,通过编译指令gcc -O3生成对应的目标文件($@),即X(%)。

这里要注意通配符%*的区别,后者代表任意文件,在删除文件时常用。

删除文件

.PHONY为伪目标文件,-代表执行时出了问题也不要管

.PHONY : clean
clean :
	-rm edit $(objects)

完整例子

上面已经将makefile的基本操作讲完了,已经差不多能满足日常需求,至于其他进阶操作则以后再补充。

下面是计网套接字编程生成服务器端和客户端的makefile文件,原文件夹中含有server.cclient.c文件。

CC = gcc
FLAGS = -pthread
ALL = server client

all: $(ALL)

% : %.c
	$(CC) $(FLAGS) $< -o $@

.PHONY : clean
clean :
	-rm -f *.o $(ALL)

执行流程都是从上往下,要完成all目标,需要先完成serverclient两个子目标。 这两个子目标都包含在% : %.c中,因而可以直接执行。

其他

  • #用来注释
  • makefile中长的recipe可以用/换行,prerequisites不能换行,但可以通过下面添加变量的方法解决过长问题
LONGPRE := pre1
LONGPRE += pre2 pre3

all : $(LONGPRE)
  • 如果在gcc编译指令中声明-DXX,则意味着定义了变量XX,在C的文件中#ifdef(XX)可调用,例子可见C与汇编混编
  • @echo不会回显这条指令,echo则会,参见此回答

参考资料

  1. GNU make, https://www.gnu.org/software/make/manual/make.html#Reference
  2. http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/
  3. https://blog.csdn.net/weixin_38391755/article/details/80380786