make是一个解释makefile中指令的命令工具。Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile
文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。 Makefile
里主要包含了五种类型的语句/行:显式规则、隐式规则、变量定义、文件指示和注释。 make命令格式:make [-f Makefile] [option] [target]
编译和链接规则
1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。- makefile的构成
(1)需要由make工具创建的目标体(target),通常是目标文件或可执行文件。
(2)要创建的目标体所依赖的文件(dependency_file)。
(3)创建每个目标体时需要运行的命令(command)。
格式如下: target:dependency_files command 在这里面,变量一般都是字符串,他有点像c语言的宏。
makefile中的文件指示,包含3部分,一个是在一个Makefie中引用另一个Makefile,就像c语言的include一样;另一个是根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。
注释:注释符用 “#”,可以用反斜框进行转义,如输入注释,“\#”。
- makefile的构成
makefile的书写
1)所有文件都在一个目录中
示例
st_work : main.o st_work.o fun.o
gcc main.o st_work.o fun.o -o
st_work main (命令以Tab开头) st_work.o : st_work.c
gcc -c st_work.c -o st_work
main.o : main.c st_work.h
gcc -c main.c -o main.o
fun.o : fun.c fun.h
gcc -c fun.c -o fun.o
clean:
rm -f st_work *.o
2)多目录的写法
我们这里,在工作目录下有4个文件夹 分别是 sources(源文件) obj (中间文件)headers(头文件) bin(目标文件)
sources里面有 main.c st_work.c fun.c
obj 里面最初没有文件
headers 里面有 fun.h st_work.h
最终目标取名为 st_work,它应存放到bin里面
预备知识:
gcc 的3个参数:
- -o 指定目标文件
gcc sources/main.c -o bin/main
- -c 编译的时候只生产目标文件不链接
gcc -c sources/main.c -o obj/main.o
- -I 主要指定头文件的搜索路径
gcc -I headers -c main.c -o main.o
- -l 指定静态库
gcc -lpthread …
示例
bin/st_work : obj/main.o obj/st_work.o obj/fun.o
gcc obj/main.o obj/st_work.o obj/fun.o -o bin/st_work (命令以Tab开头)
obj/st_work.o : sources/st_work.c
gcc -I headers -c sources/st_work.c -o obj/st_work.o
obj/main.o : sources/main.c
gcc -I headers -c sources/main.c -o obj/main.o
obj/fun.o : sources/fun.c
gcc -I headers -c sources/fun.c -o obj/fun.o
clean:
rm -f bin/st_work obj/*.o
3)隐式规则的引入
3个预定义变量介绍:
- $@ 表示要生成的目标
- $^ 表示全部的依赖文件
- $< 表示第一个依赖文件
bin/st_work : obj/main.o obj/st_work.o obj/fun.o
gcc $^ -o $@ (命令一定要用以Tab开头)
obj/st_work.o : sources/st_work.c
gcc -I headers -c$< -o $@
gcc -I headers -c $< -o $@
gcc -I headers -c $< -o $@
clean:
rm -f bin/st_work obj/*.o
4)变量的引入
变量的引入和应用:
CC=gcc
HD=-I headers
SC=-c $<
OBJ=-o $@ bin/st_work : obj/main.o obj/st_work.o obj/fun.o
gcc $^ -o $@ (命令一定要用以Tab开头)
obj/st_work.o : sources/st_work.c
$(CC) $(HD) $(SC) $(OBJ)
obj/main.o : sources/main.c
$(CC) $(HD) $(SC) $(OBJ)
obj/fun.o :sources/fun.c
$(CC) $(HD) $(SC) $(OBJ)
clean:
rm -f bin/st_workfile_o/*.o
可以看到,依赖关系由上到下。
More:
(1)如此定义变量后,
objects = main.o,
foo = $(bar)
使用$(object)来代替变量的值,用“$$”来表示$。
(2)变量替换
其格式是“$(var:a=b)”或是“${var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。
foo := a.o b.o c.o
bar := $(foo:.o=.c)
(3)变量可以嵌套
$($(x))
(4)可以使用“+=”操作符给变量追加值。
(5)当make嵌套调用时,上层Makefile中定义的变量会以系统环境变量的方式传递到下层的Makefile中。这里的系统环境变量与本makefile中定义的变量的关系有点类似于全局变量与局部变量的关系。默认情况下,只有通过命令行设置的变量会被传递。而定义在文件中的变量,如果要向下层Makefile传递,则需要使用exprot关键字来声明。
5)信息显示
在执行make时,会把所有这些信息都输出来。
在命令前面加个@,就不会把相关信息输出屏幕了。
6)函数的使用
函数调用后,函数的返回值可以当做变量来使用。
调用格式如下所示: $( ;)
函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。参数可以使用变量。
bar:= $(subst $(space),$(comma),$(foo))
上面的函数subst是把第三个参数中包含第一个参数中的字串替换成第二个参数中的字串。
7)语句的使用
$(if ;,;)
$(if ;,;,;)
make支持三各通配符:“*”,“?”和“[…]”。波浪号“~”字符在文件名中也有比较特殊的用途。如果是“~/test”,这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录
5、其他注意点
1)在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,通常要给中间目标文件打个包,在Windows 下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX 下,是Archive File,也就是 .a 文件。
编译阶段,主要解决语法问题,如函数,变量等声明是否正确,而链接阶段,因为要生成可执行文件了,要明确具体的地址关系,及寻找真实的定义等。
2)将长行用 “\” 分开便于阅读。
3)和目标没有依赖关系的规则不会被处理,除非指定make 处理(如make clean)。
4)将源文件分门别类地放置时,编译时要进行寻找。通过VPATH实现。
如果没有指明这个变量,make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
VPATH = src:../headers
多个目录使用:分开。
另外一个vpath,不是变量,使用方法如下:
它可以指定不同的文件在不同的搜索目录中。使用方法有三种:
(1)vpath 。为符合模式的文件指定搜索目录。
(2)vpath 。清除符合模式的文件的搜索目录。
(3)vpath。清除所有已被设置好了的文件搜索目录。
需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。指定了要搜索的文件集,而则指定了的文件集的搜索的目录。例如:
vpath %.h ../headers
该语句表示,要求make 在“../headers”目录下搜索所有以“.h”结尾的文件。
5)有些情况下,则规则失效,没有依赖文件,文件clean命令永远不会执行;为避免这个问题,可使用”.PHONY”指明该目标。如:
.PHONY : clean
这样,在make clean是会无视clean是否存在,直接执行其命令。
与上面等效的另外一个表达:
clean: FORCE
rm $(objects)
FORCE:
6)此外,还有很少使用的双冒号规则。
7)使用include关键字可以把别的Makefile包含进来。
8)objects = *.o
通配符同样可以用在变量中。并不是说[*.o]会展开,objects的值就是“*.o”。Makefile 中的变量其实就是 C/C++中的宏。如果你要让通配符在变量中展开,也就是让 objects 的值是所有[.o]的文件名的集合,那么,你可以这样:objects := $(wildcard *.o),这种用法由关键字“wildcard”指出。
9)通过makefile编译,自动推导,如果发现当前的文件没有做过更新,则不会编译,只编译更新过了的。
预定义变量选项
预定义变量 | 含义 |
---|---|
$* | 不包含扩展名的目标文件名称。 |
$+ | 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。 |
$< | 第一个依赖文件的名称。 |
$? | 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。 |
$@ | 目标的完整名称。 |
$^ | 所有的依赖文件,以空格分开,不包含重复的依赖文件。 |
$% | 如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称为 mytarget.so(image.o),则 $@ 为 mytarget.so,而 $% 为 image.o。 |
CC | C 编译器的名称,默认值为 cc。 |
CCFLAGS | C 编译器的选项。 |
CPP | C 预编译器的名称,默认值为 $(CC) -E。 |
CPPFLAGS | C 预编译的选项。 |
CXX | C++ 编译器的名称,默认值为 g++。 |
CXXFLAGS | C++ 编译器的选项。 |
AR | 归档维护程序的名称,默认值为 ar。 |
ARFLAGS | 归档维护程序的选项。 |
AS | 汇编程序的名称,默认值为 as。 |
ASFLAGS | 汇编程序的选项。 |
make选项
命令行选项 | 含义 |
---|---|
-C DIR | 在读取 makefile 之前改变到指定的目录 DIR。 |
-f FILE | 以指定的 FILE 文件作为 makefile。 |
-h | 显示所有的 make 选项。 |
-i | 忽略所有的命令执行错误。 |
-I DIR | 当包含其他 makefile 文件时,可利用该选项指定搜索目录。 |
-n | 只打印要执行的命令,但不执行这些命令。 |
-p | 显示 make 变量数据库和隐含规则。 |
-s | 在执行命令时不显示命令。 |
-w | 在处理 makefile 之前和之后,显示工作目录。 |
-W FILE | 假定文件 FILE 已经被修改。 |
示例代码:
CC=gcc
CXX=g++
CFLAGS= -g -Wall
INC=./***
LIB=./***.a
CFLAGS+= -DSNACC_DEEP_COPY-
DHAVE_VARIABLE_SIZED_AUTOMATIC_ARRAYS -Wno-deprecated -lpthread -lssl
TARGET=***
OBJ = **.o \
./**.o\
$(TARGET): $(OBJ)
$(CXX) $(CFLAGS) -o $@ $^ $(LIB)
如下代码,把所有的cpp文件编译成.o文件
%.o: %.cpp
$(CXX) $(CFLAGS) $(INC) -c -o $@ $<
clean:
rm -f *.o
rm -f $(TARGET)