makefile小记v2

来源:互联网 发布:斗破沙城翅膀进阶数据 编辑:程序博客网 时间:2024/05/01 10:45

make

1.make概述

make工具用来自动判断一个大型程序的哪些部分需要重新编译以及实用哪些命令来编译。本文档介绍由Richard Stallman和Roland McGrath开发的GNU make,GNU make从v3.76开始由Paul D.Smith负责。GNU make符合IEEE1003.2-1992标准(POSIX.2标准)6.2条款。由于C语言的通用性,本文档以C语言为例介绍make,然而,你可以对任何支持命令行的语言编译器使用make。事实上,make不仅仅限于程序。你可以使用它来描述任何目标文件依赖于其他文件自动更新的任务。

准备

准备、运行make

为了使用make,你必须写一个描述你程序中使用文件依赖关系和更新每个文件的命令的makefile文件。对程序而言,典型地,可执行文件依赖于目标文件,目标文件通过编译源文件获得。当有一个合适的makefile文件时,在你每次更新一些源文件时,shell命令make用来执行所有必须得重编译。make使用makefile和文件的最终修改时间来确定哪些文件需要更新。对于每一个需要更新的文件,make出发makefile中对应的记录。当然,你可以提供命令行参数来控制make如何重编译文件。

2.makefile介绍

你需要一个名为makefile的文件来告诉make做什么。通常,makefile记录如何编译和链接程序。我们以如何编译、链接一个文本编辑器为例来说明如何写makefile。在这个简单的例子中,有8个源文件和3个头文件。makefile也可以告诉make工具如何运行显示提供的命令,比如删除某些文件等。当make重新编译时,每个更改过的源文件都需要重编译。如果头文件变化了,每个包含该头文件的源文件都需要重编译。每一个编译都产生一个对应的目标文件。最终,如果有任何源文件被重新编译了,不管是新添加的还是之前编译过的,都必须链接起来生成新的可执行文件。

2.1规则介绍

一个最简单的makefile包含如下形式的规则:

target … : prerequisites

        recipe

        …

        …

target通常是程序生成的文件名,比如可执行文件和目标文件,也可以是要执行的命令,比如clean等。prerequisites是用来产生输出的输入文件。recipe可能包含多个命令,一行一个命令或者一行多个命令,在每个命令前面你需要一个tab。如果你tab之外的其他前缀,你可以设置.RECIPEPREFIX变量来改变。一般而言,recipe有一定的前置条件并包含在一个规则之中,旨在当前置条件改变时创建target。然而,也可以没有前置条件,比如包含删除命令规则的clean就没有前置条件。规则表示何时、怎样标记目标规则的文件。make在prerequisites前置条件的基础上执行recipe来创建或者更新target。同样,rule也可以解释何时、怎样执行命令。除了rule外,makefile可能包含其他内容,然而一个最简单的makefile可以只包含rule。rule可能比这里介绍的要复杂得多,但是这里介绍的模式或多或少地适用。

2.1一个简单的makefile

下面是一个直观的描述了一个可执行文件edit依赖于8个目标文件,8个目标文件依赖于8个源文件和3个头文件的makefile。在这个例子中,所有的c文件都包含defs.h,只有定义编辑命令的源文件包含commang.h,只有改变编辑器缓冲区的低级文件包含buffer.h。

edit : main.o kbd.o command.o display.o \

       insert.o search.o files.o utils.o

        cc -o edit main.o kbd.o command.o display.o \

                   insert.o search.o files.o utils.o


main.o : main.c defs.h

        cc -c main.c

kbd.o : kbd.c defs.h command.h

        cc -c kbd.c

command.o : command.c defs.h command.h

        cc -c command.c

display.o : display.c defs.h buffer.h

        cc -c display.c

insert.o : insert.c defs.h buffer.h

        cc -c insert.c

search.o : search.c defs.h buffer.h

        cc -c search.c

files.o : files.c defs.h buffer.h command.h

        cc -c files.c

utils.o : utils.c defs.h

        cc -c utils.c

clean :

        rm edit main.o kbd.o command.o display.o \

           insert.o search.o files.o utils.o


我们用反斜杠或者换行来分割一个长的行,这就像使用一个长的行,但是更易读。为了适用这个makefile文件来生成名为edit的可执行文件,输入make即可。为了删除可执行文件和目标文件,输入make clean即可。在这个makefile文件中,target包括可执行文件edit,目标文件main.o和kdb.o。前置条件包括main.c和defs.h等。事实上,每一个.o文件既是一个target也是一个prerequisite。recipe包括cc -c main.c和cc -c kdb.c。

