聊一聊 make管理项目

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
CCC语言编译程序,缺省值CC
CPPC语言预处理程序,缺省值为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 dirmake开始运行之后的工作目录为指定目录。如果有多个-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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值