作为一个新手,只能说,多学多看!
分类: LINUX
2015-07-02 09:28:49
Make 可自动决定一个大程序中哪些文件需要重新编译,并发布重新编译它们的命令
如果要使用Make,必须先写一个称为Makefile的文件,该文件描述程序中各个文件之间的相互关系,并且提供每一个文件的更新命令。在一个程序中,可执行程序文件的更新依靠OBJ文件,而OBJ文件是由源文件编译得来的。
一旦合适的Makefile文件存在,每次更改一些源文件,在shell命令下简单的键入:
make
就能执行所有的必要的重新编译任务。Make程序根据Makefile文件中的数据和每个文件更改的时间戳决定哪些文件需要更新。对于这些需要更新的文件,Make基于Makefile文件发布命令进行更新,进行更新的方式由提供的命令行参数控制。
2.makefile的规则(一段,称为一条规则)
目标(target)…:依赖(prerequiries)…
<tab>命令(command)
…
…
目标(target)通常是要产生的文件的名称,目标的例子是可执行文件或OBJ文件。目标也可是一个执行的动作名称,诸如‘clean’
依赖是用来输入从而产生目标的文件,一个目标经常有几个依赖。
命令是Make执行的动作,一个规则可以含有几个命令,每个命令占一行。注意:每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。
有依赖的才是文件,没有依赖的称为 动作,make后面要加动作名,才会执行,不然不执行
缺省情况,也就是默认情况下
makefile中的第一个规则的第一个目标,一般情况下就是缺省目标
缺省情况下,make开始于第一个目标(假想目标的名称前带‘.’)。这个目标称为缺省最终目标(即make最终更新的目标
4.总结 make处理makefile的方式,就是以缺省目标为最终目标,处理所有它依赖目标的规则,其余不理会,除非
make clean
在上节的简单例子中,缺省最终目标是更新可执行文件‘edit’,所以我们将该规则设为第一规则。这样,一旦您给出命令:
make
make就会读当前目录下的makefile文件,并开始处理第一条规则。在本例中,第一条规则是连接生成‘edit’,但
在make全部完成本规则工作之前,必须先处理‘edit’所依靠的OBJ文件。
这些OBJ文件按照各自的规则被处理更新,每个OBJ文件的更新规则是编译其源文件。重新编译根据其依靠的源文件或头文件是否比现存的OBJ文件更‘新’,或者OBJ文件是否存在来判断。
如此,就能在大量修改后,一次性做到重新编译
其它规则的处理根据它们的目标是否和缺省最终目标的依赖相关联来判断。如果一些规则和缺省最终目标无任何关联则这些规则不会被执行,除非告诉Make强制执行(如输入执行make clean命令)。
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
然后,在每一个需要列举OBJ文件的地方,我们使用写为
`$(objects)'形式的变量代替
6.另一种风格的makefile文件
当时在makefile文件中使用隐含规则创建OBJ文件时,采用另一种风格的makefile文件也是可行的。在这种风格的makefile文件中,可以依据依赖分组代替依据目标分组。下面是采用这种风格的makefile文件:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
这里的defs.h是所有OBJ文件的共同的一个依赖;command.h和bufffer.h是具体列出的OBJ文件的共同依赖。
7.缺省情况下,当make寻找makefile文件时,它试图搜寻具有如下的名字的文件,按顺序:‘GNUmakefile’、‘makefile’和‘Makefile’。
通常情况下您应该把您的makefile文件命名为‘makefile’或‘Makefile’。
如果您使用非标准名字makefile文件,您可以使用‘-f’或‘--file’参数指定您的makefile文件。参数‘-f name’或‘--file=name’能够告诉make读名字为‘name’的文件作为makefile文件。如果您使用 ‘-f’或‘--file’参数多于一个,意味着您指定了多个makefile文件,所有的makefile文件按具体的顺序发生作用。一旦您使用了‘-f’或‘--file’参数,将不再自动检查是否存在名为‘GNUmakefile’、‘makefile’或‘Makefile’的makefile文件。
8.包含其它的makefile文件
include指令告诉make暂停读取当前的makefile文件,先读完include指令指定的makefile文件后再继续。指令在makefile文件占单独一行,其格式如下:
include filenames...
filenames可以包含shell文件名的格式。
include foo *.mk $(bar) *就是所有的意思
和‘include foo a.mk b.mk c.mk bish bash’等价。
9.make在编译的时候,如果命令当中没有指定 gcc编译出来的文件的名字,那么,它是不会遵循target,而是顺延被编译的源文件的名字,改个后缀而已
10.通配符例子
通配符可以用在规则的命令中,此时通配符由shell扩展。例如,下面的规则删除所有OBJ文件:
clean:
rm –f *.o
通配符在规则的依赖中也很有用。在下面的makefile规则中,‘make print’将打印所有从上次您打印以后又有改动的‘.c’文件:
print: *.c
lpr -p $?
touch print
本规则使用‘ptint’作为一个空目标文件(参看使用空目标文件记录事件);自动变量‘$?’用来打印那些已经修改的文件,参看自动变量。
当您定义一个变量时通配符不会扩展,如果您这样写:
objects = *.o
变量objects的值实际就是字符串‘*.o’。然而,如果您在一个目标、依赖和命令中使用变量objects的值,通配符将在那时扩展。使用下面的语句可使通配符扩展:
objects=$(wildcard *.o)
11.wildcard 可以加目录
在规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数“wildcard”,它的用法是:$(wildcard PATTERN...)。
使用函数wildcard得到指定目录下所有的C语言源程序文件名的命令格式为:
$(wildcard *.c)
SRC = $(wildcard ${DIR_SRC}/*.c)
12.patsubst 可以加目录 为毛我写的test1的这东西,。c后面要有个空格啊。。。。。
我们可以把所获得的C语言源程序文件名的字符串通过将‘.c’后缀变为‘.o’转换为OBJ文件名的字符串,其格式为:
$(patsubst %.c,%.o,$(wildcard *.c))
这里我们使用了另外一个函数:patsubst,详细内容参阅字符串替换和分析函数。
这样,一个编译特定目录下所有C语言源程序并把它们连接在一起的makefile文件可以写成如下格式:
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
13.一个完整个简单的,多目录并生产到指定目录的程序
实际当中程序文件比较大,这时候对文件进行分类,分为头文件、源文件、目标文件、可执行文件。也就是说通常将文件按照文件类型放在不同的目录当中,这个时候的Makefile需要统一管理这些文件,将生产的目标文件放在目标目录下,可执行文件放到可执行目录下
1 DIR_INC = ./include 2 DIR_SRC = ./src 3 DIR_OBJ = ./obj 4 DIR_BIN = ./bin 5 6 SRC = $(wildcard ${DIR_SRC}/*.c) 7OBJ = $(patsubst %.c,${DIR_OBJ}/%.o,$(notdir ${SRC})) 8 9TARGET = main 1011BIN_TARGET = ${DIR_BIN}/${TARGET} 1213CC = gcc 14CFLAGS = -g -Wall -I${DIR_INC} 1516${BIN_TARGET}:${OBJ} 17 $(CC) $(OBJ) -o $@ 1819${DIR_OBJ}/%.o:${DIR_SRC}/%.c 20 $(CC) $(CFLAGS) -c $< -o $@ 21.PHONY:clean 22clean: 23 find ${DIR_OBJ} -name *.o -exec rm -rf {}
通配符在规则的依赖中也很有用。在下面的makefile规则中,‘make print’将打印所有从上次您打印以后又有改动的‘.c’文件:
print: *.c
lpr -p $?
touch print
本规则使用‘ptint’作为一个空目标文件(参看使用空目标文件记录事件);自动变量‘$?’用来打印那些已经修改的文件,
自动变量‘$<’的值是第一个依赖
15.目前发现的问题是:虽然在vpath中加入了目标目录,在依赖的时候能够找到,但是命令行中,gcc不承认,还得再敲一次目录地址。。。。。。
但是,make具有相当的自主性,所以像这种简单的 ,直接可以
vpath %.c Dir1
main:SymbolGet.o Mymain.o
cc -o main SymbolGet.o Mymain.o
SymbolGet.o:SymbolGet.c
Mymain.o:Mymain.c
编译单独的C语言源程序并不需要写出命令,因为make可以把它推断出来:make有一个使用‘CC –c’命令的把C语言源程序编译更新为相同文件名的OBJ文件的隐含规则。
16.假想目标
假想目标并不是一个真正的文件名,它仅仅是您制定的一个具体规则所执行的一些命令的名称。使用假想目标有两个原因:避免和具有相同名称的文件冲突和改善性能。
假想目标能够终止任何在目录下创建名为‘clean’的文件工作。但如在目录下存在文件clean,因为该目标clean没有依赖,所以文件clean始终会认为已经该更新,因此它的命令将永不会执行。为了避免这种情况,您应该使用象如下特别的.PHONY目标格式将该目标具体的声明为一个假想目标:
.PHONY : clean
一旦这样声明,‘make clean’命令无论目录下是否存在名为‘clean’的文件,该目标的命令都会执行。
17.Makefile中的 符号 $@, $^, $< 的意思:
$@ 表示目标文件
$^ 表示所有的依赖文件
$< 表示第一个依赖文件
$? 表示比目标还要新的依赖文件列表
18.
lpr -p $? # $?列出比目标文件(print)更新的所有依赖文件,并由lpr命令提交给打印机
|
19。静态格式规则 (适用于多目标的规则)
静态格式规则是指定多个目标并能够根据每个目标名构造对应的依赖名的规则。静态格式规则在用于多个目标时比平常的规则更常用,因为目标可以不必有完全相同的依赖;也就是说,这些目标的依赖必须类似,但不必完全相同。
静态格式规则和定义为格式规则的隐含规则有很多相同的地方(详细参阅定义与重新定义格式规则)。双方都有对目标的格式和构造依赖名称的格式,差异是make使用它们的时机不同。
隐含规则可以应用于任何于它匹配的目标,但它仅仅是在目标没有具体规则指定命令以及依赖可以被搜寻到的情况下应用。如果有多条隐含规则适合,仅有执行其中一条规则,选择依据隐含规则的定义次序。
相反,静态格式规则用于在规则中指明的目标。它不能应用于其它任何目标,并且它的使用方式对于各个目标是固定不变的。如果使用两个带有命令的规则发生冲突,则是错误。
静态格式规则因为如下原因可能比隐含规则更好:
l 对一些文件名不能按句法分类的但可以给出列表的文件,使用静态格式规则可以重载隐含规则链。
l 如果不能精确确定使用的路径,您不能确定一些无关紧要的文件是否导致make使用错误的隐含规则(因为隐含规则的选择根据其定义次序)。使用静态格式规则则没有这些不确定因素:每一条规则都精确的用于指定的目标上。
20.在规则中使用命令
规则中的命令由一系列shell命令行组成,它们一条一条的按顺序执行。除第一条命令行可以分号为开始附属在目标-依赖行后面外,所有的命令行必须以TAB开始。空白行与注释行可在命令行中间出现,处理时它们被忽略。(但是必须注意,以TAB开始的‘空白行’不是空白行,它是空命令,参阅使用空命令。)
用户使用多种不同的shell程序,如果在makefile文件中没有指明其它的shell,则使用缺省的‘/bin/sh’解释makefile文件中的命令。参阅命令执行。
使用的shell种类决定了是否能够在命令行上写注释以及编写注释使用的语法。当使用‘/bin/sh’作为shell,以‘#’开始的注释一直延伸到该行结束。‘#’不必在行首,而且‘#’不是注释的一部分。
21.命令回显
正常情况下make在执行命令之前首先打印命令行,我们因这样可将您编写的命令原样输出故称此为回显。
以‘@’起始的行不能回显,‘@’在传输给shell时被丢弃。典型的情况,您可以在makefile文件中使用一个仅仅用于打印某些内容的命令,如echo命令来显示makefile文件执行的进程:
@echo About to make distribution files
make时给出‘-n’或‘--just-print’标志,则仅仅回显命令而不执行命令
‘-s’或‘--silent’标志可以使make阻止所有命令回显,好像所有的行都以‘@’开始一样
22.make读取makefile文件的过程
GNU make把它的工作明显的分为两个阶段。在第一阶段,make读取makefile文件,包括makefile文件本身、内置变量及其值、隐含规则和具体规则、构造所有目标的依靠图表和它们的依赖等。在第二阶段,make使用这些内置的组织决定需要重新构造的目标以及使用必要的规则进行工作。
23.执行命令
需要执行命令更新目标时,每一命令行都会使用一个独立的子shell环境,保证该命令行得到执行。(实际上,make可能走不影响结果的捷径。)
请注意:这意味着设置局部变量的shell命令如cd等将不影响紧跟着的命令行;如果您需要使用cd命令影响到下一个命令,请把这两个命令放到一行,它们中间用分号隔开,这样make将认为它们是一个单一的命令行,把它们放到一起传递给shell,然后按顺序执行它们
24。并行执行
GNU make可以同时执行几条命令。正常情况下,make一次执行一个命令,待它完成后在执行下一条命令。然而,使用‘-j’和‘--jobs’选项将告诉make同时执行多条命令。在MS-DOS上,‘-j’选项没有作用,因为该系统不支持多进程处理。
如果‘-j’选项后面跟一个整数,该整数表示一次执行的命令的条数;这称为job slots数。如果‘-j’选项后面没有整数,也就是没有对job slots的数目限制。缺省的job slots数是一,这意味着按顺序执行(一次执行一条命令)。同时执行多条命令的一个不太理想的结果是每条命令产生的输出与每条命令发送的时间对应,即命令产生的消息回显可能较为混乱。
25.命令错误
要忽略一个命令执行产生的错误,请使用字符‘-’(在初始化TAB的后面)作为该命令行的开始。字符‘-’在命令传递给shell执行时丢弃。例如:
clean:
-rm -f *.o
这条命令即使在不能删除一个文件时也强制rm继续执行。
26.递归调用make
递归调用意味着可以在makefile文件中将make作为一个命令使用。这种技术在包含大的系统中把makefile分离为各种各样的子系统时非常有用。例如,假设您有一个子目录‘subdir’,该目录中有它自己的makefile文件,您希望在该子目录中运行make时使用该makefile文件,则您可以按下述方式编写:
subsystem:
cd subdir && $(MAKE)
或, 等同于这样写 (参阅选项概要):
subsystem:
$(MAKE) -C subdir
您可以仅仅拷贝上述例子实现make的递归调用,但您应该了解它们是如何工作的,它们为什么这样工作,以及子make和上层make的相互关系。
为了使用方便,GNU make把变量CURDIR的值设置为当前工作的路径。如果‘-C’选项有效,它将包含的是新路径,而不是原来的路径。该值和它在makefile中设置的值有相同的优先权(缺省情况下,环境变量CURDIR不能重载)。注意,操作make时设置该值无效。
变量MAKE的工作方式:
27。变量引用基础
写一个美元符号后跟用圆括号或大括号括住变量名则可引用变量的值:‘$(foo)' 和 ‘${foo}'都是对变量‘foo’的有效引用。
在GNU make中可以使用两种方式为变量赋值,我们将这两种方式称为变量的两个特色(two flavors)。两个特色的区别在于它们的定义方式和扩展时的方式不同。
1)变量的第一个特色是递归调用扩展型变量。这种类型的变量定义方式:在命令行中使用‘=’定义(参阅设置变量)或使用define指令定义(参阅定义多行变量)。变量替换对于您所指定的值是逐字进行替换的;如果它包含对其它变量的引用,这些引用在该变量替换时(或在扩展为其它字符串的过程中)才被扩展。这种扩展方式称为递归调用型扩展
简单扩展型变量在命令行中用‘:=’定义(参阅设置变量)。简单扩展型变量的值是一次扫描永远使用,对于引用的其它变量和函数在定义的时候就已经展开。简单扩展型变量的值实际就是您写的文本扩展的结果。因此它不包含任何对其它变量的引用;在该变量定义时就包含了它们的值。
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
1、“=”
make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:
x = foo
y = $(x) bar
x = xyz
在上例中,y的值将会是 xyz bar ,而不是 foo bar 。
2、“:=”
“:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。
x := foo
y := $(x) bar
x := xyz
在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。
28.变量引用高级技术
替换引用是用您指定的变量替换一个变量的值。它的形式‘$(var:a=b)’(或‘${var:a=b}’),它的含义是把变量var的值中的每一个字结尾的a用b替换。
嵌套变量引用(计算的变量名)是一个复杂的概念,仅仅在十分复杂的makefile程序中使用。绝大多数情况您不必考虑它们,仅仅知道创建名字中含有美元标志的变量可能有奇特的结果就足够了。然而,如果您是要把一切搞明白的人或您实在对它们如何工作有兴趣,请认真阅读以下内容。
变量可以在它的名字中引用其它变量,这称为嵌套变量引用(计算的变量名)。$($(x))多个$
3)为变量值追加文本(如果初始赋值是用的=,那么+=也是默认的=,所以,全文本此变量都是一个值。。。。)
为已经定以过的变量的值追加更多的文本一般比较有用。您可以在独立行中使用‘+=’来实现上述设想。如:
objects += another.o
这为变量objects的值添加了文本‘another.o’(其前面有一个前导空格)。这样:
objects = main.o foo.o bar.o utils.o
objects += another.o
变量objects
设置为‘main.o
foo.o bar.o utils.o another.o'。
使用 `+=' 相同于:
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o
如果一个变量设置时使用了命令参数(参阅变量重载),那么在makefile文件中通常的对该变量赋值不会生效。此时对该变量进行设置,您需要使用override指令,其格式如下:
override variable = value
或 override variable := valueoverride指令不是打算扩大makefile和命令参数冲突,而是希望用它您可以改变和追加哪些设置时使用了命令参数的变量的值。
例如,假设您在运行C编译器时总是使用‘-g’开关,但您允许用户像往常一样使用命令参数指定其它开关,您就可以使用override指令:
override CFLAGS += -g
31.生成多个可执行文件30.定义多行变量
设置变量值的另一种方法时使用define指令。这个指令有一个特殊的用法,既可以定义包含多行字符的变量。这使得定义命令的固定次序十分方便(参阅定义固定次序命令)。
在define指令同一行的后面一般是变量名,当然,也可以什么也没有。变量的值由下面的几行给出,值的结束由仅仅包含endef的一行标示出。除了上述在语法上的不同之外,define指令象‘=’一样工作:它创建了一个递归调用型变量(参阅变量的两个特色)。变量的名字可以包括函数调用和变量引用,它们在指令读入时扩展,以便能够计算出实际的变量名。
define two-lines
echo foo
echo $(bar)
endef
变量的值在通常的赋值语句中只能在一行中完成,但在define指令中在define指令行以后endef行之前中间所有的行都是变量值的一部分(最后一行除外,因为标示endef那一行不能认为是变量值的一部分)。前面的例子功能上等同于:
two-lines = echo foo; echo $(bar)
如果您希望使用define指令的变量定义比使用命令行定义的变量优先,您可以把define指令和override指令一块使用:
override define two-lines
foo
$(bar)
endef
|
32。规则里的每条命令都有单独的shell环境,所以相互不影响,如果想要上一条命令能影响下一条的话,那就需要写在同一行上,以分号隔开,命令过长可以 用 \ 断开。
33.pwd 是 打印当前目录的关键字
34.在makefile中执行可执行文件,当你ctr+c中断退出的时候,当前目标如果生成了文件,文件会被自动删除,打印“目标文件”deleteing,如果没有,也会自动打印 “目标”interrupt
35.对于一些不生成文件的规则,也就是说 目标文件不存,就不存在与依赖文件的时间戳比较,默认不存在,也就是没产生,也就是一直会执行(如果轮得到它的话......)
36.在makefile中,会有一些预定义变量,也就是提供给广大同行们的的一个准则,用不用都无所谓(最好用一下)
比如CFLAGS,是编译器选项,往里面填东西就行了,
还有 LDFLAGS ,告诉链接器从哪里寻找库文件,flag,是标记的意思
|