当target是一个文件时,如果前置条件有所改变,那么它需要被重新编译或者链接。在这个makefile中,edit依赖于每个目标文件,目标文件main.o依赖于源文件main.c和头文件defs.h。recipe可以在一行包含target和prerequisites。这些recipe表示怎样更新target文件。tab必须出现在recipe每行的头部来区别recipe和makefile文件中的其他内容。make对recipe的工作方式一无所知,其做的只是执行你指定的recipe。目标clean不是文件,仅仅是一个动作。正常情况下你不想执行这条动作,clean不是任何规则的前置条件。自然,除非你明确要clean,否则clean不会做任何事。clean不仅不是任何rule的前置条件,也不需要任何前置条件,clean的目的仅仅是执行某些recipe。仅仅执行命令的target称为phony target(伪目标)。

2.3make如何运行makefile

缺省情况下,make从第一个target开始(不是以.开始的target),这称为缺省目标。目标是make最终更新的target。你可以通过命令行参数或者.DEFAULT_GOAL变量改变这一目标。在上一部分提到的makefile中,缺省目标是更新可执行文件edit,因此我们把这条规则放在最前面。因此,当你输入命令make时,make读入当前目录的makefile文件,然后开始处理第一条规则。在这一例子中,rule是重新连接edit。在make可以顺利进行此rule之前,它必须进行edit依赖的规则,即目标文件。这些rule指示通过编译源文件来更新.o文件。当目标文件不存在或者前置条件如源文件和头文件较目标文件更新时,必须进行重编译。进行其他规则是因为他们的target是目标的前置条件。如果目标goal不依赖于某些rule,这些rule不会进行,除非明确指定make要进行,比如make clean。

在重编译目标文件之前,make更新它的前置条件即源文件和头文件。由于在这个makefile中,.c和.h不是任何rule的target,所以make不进行任何处理。make会更新自动生成的c程序,比如Bison和Y爱Yacc生成的c程序。

重编译需要的目标文件后,make决定是否重新连接edit。如果edit不存在或者目标文件比edit新,则进行重连接。

2.4在makefile中使用变量

在上面的makefile中,我们两次列出了可执行文件edit所需要的目标文件:


edit : main.o kbd.o command.o display.o \

              insert.o search.o files.o utils.o

        cc -o edit main.o kbd.o command.o display.o \

                   insert.o search.o files.o utils.o

这种重复很难排错。如果一个新的目标文件加入到系统中,我们可能只在一个地方加入而在另一个地方忘记加入。我们可以通过使用变量来消除这种风险并简化makefile。变量允许定义一次字符串,之后多次替代使用。为每一个makefile生命目标文件列表变量objects,OBJECTS,objs,OBJS,obj,OBJ是最佳实践。我们定义变量objects如下:

objects = main.o kbd.o command.o display.o \

          insert.o search.o files.o utils.o

有了变量objects之后,我们可以用S(objects)来替代每次需要目标文件列表的地方。下面是用变量替代目标文件列表之后的makefile:

objects = main.o kbd.o command.o display.o \

          insert.o search.o files.o utils.o


edit : $(objects)

        cc -o edit $(objects)

main.o : main.c defs.h

        cc -c main.c

kbd.o : kbd.c defs.h command.h

        cc -c kbd.c

command.o : command.c defs.h command.h

        cc -c command.c

display.o : display.c defs.h buffer.h

        cc -c display.c

insert.o : insert.c defs.h buffer.h

        cc -c insert.c

search.o : search.c defs.h buffer.h

        cc -c search.c

files.o : files.c defs.h buffer.h command.h

        cc -c files.c

utils.o : utils.c defs.h

        cc -c utils.c

clean :

        rm edit $(objects)

2.5make推导recipe

没有必要在编译单独源文件时声明所有的recipe,因为make可以自动推导。make包含了根据.c文件使用cc -c更新.o文件的隐式规则。当.c文件以这种方式使用时,它会被自动地添加到前置条件中,因此可以在前置条件中省略.c文件,如下:

objects = main.o kbd.o command.o display.o \

          insert.o search.o files.o utils.o


edit : $(objects)

        cc -o edit $(objects)


main.o : defs.h

kbd.o : defs.h command.h

command.o : defs.h command.h

display.o : defs.h buffer.h

insert.o : defs.h buffer.h

search.o : defs.h buffer.h

files.o : defs.h buffer.h command.h

utils.o : defs.h


.PHONY : clean

clean :

        rm edit $(objects)

这使我们在实践中书写makefile的常用方式。

2.6另一种样式的makefile

当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

2.7清除目录的规则

书写规则的目标不仅仅是编译程序。除此之外makefile可以指示如何做其他任务,比如如何删除目标文件和可执行文件等。一个简单的清除目录的规则如下:

clean:

        rm edit $(objects)

实际上,我们可能会以更复杂的方式来书写这一规则,如下:

.PHONY : clean

clean :

        -rm edit $(objects)


这防止了make在遇到错误时终止的情况。这种规则不应该放在makefile的头部,因为我们不想缺省运行它。因此,在上面个的makefile中,edit仍然是缺省的goal。clean不是edit的前置条件,当我们运行make命令的时候clean不会进行任何操作。为了运行clean规则,输入make clean。


