NMAKE参考之四——描述块

描述块

描述块是makefile文件的核心组成部分。下面是一个经典的NMAKE描述块:

target.lib : one.asm two.asm three.asm
    ML one.asm two.asm three.asm
    LIB target -+one.obj -+two.obj -+three.obj;

描述块中的第一行是依赖行。在本例中,依赖关系包含一个目标和三个依赖项。跟在依赖行后的是命令块,命令块中列了一条或多条命令。接下来的章节讨论依赖关系、目标和依赖项。(译注:原文中代码部分被设置成阴影了,这里从文中其它地方拷贝过来一个Makefile的例子)

依赖行

描述块的第一行是依赖行。依赖行指定了一个或多个目标以及零个或多个依赖文件。下面是一个依赖行的例子:

myapp.exe : myapp.obj another.obj myapp.def

上面的依赖行意思是,如果MYAPP.OBJ、ANOTHER.OB或是MYAPP.DEF文件比MYAPP.EXE的时间戳更新,NMAKE会重新生成目标文件MYAPP.EXE。

依赖行不能缩进(不能以空格或是制表符开始)。第一个目标必须放在依赖行的开始,目标文件和依赖文件之间由冒号隔开(下文中“目标在多个描述块中的使用”章节中描述的多目标除外)。冒号前后可以有零个或多个空格或是制表符。整个依赖关系必须放在一行中;不过你可以在目标或是依赖项后使用(\)符号来扩充当前行,然后在下一行中继续描述依赖。

在执行任何命令行之前,NMAKE先扫描所有依赖关系和推导规则生成一个“依赖树”,它包括完全更新目标所需要的所有步骤。NMAKE会检查各个依赖项自身是否还依赖于其它项(即依赖项是否是其它描述块中的目标项)。NMAKE完成依赖关系扫描后,它开始检查时间戳。如果依赖树中的任意一个依赖项比目标项更新,那么NMAKE会重新编译生成目标。

目标

依赖行的目标段列出了一个或多个目标的名字。必须至少指定一个目标。多个目标之间用一个或多个空格或是制表符隔开。你可以指定目标文件的路径。目标名字不区分大小写,但是一个目标(包含路径)名不能超过256个字符。

如果“:”前的目标文件是单个字符,那么在最后一个目标文件和“:”之间必须由空格分开,否则NMAKE会将它们解释成一个磁盘标识符。

通常目标是由描述块中的命令生成的文件名。当然,目标不但可以是一个有效的文件名,也可以是一个伪目标。(关于伪目标请参考下文相关章节)

NMAKE将通过命令行中指定生成目标。如果NMAME没有在命令行中指定,那么NMAKE将生成Makefile文件中第一个依赖关系的第一个目标。

以上一节中的示例代码为例,NMAKE首先查看MYAPP.EXE是否存在并且是最新的,否则会根据依赖关系和命令生成一个目标文件MYAPP.EXE。

多个描述块中的目标

如果用一个“:”区分目标项和依赖项,目标文件只能出现在一个描述块中。如果希望使用多个描述块更新一个目标项,那么需要使用“::”来区分目标和依赖项。这个功能的一种典型用法是当一个复杂目标由多个组件构成,而每个组件又由不同命令生成的时候。

例子

下面的Makefile可以用来生成一个库:

target.lib :: one.asm tow.asm three.asm
    ML one.asm two.asm three.asm
    LIB target -+one.obj -+two.obj -+three.obj;
target.lib :: four.c five.c
    CL /C four.c five.c
    LIB target -+four.obj -+five.obj    

如果有汇编文件被更新,NMAKE会使用汇编文件更新target.lib。同样的,如果任意一个C文件有改变,那么NMAKE会通过编译C文件来更新target.lib。

累计依赖项

如果在一个描述块重复同一个目标,则称之为累计依赖项。例如:

bounce.exe : jump.obj
bounce.exe : up.obj
    echo Building bounce.exe...

上面的例子会被NMAKE解释为:

bounce.exe : jump.obj up.obj
    echo Building bounce.exe...

这种写法有一些副作用。由于NMAKE在生成依赖树的时候,一次只能处理一个目标项,然而一行可以包括多个目标项,就像:

bounce.exe leap.exe : jump.obj
bounce.exe climb.exe : up.obj
    echo Building bounce.exe...

这个例子会被NMAKE处理成:

bounce.exe : jump.obj
leap.exe : jump.obj
bounce.exe : up.obj
climb.exe : up.obj...
    echo Building bounce.exe...

NMAKE为三个目标项依次建立依赖树,就像它们是在单独的描述块中指定的一样。如果bounce.exe或climb.exe过期了,NMAKE会执行给定的命令。如果leap.exe过期了,命令不会被执行,NMAKE会尝试使用推导规则。

如果目标在Makefile中在不同的两个地方使用单冒号指定的依赖关系,并且只有一个依赖行后有命令,那么NMAKE会将两行依赖合并成一行。不过这会有个副作用:NMAKE不会为没有命令的依赖行调用推导规则(详见“推导规则”一节),它会认为两个依赖行同属于一个描述块并执行另一个依赖项指定的命令。

