windows下makefile教程.docx
- 文档编号:10649540
- 上传时间:2023-02-22
- 格式:DOCX
- 页数:15
- 大小:24.38KB
windows下makefile教程.docx
《windows下makefile教程.docx》由会员分享,可在线阅读,更多相关《windows下makefile教程.docx(15页珍藏版)》请在冰豆网上搜索。
windows下makefile教程
windows下的makefile教程
先说几句废话
以前看书时经常遇到makefile,nmake这几个名词,然后随之而来的就是一大段莫名其妙的代码,把我看得云里雾里的。
在图书馆和google上搜了半天,也只能找到一些零零星星的资料,把我一直郁闷得不行。
最近因缘巧合,被我搞到了一份传说中的MASM6手册,终于揭开了NMAKE的庐山真面目。
想到那些可能正遭受着同样苦难的同志以及那些看到E文就头晕的兄弟,所以就写了这篇文章。
假如大家觉得有帮助的话,记得回复一下,当作鼓励!
如果觉得很白痴,也请扔几个鸡蛋.本文是总结加翻译,对于一些关键词以及一些不是很确定的句子,保留了英文原版,然后再在括号里给出自己的理解以作参考。
由于水平有限,加上使用NMAKE的经验尚浅,有不对的地方大家记得要指正唷。
MASM6手册在AOGO(好像是)可以download,在我的BLOG上有到那的链接。
关于NMAKE
MicrosoftProgramMaintenanceUtility,外号NMAKE,顾名思义,是用来管理程序的工具。
其实说白了,就是一个解释程序。
它处理一种叫做makefile的文件(以mak为后缀),解释里面的语句并执行相应的指令。
我们编写makefile文件,按照规定的语法描述文件之间的依赖关系,以及与该依赖关系相关联的一系列操作。
然后在调用NMAKE时,它会检查所有相关的文件,如果目标文件(targetfile,下文简称target,即依赖于其它文件的文件)的timestamp(就是文件最后一次被修改的时间,一个32位数,表示距离1980年以来经过的时间,以2秒为单位)小于依赖文件(dependentfile,下文简称dependent,即被依赖的文件)的timestamp,NMAKE就执行与该依赖关系相关联的操作。
请看下面这个例子:
foo.exe:
first.objsecond.obj
linkfirst.obj,second.obj
第一行定义了依赖关系,称为dependencyline;第二行给出了与该依赖关系相关联的操作,称为commandline。
因为foo.exe由first.obj和second.obj连接而成,所以说foo.exe依赖于first.ogj和second.obj,即foo.exe为target,first.obj和second.obj为dependent。
如果first.obj和second.obj中的任何一个被修改了(其timestamp更大),则调用link.exe,重新连接生成foo.exe。
这就是NMAKE的执行逻辑。
综上,NMAKE的核心就是这3个家伙——依赖关系,操作和判定逻辑(target.timestamp MAKEFILE的语法 现在详细讨论一下makefile的语法。 makefile就像一个玩具型的程序语言,麻雀虽小,但五脏具全。 makefile的组成部分包括: 描述语句(descriptionblock),inferencerules(推导规则),宏和指令(directive)。 描述语句就是dependentlines和commandlines的组合;inferencerules就是预先定义好的或用户自己定义的依赖关系和关联命令;宏就不用说了吧;指令就是内定的一些可以被NMAKE识别的控制命令,提供了很多有用的功能。 另外,makefile中使用以下几个具有特殊意义的符号: ^#/(){}! @-: ;$ ^(caret): 用于关闭某些字符所具有的特殊意义,使其只表示字面上的意义。 例如: ^#abc表示#abc这个字符串,而#abc则用于在makefile中加入注释,#在这里为注释标志,就像C++中的//。 另外,在一行的末尾加上^,可以使行尾的回车换行符成为字串的一部分。 #(numbersign): 为注释标志,NMAKE会忽略所有从#开始到下一个换行符之间的所有文本。 这里要注意的是: 在commandlines中不能存在注释。 因为对于commandlines,NMAKE是将其整行传递给OS的。 通常对于commandlines的注释都是放在行与行之间。 /(backslash): 用于将两行合并为一行。 将其放在行尾,NMAKE就会将行尾的回车换行符解释为空格(space)。 %(percentsymbol): 表示其后的字符串为一文件名。 用法较复杂,在讲dependentlines的时候再详细讨论。 ! (exclamationsymbol): 命令修饰符,在下面会有详细的讨论。 @(atsign): 命令修饰符,在下面会有详细的讨论。 : (colon): 用于dependentlines和inferencerules中,用于分隔target和dependent。 ;(semicolon): 如果对于一个dependentline只有一条命令,则可以将该命令放在dependentline的后面,二者之间用“;”分隔。 $(dolorsign): 用于调用宏,在下面讲宏的时候再详细讨论。 在makefile中还可以使用DOS通配符(wildcard)来描述文件: *和? 。 作用相信大家都很熟悉了,在此就不再浪费口水了。 如果要将中间有空格或制表符的字符串作为整体对待,则应该用双引号将之括起来,例如,在指定一个中间有空格的长文件名的时候: “MyDocument” 或在定义一个宏的时候: MYMACRO=”copya: /foo.exec: /” 描述语句块(DescriptionBlocks) 描述语句块为makefile主体的基本组成单元,其典型结构如下: target: dependents commandsblock DependentLine 每一个描述语句块中只有一个dependentline,其定义了一个依赖关系。 该行的开头不能有任何空白(空格或制表符)。 冒号两边的target和dependent都可以有多个,之间以空格分隔。 NMAKE在分析makefile时首先会从头到尾扫描每一个dependentline,然后根据依赖关系建立起一棵依赖关系树(dependenttree)。 例如对于依赖关系: foo.exe: first.objsecond.obj first.obj: first.cpp second.obj: second.cpp 则在其依赖关系树中,foo.exe为first.obj和second.obj的父亲,而first.obj则是first.cpp的父亲,second.obj是second.cpp的父亲。 如果second.cpp被更新了,则second.obj会被重新构造,从而导致foo.exe被重新构造。 NMAKE就是这样由下而上地对整棵树中的结点进行评估的。 虽然makefile中可以有很多的dependentlines,但NMAKE只会构造出现在它的命令行中的targets,或者,如果命令行中没有给出targets,就构造第一个dependentline中的第一个target。 其他所有无关的targets都不会被构造。 例如: foo1.exefoo2.exe: first.obj first.obj: first.cpp second.obj: second.cpp 假设上面的第一行语句为makefile中出现的第一个dependentline,且命令行中没有给出target。 当first.cpp被更新后,first.obj和foo1.exe都会被重新构造,而foo2.exe和second.obj则不会。 当在一个dependentline中出现多个target时,例如: boy.exegirl.exe: first.obj echoHello 该语句相当于: boy.exe: first.obj echoHello girl.exe: first.obj echoHello (注: echo是一条控制台命令,用于在STDOUT上显示一行信息) 同一个target也可以出现在多个dependentlines中。 在这种情况下,如果只有一个dependentline后跟有commandline,则它们会被合并为一个描述语句块,例如: foo.exe: first.obj echoBuildingfoo.exe… … foo.exe: second.obj NMAKE会将其处理为: foo.exe: first.objsecond.obj echoBuildingfoo.exe… 如果每一个dependentline后都有commandline,则它们会被作为两个描述语句块处理。 如果在dependentline中使用双冒号(: : )来分隔target和dependent,并且同一个target出现在多个描述语句块中,此时,NMAKE将会匹配最合适的语句块,以构造该target。 例如: target.lib: : one.asmtwo.asmthree.asm MLone.asmtwo.asmthree.asm LIBtarget-+one.obj-+two.obj-+three.obj; target.lib: : four.cfive.c CL/cfour.cfive.c LIBtarget-+four.obj-+five.obj; Target.lib同时出现在两个描述语句块中,此时,NMAKE在处理该makefile时,将会选择其中一个描述语句块中的命令来执行。 如果任何asm文件被更新了,NMAKE就调用ML重新编译之,然后再调用LIB(但CL以及之后的命令都不会被调用);类似地,如果任何C文件被更新了,NMAKE就会调用CL。 在通常情况下,target和dependent都是文件名。 NMAKE会首先在当前目录下搜索dependent,如果没有找到,就到用户指定的目录下搜索。 指定搜索路径的语法如下: {directory1;directory2;…}dependent 搜索路径放在{}之中,如果有多个,就用“;”分开。 注意,在各个语法成分之间是不能有空白的。 Target和dependent也可以不是一个文件,而是一个标号(label)。 这时,就称之为pseudotarget(伪文件)。 Pseudotarget的名字不能与当前目录下的任何文件名相同。 一个pseudotarget如果要作为dependent,那么它必须要作为target出现在某个dependentline中。 当使用pseudotarget作为target时,与之关联的commandsblock一定会被执行,同时NMAKE会赋予它一个假想的timestamp。 该timestamp等于它的dependents中最大的timestamp,或者,如果它没有dependent,就等于当前时间。 该假想的timestamp在pseudotarget作为dependent时会被用来进行有效性评估。 这个特性最大的好处就是,你可以让NMAKE构造多个target,而不用将每个target都在NMAKE的命令行中列出来,例如: all: setenvproject1.exeproject2.exe project1.exe: project1.obj LINKproject1; project2.exe: project2.obj LINKproject2; setenv: setLIB=/project/lib 上例中有两个pseudotarget,一个是all,另一个是setenv。 首先是setenv被评估,其作用是设置环境变量LIB,然后project1.exe和project2.exe依次被更新. CommandsBlock 第二行开始到下一个dependentline之间为commandsblock,其给出了当dependents中的任何一个的timestamp大于target时,需要执行的指令序列(commadnsblock也可以为空,此时,NMAKE什么也不干)。 commandline必须以空白开头(刚好与dependentline相反,NMAKE就是通过该特征来分辨二者的),并且在dependentline和commandsblock中的第一条语句之间不能有空白行(就是除了一个换行符,什么也没有的行。 所以只有一个空格或制表符的行是合法的,此时NMAKE将其解释为一个nullcommand),但在commandlines之间可以有空白行。 Commandsblock中的每一条命令可以是在控制台中合法的任何命令。 事实上大可将commandsblock当成一个由控制台命令序列组成的批处理文件。 此外,对commandsblock中的命令,还可以在其前面添加一个或多个所谓的命令修饰符(commandmodifier),以实现对命令的一些额外的控制。 命令修饰符有以下3种: 1)@command 消除该命令的所有到STDOUT的输出。 2)–[number]command 关掉对该命令返回值的检测。 在默认的情况下,如果一条命令返回非0值,则NMAKE将会停止执行。 但如果在命令前加上一“-”,则NMAKE将会忽略该命令的返回值。 如果“-”紧接着一个整数,则NMAKE会忽略掉任何大于该整数的返回值。 3)! command 如果该命令的执行对象为$**或$? (这两个都是预定义的宏,前者表示相应的dependentline中所有的dependent,后者表示所有比target具有更大的timestamp的dependent),则该“! ”修饰符将会使该命令施行于这两个宏所描述的每一个独立的文件上。 NMAKE还提供了一些语法可以在commandsblock中表示相应的dependentline中第一个dependent的文件名组成。 例如: foo.exe: c: /sample/first.objc: /sample/second.obj link%s NMAKE将“link%s”解释为: linkc: /sample/first.obj 如果将命令改为“link%|pfF.exe”,则NMAKE将之解释为: linkc: /sample/first.exe %s表示全文件名,%|[part]F表示文件名中的某个部分,part可以是下列字符中的一个或多个,如果part为空,%|F与%s的意思相同: 1)d: 盘符; 2)p: 路径; 3)f: 文件基本名; 4)e: 文件扩展名; InferenceRules(推导规则) Inferencerules(下文简称IR)是一个模板,它用于决定如何从一个具有某种扩展名的文件构造出一个具有另一种扩展名的文件。 NMAKE通过IR来确定用来更新target的命令以及推导target的dependents。 IR的好处在于它满足了像我这样的懒人的需要。 只要提供了正确的IR,则描述语句块就可以极大地化简。 请看下面的例子: foo.obj: 上面的语句将会运作得很好。 是不是觉得很吃惊呢? 事实上,NMAKE在处理该语句的时候,它首先在当前目录下搜索基本名为foo的文件(假设当前目录下有一个foo.c文件)。 然后它查找一个后缀列表(suffixlist),里面的每一项包含了从一种类型的文件构造另一种类型的文件需要调用的命令和参数的相关信息。 在NMAKE预定义的列表中,foo.c到foo.obj的构造命令为CL。 最后NMAKE调用CL,编译foo.c。 呵呵,这么一长串的操作一条简单的语句就搞定了,是不是很方便呢! 当出现下列情况之一时,NMAKE就会尝试使用IR: lNMAKE遇到一个没有任何命令的描述语句块。 此时NMAKE就会搜索后缀列表,试图找到一个匹配的命令来构造target。 l无法找到某个dependent,并且该dependent没有作为target出现在其它dependentline中(即它不是一个pseudotarget)。 此时NMAKE就会搜索给定的目录以及后缀列表,试图找到一个IR来构造出该dependent。 l一个target没有dependent,并且描述语句块中没有给出指令。 此时NMAKE就会试图找出一个IR来构造出该target。 l一个target在NMAKE的命令行中给出,但在makefile里没有该target的相关信息(或根本就没有makefile)。 此时NMAKE就会试图找出一个IR来构造出该target。 定义一个IR的语法如下: [{frompath}].fromext[{topath}].toext; commands 注意,各语法元素之间不能有任何空格。 Dependent的后缀名在fromext中给出,target的后缀名在toext中给出。 Frompath和topath是可选的,分别给出了搜索的路径。 在每个IR的定义中只能分别为每一个后缀名给出一个搜索路径。 如果想要指定多个搜索路径,就必须定义多个IR。 并且,如果你为一个后缀指定了搜索路径,那么你也必须为另一个后缀指定搜索路径。 即是说,fromext和topath只要有一个存在,则另一个也必须存在。 你可以使用{.}或{}来表示当前目录。 另外,要注意的是,如果你在IR中指定了搜索路径,则在dependentlien中也必须指定同样的路径,否则IR将不会应用于dependentline上,例如: {../proj}.exe{../proj}.obj: 该IR不会用于下列语句上: project1.exe: project1.obj 但会用于下列语句上: {../proj}project1.exe: {../proj}project1.obj NMAKE本身提供了一个预定义的后缀列表,内容如下: RuleCommandDefaultAction .asm.exe$(AS)$(AFLAGS)$*.asmML$*.ASM .asm.obj$(AS)$(AFLAGS)/c$*.asmML/c$*.ASM .c.exe$(CC)$(CFLAGS)$*.cCL$*.C .c.obj$(CC)$(CFLAGS)/c$*.cCL/c$*.C .cpp.exe$(CPP)$(CPPFLAGS)$*.cppCL$*.CPP .cpp.obj$(CPP)$(CPPFLAGS)/c$*.cppCL/c$*.CPP .cxx.exe$(CXX)$(CXXFLAGS)$*.cxxCL$*.CXX .cxx.obj$(CXX)$(CXXFLAGS)/c$*.cxxCL/c$*.CXX .bas.obj$(BC)$(BFLAGS)$*.bas;BC$*.BAS; .cbl.exe$(COBOL)$(COBFLAGS)$*.cbl,$*.exe;COBOL$*.CBL,$*.EXE; .cbl.obj$(COBOL)$(COBFLAGS)$*.cbl;COBOL$*.CBL; .for.exe$(FOR)$(FFLAGS)$*.forFL$*.FOR .for.obj$(FOR)/c$(FFLAGS)$*.forFL/c$*.FOR .pas.exe$(PASCAL)$(PFLAGS)$*.pasPL$*.PAS .pas.obj$(PASCAL)/c$(PFLAGS)$*.pasPL/c$*.PAS .rc.res$(RC)$(RFLAGS)/r$*RC/r$* 在上表中,类似AFLAG和CFLAG这种被包含在括号里面的是未定义的宏,通过在makefile中对这些宏给出定义,可以为这些命令指定编译器和参数。 例如: $(AS)$(AFLAGS)$*.asm AS宏用于指定编译器,NMAKE中默认为ML;AFLAGS宏用于给出编译器参数,NMAKE将之留给用户定义,默认为空。 所以默认的操作为: ML$*.asm 这里可以看到将宏展开的语法,就是将宏的名字用圆括号括起来,然后在前面加上一个美元符号。 另外需要说明的是,”$*”是NMAKE预定义的一个特殊的宏,其等于target的路径加上target的基本名。 宏(MARCRO) 这个相信大家都十分熟悉了。 在makefile中通过使用宏将可以获得很大的灵活性。 下面就是在makefile中定义宏的语法: macroname=string 在makefile中,macroname是宏的名字,其可以是任何字母,数字和下划线的组合,最多可以有1024个字符。 另外要注意的是,macroname是大小写敏感的。 string是宏的定义体,可以有高达65510个字符。 任何包含0个字符或只包含空白的字符串都被视为空字串(nullstring),此时,该宏也被视为NULL,任何其出现的地方,都会被替换为空白。 在使用宏时,还应知道以下几个具有特殊意义的符号: l#用于注释,例如: command=ML#compileasmfile l/将宏定义分作多行来写,例如: LINKCMD=linkmyapp another,,NUL,mylib,myapp “/”后面的回车换行符会被空格替换,上面两行相当于: LINKCMD=linkmyappanother,,NUL,mylib,myapp l$将宏展开,用法在后面介绍。 l^如果要在宏中包含以上符号,但又不使用它们的特殊语义,则可以这样: dir=c: /windows^ 此时,dir相当于字符串”c: /windows/”。 以下是一些语法上的细节: 1)在定义宏时,宏名字的第一个字符必须是该行的第一个字符; 2)每行只能定义一个宏; 3)在”=”两边可以有空格,但它们都会被忽略; 4)在宏定义体中可以有空格,它们都会被视为宏的一部分; 除了可以在makefile中定义宏之外,宏定义也可以出现在NMAKE命令行中。 此时,如果在宏定义中有任何空白,则必须用双引号将之括起来,例如: NMAKE"LINKCMD=LINK/MAP" NMAKELINKCMD="LINK/MAP" 而像下面这样则是不允许的(等号两边
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- windows makefile 教程