3.开始写makefile

指示make如何重新编译一个系统的信息来自名为makefile的数据文件。

3.1makefile的内容

makefile包含5类内容:explicit rules,implicit rules,variable definitions,directives和comments。rules、variables和directives在后续章节介绍。

1.explicit rule是指何时、如何标记一个或者多个文件即规则的targets。它列出targets依赖的其他文件即target的prerequisites,并且可能会生命如何创建或者更新targets的recipe。

2.implicit rule是指根据名字确定何时、如何标记一类文件。它描述了一个target如何依赖于名字和它类似的其他文件并给出如何创建或者更新target的recipe。

3.variable definition是指为变量定义字符串常量以方便以后使用。上面的makefile定义了objects作为所有目标文件的列表。

4.directive 是指make在读取makefile的时候执行的特殊指令。包括:

(1)读入其他makefile。

(2)决定是否使用或者忽略Makefile的一部分。

(3)从跨行字符串定义变量。

5.makefile中得#是comment。包括#在内的该行其余部分都被忽略,除非一个反斜杠转义反斜杠导致下一行还是注释。仅仅包含注释的一行会被忽略。如果需要字符#,用反斜杠转义\#。注释可能出现在makefile的任何部分,尽管在某些情况下它们被特别对待。不能在变量引用和函数调用中使用注释:#会被视为字符除非作为注释的开头。recipe内的注释会传递到shell,就像recipe内的其他部分。shell决定如何解释它。在define directive中,变量定义的时候注释并没有被忽略,而是包含在变量的值中。当变量展开时,它们会根据上下文被make视为注释或者recipe的内容。


3.1.1分割长的行

在makefile基于行的语法中,新行标记了语句的结束。GNU make对一个语句行的长度没有限制,完全取决于计算机的内存。然而,一个很长的行很难阅读理解。可以通过在长行的中间添加新行来重新格式化你的语句:即用\分隔。

3.2makefile命名

缺省情况下,make按照如下顺序来查找makefile:GNUmakefile,makefile,Makefile。

通常,应该将其命名为makefile或者Makefile。你可以使用GNUmakefile如果你确定仅仅GNU make使用它,其他版本的make不使用。如果make没有找到任何名称,则make不使用任何makefile。你必须通过命令行参数提供一个goal,make会使用内置的implicit rules来推断如何标记它。

如果你想使用非标准的名称,可以指定通过-f或者—file来指定makefile的名称。

3.3包含其他makefile

include directive指示make在继续读取本makefile之前读入一个或者多个其他的makefile文件。该指令的形式如下:

include filenames

本行开头多余的空格会被忽略,然而第一个字符不能是tab。如果该行由tab开始,则会被视为recipe。如果你希望make忽略不存在的makefile或者不可用的makefile,可以使用如下形式的include:

-include filenames

出于不同版本make之间的兼容性,sinclude是-include的别名。

3.4可变makefiles

如果设置了环境变量MAKEFILES,make视其值为其他makefile的名字,这些makefile需要首先被读入,行为就像是include。

3.5makefiles怎样被重新生成

makefile可以通过RCS、SCCS等其他文件重新生成。如果makefile可以通过其他文件重新生成,你通常希望生成的makefile是最新版本。

3.6重写其他makefile的部分内容

保留两个makefile文件,通过include一个makefile文件来生成另一个makefile文件是一个不错的实践。比如:

foo:

        frobnicate > foo


%: force

        @$(MAKE) -f Makefile $@

force: ;

当你输入make foo时,make会搜索makefile,读入,运行frobnicate > foo 来生成foo。

%匹配任何target。

3.7make如何读取makefile

GNU make有两个阶段。第一阶段是读取所有的makefiles,初始化所有的变量、显式规则、隐式规则并在targets和prerequisites之间建立依赖图。第二阶段make使用内部结构决定哪些targets需要被重建,具体使用哪些rules。

变量赋值:

immediate = deferred

immediate ?= deferred

immediate := immediate

immediate ::= immediate

immediate += deferred or immediate

immediate != immediate


define immediate

  deferred

endef


define immediate =

  deferred

endef


define immediate ?=

  deferred

endef


define immediate :=

  immediate

endef


define immediate ::=

  immediate

endef


define immediate +=

  deferred or immediate

endef


define immediate !=

  immediate

endef


条件directives:

条件directives即时解析。


rule定义:

rule通常以一种形式展开,除非:

immediate : immediate ; deferred

        deferred

3.8二级展开

GNU make允许对某些或者全部定义在makefile中得targets进行prerequisites的二级扩展。为了允许二级扩展,.SECONDEXPANSION target必须在第一个prerequisite列表之前被定义。

explicit rules的二级展开

static pattern rules的二级展开

implicit rules 的二级展开


0 0
原创粉丝点击