下面的Makefile和前一个例子是完全一样的:

bounce.exe : jump.obj
    echo Building bounce.exe...
.
.
.
bounce.exe : up.obj

为了消除这个影响,可以使用双冒号(::)来分隔目标和依赖项。使用双冒号的描述块中如果没有命令块,NMAKE会自动调用推导规则,即使另一个使用双冒号的依赖关系中可能包含相同的目标,并且还提供了命令块也不例外。

伪目标

“伪目标”不是一个目标文件,它是包括了一组命令的一个标签。NMAKE将伪目标解释为一个不存在的文件,因此它永远是过期的状态(因为不会生成相应的目标文件,因此NMAKE每次检查时间戳都会认为它需要被更新)。所以每次NMAKE检查一个伪目标时,命令块总是会被重新执行一遍。必须确保伪目标的文件名在文件系统中没有被使用。

伪目标的名字同样也必须遵循文件名的语法规则。因此伪目标的名字是不区分大小写的。但是,如果伪目标名没有文件后缀(也就是说,它的名字没有使用“.”),它可以从8个字符的文件名限制扩展为256字符长度。

伪目标名也可以被放在描述块的依赖项中。这样的话它应该另有一个描述块说明如何生成它。所以当伪目标作为依赖项的时候,命令块可以为空。

伪目标文件有个默认的时间戳,当它作为依赖项出现的时候,它的时间戳是所有依赖项的时间戳中最新的(也就是说NMAKE每次检查伪目标的时间戳,它总是所有依赖项中最新的一个)。如果一个伪目标作为目标项出现,而且它没有依赖项的时候(只有一组命令),它的时间戳被认为是当前的时间。(因此在编译的时候这组命令总是会被执行一遍)

如果你想自动编译多个目标,伪目标是非常有用的。NMAKE一般情况下只会生成命令行指定的目标或是在默认情况下生成Makefile文件中第一个依赖关系的目标。如果想让NMAKE生成多个目标文件,又不想在命令行中一一指出它们的话,可以使用一个伪目标的描述块,并将你想编译的目标作为伪目标的依赖项。然后再将该描述块放在Makefile文件中的第一个依赖关系位置,这样NMAKE就可以自动生成多个目标了。(当然也可以在NMAKE命令行指定伪目标的名字)

示例1

下面的例子中,update是一个伪目标

UPDATE : *.*
    !COPY $** a:\product

在编译update的时候,NMAKE将拷贝当前目录下的所有文件到指定的磁盘目录中。

示例2

在下面的Makefile中,如果没有指定编译目标或是指定了all作为目标的话,NMAKE会依次编译project1.exe和project2.exe两个目标文件。setenv的功能是修改环境变量LIB(一般它应该在NMAKE all之前执行):

all : setenv project1.exe project2.exe

project1.exe : project1.obj
    LINK project1;

project2.exe : project2.obj
    LINK project2;

setenv :
    set LIB=\project\lib

依赖项

依赖行中列出了零到多个依赖项的名字。通常一个依赖项是一个用来生成目标的一个文件。不过一个依赖项既可以是有效的文件名,也可以是伪目标。当然你也包括指定文件路径。依赖项名字不区分大小写。每个依赖项之前有一个或多个空格或是制表符隔开。用一个或是两个冒号来分开目标和依赖项。

NMAKE可以为描述块产生一个推导依赖,它是由一套推倒规则继承而来的(更多详情请参照“推倒规则”一章)。NMAKE会在检查依赖项之前检查推导依赖。注意,推倒依赖就像一个被明确声明的依赖项一样,如果一个依赖关系中的推导依赖使目标项过期了,NMAKE将会调用依赖关系的命令块。

NMAKE使用依赖树确保在更新一个目标项时,其所有依赖项都被更新过(就像一种迭代的方式)。如果一个依赖项文件不存在,NMAKE会在查找其他描述块来尝试生成这个依赖项;如果文件存在,NMAKE会检查时间戳来确保它是最新的。

下面的例子中myapp.exe包括三个依赖项:

myapp.exe : myapp.obj another.obj myapp.def

依赖项的搜索路径

每个依赖项有一个可选的搜索路径,如以下所指定的:

{directoy[;directory...]}dependent

NMAKE 首先在当前目录中查找依赖项,然后按指定的顺序从其他目录中查找。宏可以指定部分或全部搜索路径。将目录名括在大括号 ({ }) 内;用分号 (;) 分隔多个目录。不允许使用空格或制表符。

示例下面的依赖行制定了一个搜索路径:

forward.exe : {\src\alpha;d:\proj}pass.obj

目标文件forward.exe有一个依赖项pass.obj。目录列指定了两个目录。NMAKE首先在当前目录搜索pass.obj,如果没有找到pass.obj,那么NMAKE搜索\src\alpha目录,然后是d:\proj目录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值