【转】Windows_makefile_03
【原创】Windows 下的 Makefile 编写(三)推理规则 - 看雪安全论坛.html
http://bbs.pediy.com/showthread.php?t=127038
Windows 下的 Makefile 编写(三)推理规则
作者:cntrump
推理规则是Makefile中自动化的核心功能之一,掌握了推理规则会让Makefile的编写更简单和更易维护。
推理规则
推理规则提供命令来更新目标并推理目标的依赖项。推理规则是用户自定义的或者是预定义的,预定义的推理规则可以被重新定义。
定义规则
.fromExt.toExt: commands
fromExt是依赖文件的扩展名,toExt是目标文件的扩展名。在依赖文件扩展名前面的 . 号必须被放在行首。
All:main.obj func.obj link $** main.obj:main.cpp cl /c main.cpp func.obj:func.cpp cl /c func.cpp
在应用了推理规则之后,可以简单地写为
All:main.obj func.obj link $** .cpp.obj: cl /c $<
在命令行下的输出结果如下:
F:\我的文章\hello>nmake Microsoft (R) Program Maintenance Utility Version 6.00.9782.0 Copyright (C) Microsoft Corp 1988-1998. All rights reserved. cl /c main.cpp Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. main.cpp cl /c func.cpp Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. func.cpp link main.obj func.obj Microsoft (R) Incremental Linker Version 6.00.8447 Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
由此可以看出来,推理规则已经从 .obj 目标文件自动推导出相应的 .cpp 文件,并且为每一个目标执行一次编译命令。
上述的推理规则叫做标准推理规则,标准推理规则会被调用多次。在文件数量很多的时候,这样显然会降低速度,为此在1.62及以后的版本中,还定义了批模式规则。
当 N 条命令通过推理规则时,批模式推理规则只调用一次该推理规则。如果没有批模式推理规则,将需要调用 N 条命令。N 是触发推理规则的依赖项的数目。
由于较低版本的nmake不支持批模式推理规则,所以在使用批模式规则时最好先检查一下当前nmake的版本:
_NMAKE_VER 宏可以返回当前nmake的文件版本,返回的是一个字符串文本。
!message $(_NMAKE_VER)
在VC6 SP6下的输出结果为:
6.00.9782.0
批模式和标准模式唯一的差异就是在目标后多加上一个冒号:
All:main.obj func.obj link $** .cpp.obj:: cl /c $<
执行后的输出结果如下:
cl /c main.cpp func.cpp Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. main.cpp func.cpp Generating Code... link main.obj func.obj Microsoft (R) Incremental Linker Version 6.00.8447 Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
不同的地方我标识了红色,可以看到编译命令只执行一次,并且一次编译多个文件。这样cl.exe 在编译期间只会被调用一次,相对于多次调用速度要快。
需要注意的一个地方是:由于一次处理多个文件,这就得要求执行命令的程序本身支持处理多个文件,假如cl.exe 一次只能处理单个源文件,那么使用批模式肯定会导致失败。
在上面的例子中,目标文件和依赖文件都没有指定路径,所以默认使用当前目录。如果目标文件或者依赖文件不在当前目录下,就需要为其指定搜索路径:
{fromDir}.fromExt{toDir}.toExt: command
fromDir是依赖文件所在目录,toDir是依赖文件所在目录。目录可以使用宏来表示。大括号是必须的,且每个扩展名只能指定一个目录路径,{}(空的大括号)表示当前目录。
例如,.obj文件被编译输出到Debug目录,源文件在当前目录下:
outdir=.\Debug objects=$(outdir)\main.obj \ $(outdir)\func.obj All:$(objects) link $** .cpp{$(outdir)}.obj:: cl /Fo"$(outdir)/" /c $<
在执行命令之前 Debug目录必须存在,否则编译命令将失败。命令成功执行后,会在Debug目录下生成 .obj文件,最终的可执行文件生成在当前目录下。
为了能更好的理解上面的推理规则,可以将上面的宏进行展开:
All:.\Debug\main.obj .\Debug\func.obj link $** .cpp{.\Debug}.obj:: cl /Fo".\Debug/" /c $<
最后需要说明的是,在批模式下,必须使用 $< 宏来指定依赖文件项目。
如果你理解了宏,预处理和推理规则,那么已经可以阅读大多数Windows下开源项目的Makefile文件了,并且已经可以自己写出实用的Makefile。
剩下的就是多加练习,试着为IDE向导生成的项目编写Makefile,多尝试几次之后你会发现原来Makefile很简单。*转载请注明来自看雪论坛@PEdiy.com