C++之Makefile语法与写法
始、Makefile的规则
关键:规则的嵌套——会先完成规则的先验条件
变量使用时都用${ }包围起来,才能取到变量的实际值,否则取到的就是一个值为变量名的东西了。变量能赋值的,要不然怎么叫变量呢
这里的变量在实际执行时,会被换成它们的实际值,变量名起简化和占位符的作用。
一、变量与函数
1. 函数notdir,wildcard和patsubst
这三个都是函数名,函数格式为:return = $(func arg1,arg2,..),即 $ 和 () 的结合。
例:将当前文件夹中所有以 .c为后缀的文件名替换为以 .o为后缀,并返回一个以空格为间隔的列表
SRC = $(patsubst %.c,%.o,${./})
2. 逻辑判断 ——参考博客
ifeq (arg1, arg2):当arg1 == arg2 时,为true。
# 1. 连续的结构,只需要一个endif ifeq (${mode}, TE) EXECUTABLE := te else ifeq (${mode}, TR) EXECUTABLE := tr endif # 2. 非连续的两个结构,每个结构都需要一个endif ifeq (${mode}, TE) EXECUTABLE := te endif ifeq (${mode}, TR) EXECUTABLE := tr endif
可以逻辑判断实现条件编译,如下,不同mode值将执行不同的规则。
ifeq (${mode}, train} .PHONY : target ${TRAIN} target : ${TRAIN} else ifeq (${mode}, test} .PHONY : target ${TEST} target : ${TEST} endif
二、Makefile中的自动化变量$@, $^, $< , $?, $%, $+, $* 等的含义 ——参考博客
$@ 表示目标文件 $^ 表示所有的依赖文件 $+ 这个变量很像“$^”,也是所有依赖目标的集合。只是它不去除重复的依赖目标 $< 表示第一个依赖文件 $? 表示比目标还要新的依赖文件列表 $% 仅当目标是函数库文件时,表示规则中的目标成员名。例如,如果一个目标是“foo.a(bar.o)”,那么,“$%”就是“bar.o”,“$@”就是“foo.a”。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空 $* 这个变量表示目标模式中“%”及其之前的部分。如果目标是“dir/a.foo.b”,并且目标的模式是“a.%.b”,那么,“$*”的值就是“dir/a.foo”。这个变量对于构造有关联的文件名是比较有较。
如果目标中没有模式的定义,那么“$*”也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么“$*”就是除了后缀的那一部分。例如:如果目标是“foo.c”,因为“.c”是make所能识别的后缀名,所以,“$*”的值就是“foo”。
这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用“$*”,除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么“$*”就是空值。
三、规则 ——参考 博客
1. makefile中的规则的基本格式如下:
targets:dependeds
commands
targets:要生成的文件(多个文件则以空格分开),或者是一个动作,可用称为伪目标;若target为空(未赋值的变量),则该规则无效
dependeds:执行此规则所必须的依赖,可以是其它target,形成递归嵌套;所有依赖都满足后才会执行command生成target;
command:规则所执行的命令,以tab键开始;规则后的所有连续的 以tab键开始的行 都会被视作command。tab、多个空格 二者是不等效的。
eg:
... main.o:main.cpp gcc -o main.o ...
test:test.o
...
.PHONY:clean
clean:
rm -rf ...
一次执行makefile,有一个最终目标,这个最终目标可能会带动其它依赖目标的生成。
2. 规则的执行:
1)直接执行 make 时,先查找makefile中的第一个规则,以这个规则的目标(若有多个目标,则以第一个)为最终目标,完成这个规则的执行; ==》如果要执行其它规则,需要指定目标,如 make test、 make clean。
2)依赖项dependeds将从左往右检查是否存在,如果需要执行其它嵌套的规则,会先执行,若不存在且无生成规则,则停止并放弃检查后面的依赖项。
3)除了第一个规则或者指定的规则,.PHONY指定的目标也会在执行时生成。
3. 伪目标
上面的clean是伪目标,所以也不会真正生成clean文件。由于它没有依赖项,make无法判断伪目标的依赖关系,所以要执行相关命令需要显式地指定目标,如:make clean,这种规则每次指定时都一定会执行。
.PHONY可以将目标显示地声明为伪目标。即使真的有一个文件和伪目标同名,.PHONY指定的伪目标的规则在执行时也不会生成文件。
.PHONY也可以用于指定一个伪目标,该伪目标依赖于多个真实目标,如下:
.PHONY:all # .PHONY后面跟着all,all没有具体的生成命令,但是all依赖于aa和bb,故而aa、bb会先生成
all:aa bb
aa: xxx
...
伪目标每次要生成时都会生成,无论是否有更改;其它规则如果是未更改则不会重新生成。
* 遇到一个问题,以上面的.PHONY:all为例,会出现 make: Nothing to be donefor'all'.
解决:改为以下写法即可达到每次make都重新执行规定的目标
.PHONY:all aa bb
all:aa bb
* .PHONY不影响第一规则为默认规则,若.PHONY所在规则不是第一规则,其指定的规则用make是不会默认执行的。
下面写法里,执行 make,则aa,bb都会生成,.PHONY行 和 伪目标all行可以调换顺序,等效。
.PHONY:all aa bb
all:aa bb
aa: xxx
...
下面的例子中,执行 make,只有aa会生成。
aa: xxx
...
.PHONY:all aa bb
all:aa bb
四、参数传递
一个例子 :这个用 -D 传递进去是一个宏,这个宏也可以赋值 更多
-DDEBUG #相当于在代码里面定义了 #define DEBUG 1
-DDEBUG=2 #相当于在代码里面定义了 #define DEBUG 2。
另一个例子
Makefile文件: ARCH := $(shell arch) ifeq ($(ARCH), "x86_64") SETTING := 1 else SETTING := 0 endif CXX := g++ -std=c++11 CFLAGS := -Wall -g -O2 -D${SETTING} ALL = main OBJECT = main.o LIBS = %.o:%.cpp $(CXX) -c $^ -o $@ $(LIBS) $(CFLAGS) $(ALL):$(OBJECT) $(CXX) -o $@ $^ $(LIBS) $(CFLAGS) .PHONY:clean clean: rm -rf $(ALL) $(OBJECT)
// main.cpp
#include <iostream> using namespace std; int main(){ cout << "Hello World!" << endl; #ifdef SETTING cout<<"setting = 1"<<endl; #else cout<<"setting = 0"<<endl; #endif return 0; }
也可以用传统的传参方法,传给main函数的argv。