make
Makefile文件是一个文本形式的数据库文件,其中包含的规则指名make编译哪些文件以及怎样编译这些文件。
一条规则包含3方面内容:
1:make要创建的文件(target);
2:编译目标文件所需的依赖文件列表(dependencies);
3:通过依赖文件创建目标文件所需执行的命令组(commands)。
Makefile 通用形式如下:
target:dependency file1 dependency file2 […]
command1
command2
[…]
注意:每一个命令行的首字符必须是Tab制表符,仅使用8个空格是不够的。除非特别指出,否则make的工作目录就是当前目录。
下面介绍一个简单的Makefile实例:
printer:printer.o shape.o op_lib.o
gcc -o printer printer.o shape.o op_lib.o
printer.o:printer.c printer.h shape.h op_lib.h
gcc -c printer.c
shape.o:shape.c shape.h
gcc -c shape.c
op_lib.o:op_lib.c op_lib.h
gcc -c op_lib.c
clean:
rm printer*.o
上面就是一个简单的实例,下面说一下编写的规则:
Makefile编写规则
之前提到过关于伪目标,在这里就在说一下伪目标吧。
伪目标
Makefile文件有普通目标(比如:printer:…等),也有伪目标,而伪目标呢,不像普通目标那样有依赖文件,伪目标没有对应实际的文件,比如clean就是伪目标,由于伪目标没有依赖文件,它不会自动执行,原因是什么呢,是:make在执行到目标clean时,make先检查它的依赖文件是否存在,由于clean没有依赖文件,make就认为该目标clean是最新版本,不需要重新创建。要想启动伪目标必须使用如下命令:make [virtual target],在上例中是执行make clean。
实际上可以使用特殊的make目标.PHONY,目标.PHONY的依赖文件含义与通常一样,但make不会检查是否存在它的依赖文件而直接执行.PHONY所对应规则的命令。
例如:
printer:printer.o shape.o op_lib.o
gcc -o printer printer.o shape.o op_lib.o
printer.o:printer.c printer.h shape.h op_lib.h
gcc -c printer.c
shape.o:shape.c shape.h
gcc -c shape.c
op_lib.o:op_lib.c op_lib.h
gcc -c op_lib.c
.PHONY:clean
clean:
rm printer*.o
变量
所谓变量就是用指定文本串在Makefile中定义的一个名字,Makefile中变量一般用大写,并用等号给它赋值。引用时只需用括号将变量名括起来并在括号前加上$符号。如:VARNAME=some_text
在编写大型的应用程序的Makefile时,其中涉及的依赖文件和规则繁多,如果使用变量表示某些依赖文件的路径,则会大大简化Makefile。一般在Makefile文件开始就定义文件所需要的所有变量,这样使Makefile文件清晰且便于修改。(注:#号在Makefile文件中表示后面的内容是注释,这个在Python和shell中是一样的),下面举个例子吧
一个简单的Makefile文件
OBJECT=printer.o shape.o op_lib.o
HEADER=printer.h shape.h op_lib.h
printer:$(OBJECT)
gcc -o printer $(OBJECT)
printer.o:printer.c $(HEADER)
gcc -c printer.c
shape.o:shape.c shape.h
gcc -c shape.c
op_lib.o:op_lib.c op_lib,h
gcc -c op_lib.c
.PHONY:clean
clean:
rm printer*.o
make变量
上面说的变量都是自定义变量,make管理项目也允许在Makefile中使用特殊变量,即make变量。make变量包括环境变量,自动变量和预定义变量。
这里说的环境变量就是系统环境变量,make命令执行时会读取系统环境变量并创建与其同名的变量,但是如果Makefile中有同名变量,则用户定义变量会覆盖系统的环境变量值。
make管理项目允许使用的自动变量全部以美元符号$开头,以下是部分自动变量:
$@ | Makefile文件中规则的目标文件名 |
---|---|
$< | Makefile文件中规则的目标第一个依赖文件名 |
$^ | Makefile文件中规则的目标所对应的所有依赖文件的列表,以空格分隔 |
$? | Makefile文件中规则的目标所对应的依赖文件中新于目标的文件列表,以空格分隔 |
$(@D) | Makefile文件中规则的目标文件的目录部分(如果目录在子目录中) |
$(@F) | Makefile文件中规则的目标文件的文件名部分(如果目标在子目录中) |
make管理项目支持的预定义变量主要用于定义程序名,以及传给这些程序的参数及标志值。
变量含义如下:
AR | 归档维护程序,缺省值为ar |
---|---|
AS | 汇编程序,缺省值为as |
CC | C语言编译程序,缺省值CC |
CPP | C语言预处理程序,缺省值为cpp |
RM | 文件删除程序,缺省值为rm -f |
ARFLAGS | 传给归档维护程序的标志,缺省值为rv |
ASFLAGS | 传给汇编程序的标志,无缺省值 |
CFLAGS | 传给C语言编译程序的标志,无缺省值 |
CPPFLAGS | 传给C语言预处理程序的标志,无缺省值 |
LDFLAGS | 传给连接程序的标志,无缺省值 |
隐式规则
我们上面说的Makefile规则都是用户自己定义的,其实make还有一系列隐式规则(或称为预定义规则)集。比如:
#a simple Makefile
OBJECT=printer.o shape.o op_lib.o
printer:$( OBJECT)
gcc -o printer $(OBJECT)
.PHONY:clean
clean:
rm printer*.o
缺省目标printer的依赖文件是printer.o shape.o op_lib.o,但在Makefile中并没有提及如何生成这些目标的规则,这时,make使用所谓的隐式规则,实际上,对每个名为somefile.o的目标文件,make先寻找与之对应的somefile.c文件,并用gcc -c somefile.c -o somefile.o 编译生成这个目标文件。
注意:如果在项目中使用多种语言时,不要使用隐式规则,因为使用隐式规则得到的结果可能预期结果不同。
模式规则
模式规则是指用户自定义的隐式规则。隐式规则和普通规则格式一致,但是目标和依赖文件必须带有符号%.该符号可以和任何非空字符串匹配。例如:%.o:%.c.实际上make已经对一些模式规则进行了定义,如:
%o:%c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
此规则中表示所有的Object文件都由C源码生成,使用该规则时,它利用自动变量 < 和 <和 <和@代替第一个依赖文件和Object文件,变量CC,CFLAGS,CPPFLAGS使用系统预定阈值。
make命令
建立Makefile文件后,就可以使用make命令生成和维护目标文件了。命令格式为:
make [options] [macrodef] [target]
选项options指定make的工作行为,选项macrodef(宏定义)指定执行Makefile时的宏值,目标(target)是要更新的文件列表。这些参数都是可选的,参数之间用空格隔开。
通过在命令行中指定make命令的选项(options),可以使make以不同方式运行。现在将一些常用选项列举如下:
-C dir | make开始运行之后的工作目录为指定目录。如果有多个-C,后面的dir指定的是相对于前一个的目录 ,如-C/ -C etc等价于-C /etc |
---|---|
-d | 打印除一般处理信息之外的调试信息,例如进行比较的文件的时间、真正被remake的文件等 |
-e | 不允许在makefile中对环境的宏赋新值 |
-f file | 使用指定的文件为makefile |
-i | 忽略运行makefile文件时命令行产生的错误,不退出make |
-I dir | 指定搜索被include的makefile目录。如果命令行中有多个-I选项,按出现的顺序依次搜索。与make的其他选项不同,允许dir紧跟在-I 之后(一般的选项和参数之间一定要加空格),这是为了与C预处理器兼容 |
-k | 执行命令出错时放弃当前的目标文件,尽可能地维护其他目标 |
-n | 按实际运行时的执行顺序显示命令,包括以@开头的命令,但不真正执行 |
-o file | 不维护指定文件,即使它比其依赖文件更旧。如果认为某个文件太陈旧了,没有必要在加以维护,可以使用该选项忽略该文件及与之的有关规则 |
-p | 显示makefile中所有宏定义和描述内部规则的规则,然后按一般情况执行。如果只想打印这些信息而不真正进行维护,可以使用make -p -f /dev/null |
-q | “问题模式”,如果指定的目标目前没有过期,就返回0,否则返回一个非零值,不运行任何命令或打印任何信息 |
-r | 忽略内部规则,同时清除缺省的后辍规则 |
-s | 执行但不显示执行的命令 |
-S | 执行makefile命令菜单时出错即退出make。这时make的默认工作方式,所以一般不必指定 |
-t | 修改每个目标文件的创建日期,但不真正重新创建文件 |
-v | 打印make的版本号,然后正常执行,如果希望只打印信息而不真正维护,使用make -v -f /dev/null |
宏
在make中使用宏,首先要定义宏,在makefile中引用宏的宏定义格式为:
宏名 赋值符号 宏值
宏名由用户指定,可以使用字母、数字、下划线(_)的任意组合,不过不能以数字开头,习惯上一般使用大写字母,并使用名字有意义,便于阅读和维护。
赋值符号有三种:
.= | 直接将后面的字符串赋给宏 |
---|---|
.:= | 后面跟字符串常量,将它的内容赋给宏 |
.+= | 宏原来的值加上一个空格,再加上后面的字符串,作为新的宏值 |
一般常用的是第一种。除了空格,赋值符号的前面不能有制表符或其他任何分隔符号,否则都会被make当作宏名的一部分,从而引起语法错误。
宏的引用有两种格式:
** $(宏名) ** 或 ** $ {宏名} **
当宏名只有单个字符时,可以省略括号,如 $A 就等于 $(A) .由于make将 $符号作为宏引用的开始,因此要表示 $符号需要用两个 $(即 $ $).
make处理时会先扫描一遍整个makefile,确定所有宏的值。因此注意宏的引用可以在定义之后,而且使用的是最后一次赋予的值。例如:
all:print1 print2
var1 = hello
print1: ;@echo $(var1)
var1 += world
print2: ;@echo $(var1)
则两次打印的都是hello world。
宏还允许嵌套使用,处理时依次展开。例如:
HEADFILE = myfile.h
HEADEFILE! = myfile2.h
INDEX = 1
现在引用$(HEADFILE $ (INDEX)),首先展开宏INDEX,得到 $( HEADFILE1),最后的结果是myfile2.h.
宏的定义可以出现在三个地方。一种是在makefile中定义,一种是在mde命令行中指明,一种是载入环境中的宏定义。在makefile中定义只需直接书写上面的定义式,也可以用前面介绍过的伪目标 . include 从其他文件中获得宏定义。在make命令行中定义时,应放在“属性”之后,“目标文件”之前。
CC=gcc
CFGLASS=-Wall -g -c -O2
all:server client
server:server.o
$(CC) $< -o $@
client:client.o
$(CC) $< -o $@
%.o:%.c
$(CC) $(CFGLASS) $< -o $@
.PHONY:
clean
clean:
$(RM) -rf *.o a.out server client