freeeyes 发表于 2012-7-14 17:10:25

MPC自动生成make文件普及贴

MPC使用介绍(一)

学习ACE也是很久以前的事情了,但是一直没有接触到MPC,最近学习CORBA,使用了TAO作为CORBA库,在使用的过程中简单接触了一下MPC,但是由于MPC的相关文档都是英文的,对于文不好的人是一种折磨(包括本人),因此有了翻译一个MPC文档的想法,原本在早前是看到一个更相信的MPC帮助文档的,但是学习的时候却怎么也找不到了(如果有谁找到了,请告诉我,谢谢),因此我翻译的是基于1.4a的(原文在http://downloads.ociweb.com/MPC/html/MakeProjectCreator.html),如果有什么不足之处,欢迎大家指出来。同时也欢迎大家转载,不过希望能注明出处ACE开发者。



1. MPC介绍

MPC是一个用来从mpc文件中生成不同编译工具所支持的项目文件的工具。MPC项目文件是构造单一编译目标的源文件的集合。MPC使用平台特定的输入和mpc文件一起生成不同编译工具所支持的项目文件,如:makefile, Visual C++工作区和项目文件,Visual Studio解决方案和项目文件,等。MPC同特定的项目文件相比有如下优点:能够最大限度的减少需要维护的项目文件数量;同时支持项目继承;语法简单,易于维护;等。


2. 使用MPC

一个MPC项目由一系列描述不同编译目标(如库和可执行文件)的参数集合,这些参数包括目标名称、包含路径、源文件、头文件等。一个或多个MPC项目可以定义在一个单独的mpc文件中。而MPC工作区是一系列项目的集合。
项目可以通过mpc.pl脚本来生成,一个或多个mpc文件可以通过命令行选项来传递到mpc.pl脚本进行处理。如果没有mpc文件被指定,则脚本会自动搜索项目相关文件(如源文件、头文件等)并组合成一个项目。
下图显示了如何通过mpc.pl生成项目文件:
(本来想上传附件的,结果没法上传,请大家参考原文图片)

如果需要生成工作区,则必须使用mwc.pl脚本来生成。该脚本会通过mpc文件生成相应的项目并基于这些项目创建一个工作区。如果没有mwc文件传递到脚本,则脚本会在当前目录以及所有子目录下搜索所有的mpc文件来组合成一个工作区。对于基于make的项目类型(如:make、gnumake、bmake、nmake),工作区是一个顶层的makefile;而对于如Visual Studio一样的基于GUI的工具,工作区则是集合了所有项目的一个顶层工作区文件。
下图显示了如何通过mwc.pl生成工作区文件:
(本来想上传附件的,结果没法上传,请大家参考原文图片)

2.1 支持的生成工具

MPC可以生成多个生产工具所支持的项目和工作区。表1列出了MPC支持的类型(由MPC和MWC的-type命令行选择指定)以及对应的生成工具(原文与此有些区别,我使用的是ACE 5.6中支持的类型,?表示不确定,空表示不清楚,有知道的请告诉我,谢谢):

表1. 支持的生成工具
类型生成工具
automakeGNU Automake
bcb2007Borland C++ Builder ?
bds4
bmakeBorland Make
borland
ccCC/GCC ?
em3Microsoft eMbedded Visual C++ 3.0 & 4.0
ghsGreen Hills C++ Builder
gnuaceGNU Make for ACE/TAO only (ACE扩展)
gnuautobuildGNU Auto Build
htmlHTML ?
make普通make,通过该模式生成的makefile可以用于各种make工具,但是,因为配置原因,不能同ACE/TAO扩展一起使用。

nmake Microsoft NMake sle Visual SlickEdit vc6 Visual C++ 6.0 vc7 Visual C++ 7.0 vc71 Visual C++ 7.1 vc8Visual C++ 8.0
2.2 命令行

工作区创建器(mwc.pl)和项目创建器(mpc.pl)的命令行选项是一致的。项目创建器用于通过从命令行传入的mpc文件来生成一个或多个工程,工作区创建器用于生成一个或多个工作区以及工作区相关的项目。表2描述了支持的命令行选项,其中粗体字表示常用选项,斜体字表示项目相关选项:

表2. 命令行选项
选项描述
-base该选项允许强制项目继承指定的项目,该选项可以多个同时使用来强制继承多个项目。
-exclude如果该选项同mwc.pl一起使用,则在搜索mpc文件时,用逗号分隔的目录或者mwc文件将会排除在所搜路径之外,每个需要排除的元素都应该使用相对于起始路径的路径来指定;如果该选项同mpc.pl一起使用,则没有任何作用。
-expand_vars该选项指定MPC使用环境变量的值(同-use_env选项一起使用)或者通过-relative选项指定的值来直接替换$()变量。
-feature_file该选项允许用户覆盖缺省的特性文件(MPC/config/default.features或者ACE_wrappers/bin/MakeProjectCreator/config/default.features)。该文件中的特性值将覆盖config目录下的global.features文件中的相应特性值。参见特性文件。
-features在处理之前设置特性列表,该列表需要使用逗号进行分割且不能有空格。
-genins该选择指定MPC在处理完每一个项目之后生成一个“安装”文件。这些“安装”文件可以同prj_install.pl脚本一起使用,该脚本会拷贝部分项目相关文件到用户指定的位置。
-global该选项指定全局输入文件。该文件中的所有值将会应用到所有生成的项目文件中去,该选项的缺省值是ACE_wrappers/bin/MakeProjectCreator/global.mpb或者MPC/config/global.mpb。
-hierarchy如果该选项同mwc.pl一起使用,则会在执行目录和处理的每个mpc文件之间的目录下生成一个工作区文件;该选项同mpc.pl一起使用没有任何作用。该选项在生成“make”相关的工作区文件时为确实选项。
-include该选项用于添加所有包含路径。这些搜索路径用于定位基础项目文件、模板输入文件以及模板。该选项可以在同一命令行上指定多次。
-into将输出文件放到由选项参数指定的路径中去,该路径必须是全路径。
-language该选项用于指定在生成项目时使用的语言。缺省语言为cplusplus,同时也支持csharp、java和vb。
-make_coexistence基于make的项目通常的工作区文件为Makefile(bmake和nmake)。该选项使用后面介绍的方式来使不同的make类型的项目文件可以共存于同一个目录:
bmake和nmake的工作区文件将不再为Makefile,而是工作区名称后跟项目类型后缀(.bmake或者.nmake)。
-name_ modifier该选项允许用户更改输出的项目文件名和工作区文件名。这些文件名通常由mpc文件和mwc文件来决定,但是仍然可以通过模式来进行替换。通过该选项传入的参数将会作为模式来使用,并且任何在模式中出现的星号(*)将会使用项目文件名和工作区文件名来进行替换,这些用来进行替换的项目文件名和工作区文件名由生成的文件类型来决定。
-apply_project该选项只能同-name_ modifier选项一起使用才起作用。当同-name_ modifier一起使用时,模式将会同时应用到项目名称。
-noreldefs该选项指定不要生成缺省的关联定义。关于关联定义可以参考-relative选项。
-notoplevel该选项要求mwc.pl只生产工作区相关的项目文件,而不要生成关联的工作区文件;该选项要求mpc.pl只处理传入的mpc文件,但不要生成任何项目文件。
-recurse递归的搜索当前目录以及子目录下的文件。如果目录由逗号分隔的列表传入(如:-recurse=examples,apps,TAO),则在搜索时排除这些目录。
-relative使用相对路径来替换$()指定的变量。缺省情况下,所有以_ROOT结尾的环境变量都将自动作为相对路径替换符来使用。更多信息参考下面的-relative选项介绍。
-template该选项允许用户指定替换模板。每一个有自己的模板的项目和该选项指定的模板将会替换缺省使用的模板。
-ti每一个项目都有一组模板输入文件。通过该选项,可以为某一个特定的项目类型覆盖缺省的模板输入文件。更多信息参考下面的-ti选项介绍。
-type该选项指定生成的项目和工作区类型。该选项可以多次使用来一次生成多个不同类型的项目和工作区。
-use_env该选项告诉MPC使用相应的环境变量来替换所有$()变量,而不是使用-relative选项指定的值来进行替换。
-static指定生成静态库类型的项目文件,缺省情况下生成动态库类型的项目文件。
-value_project该选项通过命令行来覆盖mpc项目设置,该选项能够为项目生成新的名称/值对,但是,该选项指定的必须是有效的项目名称/值对。更多信息参考下面的-value_project选项介绍。
-value_template该选项通过命令行来覆盖已经存在的模板输入值,该选项能够为项目生成新的名称/值对。更多信息参考下面的-value_template选项介绍。
-version输出MPC版本,不进行任何处理。
-complete该选项用于生成tcsh的complete命令来允许tcsh用户对文件名和选项进行补全。

2.2.1 选项补充说明

下面是对-relative,-ti,-value_project和-value_template选项的一些补充说明:

2.2.1.1 -relative选项

某些项目类型不支持通过$()来访问环境变量,但是MPC必须要保证在这些情况下生成的项目可用。为了避免在这种情况下,生成的项目文件中没有$(),相对路径将会在需要的时候用于替换$()变量。

-relative选项需要一个名称/值对的参数。如:

mwc.pl -relative PROJ_TOP=/usr/projects/top

在上面的例子中,如果$(PROJ_TOP)在任何mpb、mpc、mpd或者mpt文件中作为变量出现,那么将会使用相对于/usr/projects/top的路径来替换该变量。如,如果一个在/usr/projects/top/dir下的mpc文件中有如下内容:

project {
includes += $(PROJ_TOP)
}

那么,在生成的项目文件中将会有类似如下的内容:

CPPFLAGS += -I..

在这种情况下,$(PROJ_TOP)字符串将根据mpc文件所在的路径相对于由选项所指定的路径的路径来进行替换。

2.2.1.2 -ti选项

-ti选项允许用户针对不同的生成类型指定不同的模板输入文件。模板输入文件对应四种不同的分类:dll、lib、dll_exe和lib_exe。并不是所有的项目类型都区分这四种分类,但是不同项目类型的模板都会同不同的模板输入文件进行组合,并根据不同的生成类型来产生不同的输出结果。

-ti选项用于覆盖缺省的模板输入文件名,-ti选项使用单一的形如type:file的参数,type是上面提到的四种分类中的一种,file是在包含所搜路径中能找到的的mpt文件除去后缀的文件名称。

下面的例子给出了-ti选项的用法。该用法指出当生成静态库类型的项目(lib)时,使用vc7lib模板输入文件;而当生成动态库类型的项目(dll)时,使用vc7dll模板输入文件。

mpc.pl -type vc71 -ti lib:vc7lib -ti dll:vc7dll

上面例子中的情况在vc71类型中正好是缺省情况,但是同样描述出可以为不同分类指定不同的模板输入文件。

2.2.1.3 -value_project选项

-value_project在需要修改一个或多个mpc文件中的MPC变量时非常有用。例如,当你想在所有生成的项目中增加一个搜索路径的时候,可以使用如下命令:

mwc.pl -value_project includes+=/include/path

在上面的例子中,MPC会在所有生成的项目文件中增加/include/path作为搜索路径。

2.2.1.4 -value_template选项

该选项用于修改或增加模板输入名称/值对。例如,当你想为vc71类型的项目仅仅生成Release编译目标的时候,可以使用如下的命令:

mwc.pl -type vc71 -value_template configurations=Release


需要了解已经定义好的模板输入变量,请参考如下mpd文件($ACE_ROOT/bin/MakeProjectCreator/templates/*.mpd和$MPC_ROOT/templates/*.mpd)并搜索使用<%和%>括起来的名称。所有不是项目关键字(见预定义关键字)的名称都是模板变量。

2.3 环境变量

MPC使用部分环境变量来改变处理任务的方式。下面是能影响MPC的一些环境变量:

MPC使用环境变量MPC_COMMANDLINE来添加到mwc.pl脚本和mpc.pl脚本的命令行选项来影响执行的效果,该环境变量的值会添加到任何实际通过命令行传入的选项之前。

环境变量MPC_DEPENDENCY_COMBINED_STATIC_LIBRARY只影响项目类型为em3、vc6、vc7、vc71和vc8的静态库类型的项目的工作区依赖关系。如果该环境变量被设置,MPC将会为单个工作区中的项目生成项目见的依赖关系,这通常并不是用户想要的结果,因为这些依赖关系通常会产生副作用。

如果环境变量MPC_LOGGING被设置,MPC会解析值并根据设置来提供普通信息、警告信息和诊断信息。如果值包含info=1,MPC会输出普通信息;如果值包含warn=1,MPC会输出警告信息;如果值包含diag=1,MPC会输出诊断信息;如果只包含detail=1,MPC会输出相信的信息;没有前面提到的都没有,MPC会采取同MPC_SILIENT一样的处理方式。

环境变量MPC_SILIENT告诉只输出错误信息。不过,处理指示仍然会输出。
如果环境变量MPC_VERBOSE_ORDERING被设置,而在“after”中引用的项目还未处理,则MPC会对用户发出警告。该环境变量仅仅影响mwc.pl脚本。
MPC使用介绍(二)

下面的内容紧接着MPC使用介绍(一)。


3. 编写MPC和MWC文件

这节将为大家分别介绍各种输入文件类型和语法的详细信息:

3.1 输入文件

MPC处理四种不同类型的文件,但是针对大多数的用户,通常情况下只需要关心mpc文件和mwc文件。

3.1.1 项目文件(mpc)
项目文件(使用mpc后缀),包含了这些信息:包含路径、库目录、源文件以及项目间的依赖关系。一个mpc文件可以有一个或多个的项目,这些项目需要使用唯一的名称来避免项目生成是出现错误,这些项目同时还包括生成的目标类型(库和可执行文件)。

3.1.2 工作区文件(mwc)

工作区在一个mwc文件中定义了一系列的mpc文件、目录以及其他的一些mwc文件。针对每一个mpc文件,工作区创建器调用项目创建器来生成相应的项目。在所有的项目都成功生成之后,将会创建对应的工作区来包含生成的项目,并生成项目之间的依赖关系(只有生成工具支持才会生成)。一个mwc文件可以包含一个或多个使用唯一的名称来进行区别的工作区。如果没有为工作区创建器指定任何和工作区文件,工作区创建器将会在当前目录中搜索所有的mpc文件,并组合成一个单独的工作区来进行处理。

3.1.3 基础项目文件(mpb)

MPC中一个重要的特性就是项目之间的继承,项目继承允许用户设置一个基础项目(mpb文件),该基础项目包含所有继承项目都需要的公有信息,包含路径、库目录以及项目间的依赖关系等公有信息都可以包含在该基础项目中,任何从该项目继承的项目都将缺省拥有这些信息。

3.1.4 基础工作区文件(mwb)

同项目一样,工作区同样可以从其他工作区进行继承,一个基础工作区可以提供对于其他一些工作区都需要的相同信息并使用继承关系来是这些工作区都拥有这些信息。

3.2 一般性的输入文件语法

在这一小节中,我们将对不同文件的语法进行一个介绍,并同时介绍该文件类型的中所使用的部分缺省值。

3.2.1 mwc和mwb

在工作区中,可以包含不同的mpc文件和路径,在同一个mwc文件中可以同时定义一个或者多个工作区。例如:

workspace(optional name) : optional_base_workspace {
file.mpc
directory
other.mwc

exclude(vc6, vc7, vc71, vc8, nmake) {
this_directory
}
}

可以通过在workspace关键字后用括号括起来的字符串来为工作区指定名称,如果没有为工作区指定名称,则使用mwc文件不带文件后缀的文件名来作为该工作区的名称。

工作区同时支持从其他工作区进行继承。在上面的例子中,optional_base_workspace是一个带有工作区信息的mwb文件不带文件后缀的文件名,这些信息将会被从该基础工作区中继承的工作区所使用。

在花括号之间,可以是赋值语句、mpc文件、路径、其他工作区以及排除项。其中列出的mpc文件将会包含在该工作区中;如果一个路径在工作区中出项,则工作区创建器会递归的遍历该目录以及其子目录,并搜索所有的mpc文件;如果一个mwc文件被包含在该工作区中,则该文件将会被聚合到该工作区中来。

赋值语句可以散布在项目和路径之间,并影响项目生成的方式:

cmdline设置可以用来实现同从命令行中为mpc.pl脚本提供选项一样的功能(参见命令行选项)。不过,-type、-recurse、-noreldefs、-make_coexistence、-genins、-into、-language以及输入文件将会被忽略。环境变量可以通过$NAME方式来访问(其中NAME是环境变量名)。cmdline设置对于需要正确处理的特殊工作区来说极其有用。

除了上面列出的一些设置之外,mwc.pl脚本还支持的设置是implicit。如果implicit被指定为1,则在没有mpc文件的路径下,将会生成一个缺省的项目,implicit同样可以设置为基础项目的名称,在这种情况下,隐式生成的项目将从继承该基础项目。不论何种方式,如果一个路径下并没有可以用来放置到项目中的文件,则不会生成项目。当你需要定义一个特殊的工作区,而MPC的缺省行为即能生成有效的项目的时候,指定implicit设置将是非常方便的一种方式。

在一个范围中指定的赋值将只影响在该范围中包含的mpc文件和路径。如,在下面的例子中,cmdline设置仅仅影响到在相同范围内的项目,在该例子中,MPC将会针对directory/foo.mpc添加-static选项,而其他在该工作区中列出的mpc文件和路径则不受此影响。

workspace {



static {
cmdline += -static
directory/foo.mpc
}

exclude(gnuace, make) {
some.mpc
}
}

排除项用来阻止mpc文件和路径被处理。这些被排除的mpc文件和路径将不会生成项目文件和工作区。可以在exclude关键字之后用括号对需要排除的项目类型进行指定(如上面的例子),该方式将指示工作区创建器只针对列出的项目类型排除生成。如果不指定任何的排除项目类型,则表示排除所有的项目类型。
同C++语言一样,在一行中,所有在//之后的内容都会被视为注释而被忽略掉。

3.2.2 mpc和mpb

3.2.2.1 项目声明

项目声明同工作区声明类似,但是相比要复杂一些。一个mpc文件可以包含一个或者多个项目,并且每个项目都可以从基础项目中进行继承。

project(optional name) : base_project, another_base_project {
exename = client
includes += directory_name other_directory
libpaths += /usr/X11R6/lib

Header_Files {
file1.h
file2.h
fileN.h
}

Source_Files {
file1.cpp
file2.cpp
fileN.cpp
}
}

如果没有为项目指定名称,则使用mpc文件不带文件后缀的文件名来作为该项目的名称。但是,如果你的mpc文件需要包含多个项目,为这些项目指定不同名称是非常重要的,否则,后面生成的项目将会覆盖前面生成的项目。如果发现相同的项目名称,MPC会输出错误并停止处理。

3.2.2.2 基础项目

基础项目的文件名可以使用mpb或者mpc后缀名。如果,MPC处理器在包含的搜索路径中无法找到使用以mpb或者mpc作为后缀名,并且文件名为基础项目名称的文件,则MPC会输出一个严重错误并停止处理。

3.2.2.3 赋值关键字

表3中列出了可以在mpc文件中的赋值语句(如:=、+=和-=)中使用一系列关键字,其中最常用的关键字用黑体进行表示:
表3. 赋值关键字

关键字描述
after指定该项目需要在列出的项目之后才进行生成。
avoids指定在生成项目文件是,哪些特性需要被禁止。在GNUACE类型中,可以同时指定在生成目标时,哪些make宏不应该被设置。
custom_only该设置告诉MPC生成仅包含可定制生成步骤的项目,在该项目中的所有文件将通过使用Defined_Custom中提供的可定制组件列表来进行处理。
dynamicflags指定当生成动态库的时候,传递给编译器的选项。
dllout如果被定义,指定动态库的放置路径,该设置在生成动态库类型时会覆盖libout的值。
exename指定项目是可执行的项目,并同时指定生成的可执行文件名。
includes指定提供给编译器的附加搜索路径。
install指定可是否对执行程序进行安装。
libout指定动态库和静态库的放置路径。
libpaths指定提供给连接器的附件库搜索路径。
libs指定一个或者多个需要连接到目标中的库,当在模板文件中进行处理时,库修饰符有可能会被添加进来。例如,在使用vc6项目类型时,库修饰符就会被添加进来。
lit_libs该设置作用同libs,区别是不会添加库修饰符。
macros在这里提供的所有值将会作为编译器命令行中的宏定义来进行添加。
pch_header预编译头的头文件名。更多信息见表后说明。
pch_source与编译头的源文件名。更多信息见表后说明。
pure_libs该设置同lit_libs,区别是不会添加任何的前缀和文件后缀名。
postbuild如果被指定,则该设置的值将会在项目被成功生成后,被作为命令来执行。<% %>结构可以在该设置中使用来访问模板变量和模板函数。(关于模板的更多信息,参考模板文件(mpd)一节)
recurse如果被设置成1,MPC会递归遍历在组件列表中(如:Source_Files、Header_Files等)列出的目录以及其子目录,并添加所有组件相关的文件到列表中。该关键字可以作为全局的项目设置或者组件范围的设置来进行添加。
requires指定在生成项目文件时,必须的特性。在GNUACE类型中,可以同时指定在生成目标时,哪些make宏应该被设置。
sharedname指定项目是共享库的项目,并同时指定生成的共享库文件名。更多信息见表后说明。
staticflags指定当生成静态库的时候,传递给编译器的选项。
staticname指定项目是静态库的项目,并同时指定生成的静态库文件名。更多信息见表后说明。
tagchecks仅对GNUACE项目类型有效,指定由一个或者多个由tagname指定的名称,在宏中进行搜索。
tagname指定在生成目标之前,需要进行检查的GNUACE make宏。
version指定库或者可执行文件的版本号。


赋值语句可以通过+=或者-=操作符来在关键字值中添加或者删除指定的值。

如果在mpc文件中指定了sharedname而没有指定staticname,则会使用sharedname的值来作为staticname的值;反之亦然。

如果没有指定exename、sharedname和staticname,MPC会在所有的源文件中搜索main函数。如果找到了main函数,则exename会被指定为包含了main函数的源文件去除后缀之后的文件名;如果没有找到,则以项目名称来作为sharedname或者staticname。

如果项目名称、exename、sharedname和staticname中包含了星号,则MPC会根据特定的规则来替换名称中的星号:如果是项目名称中包含了星号,则使用缺省项目名称来替换星号;如果是exename、sharedname和staticname中包含了星号,则会使用相应的项目名称来替换星号。
如果有形如*_pch.h的文件存在于mpc文件所在的目录中,并且pch_header关键字没有被指定,则该文件会被作为预编译头的头文件,如果在该目录下有多个符合条件pch文件,则最接近项目名称头文件将会被指定为该项目的与编译头的头文件。该规则同样适用于pch_source关键字。

3.2.2.4 组件

mpc文件可以同时指定需要包含在生成的项目中的文件,这些文件使用表4中列出的名称来进行指定。不过,大多数情况下,用户可能只需要MPC为项目文件提供的缺省值就足够了。
表4. 组件名称和缺省值

名称缺省值
Source_Files缺省为目录下拥有以下后缀的文件:cpp、cxx、cc、c和C。
Header_Files缺省为目录下拥有以下后缀的文件:h、hpp、hxx和hh。
Inline_Files缺省为目录下拥有以下后缀的文件:i和inl。
Template_Files缺省为目录下以如下结尾的文件:_T.cpp、_T.cxx、_T.cc、_T.c和_T.C。
Documentation_Files缺省为目录下满足如下条件的文件:README、readme、.doc、.html和.txt。
Resource_Files缺省为目录下匹配项目名称并以rc作为后缀的文件。

如果某个组件未在mpc文件中列出,则使用其缺省值。为了防止可能在目录中出现的一组文件被作为缺省值使用,用户应该为相应的组件定义一个空的集合。
每个组件名称均有两种格式,第一种格式只是简单的在结构中使用一系列文件来表示:

Source_Files {
file1.cpp
file2.cpp
}

第二种格式是一系列命名块的组合:

Source_Files(MACRO_NAME) {
BlockA {
file1.cpp
file2.cpp
}

BlockB {
file3.cpp
file4.cpp
}
}

第二种格式允许用户在逻辑上将文件进行分组,以便将来能够更容易的进行维护。使用这种格式同样会对em3、gnuace、vc6、vc7、vc71和vc8的项目类型产生影响。如果某个在Source_Files中列出的源文件有相应的头文件或者内联文件同时存在于目录中,并且该文件并没有在相应的组件列表中列出,则该会自动添加到相应的组件列表中去。
3.2.2.5 verbatim语句
verbatim结构可以用来指定需要原样放置到生成的项目文件中去的内容。语法如下:

vertabim(<project type>, <location>) {
..
}

当MPC在生成由<project type>指定的项目类型,并且在模板文件中遇到匹配<location>名称指定的占位符(原文marker)时,将会直接使用块中的内容对去进行替换。如果在块中的空白需要被保留,在块中的每一行都应该使用双引号来把内容括起来。如,下面的例子描述了在生成的gnuace项目类型的GNUmakefile文件末尾,所有的all:目标都应该依赖foo。

verbatim(gnuace, bottom) {
all: foo
}

3.2.2.6 specific语句

specific关键字可以用来为特定的项目类型定义特殊的赋值语句。该关键字允许为项目指定平台相关或者OS相关的值。例如,当你想在其中一些平台中链接qt-mt库,而在其他平台连接qt-mt230nc库时,可以使用如下的例子:

specific(bmake, nmake, vc6, vc7, vc71, vc8) {
lit_libs += qt-mt230nc
} else {
lit_libs += qt-mt
}

其中else语句必须与后花括号在同一行中出现(如果有的话)。对某一种项目类型使用非操作(使用“!”)将会导致块中的内容被应用到除该项目类型之外的其他项目类型。如果出现在specific语句中的某个关键字并不是MPC关键字,则该关键字将被作为模板值修饰符来解释。在这种情况下,该块的作用同-value_template命令行选项一样。

3.2.2.7 conditional语句

conditional块允许为特定的项目类型添加额外的源文件。语法如下:

conditional(<project type> [, <project type> …]) {
source1.cpp

}
conditional(<project type> [, <project type> …]) {

source1.cpp

} else {
source2.cpp

}

其中else语句必须与后花括号在同一行中出现(如果有的话)。对某一种项目类型使用非操作(使用“!”)将会导致块中的内容被应用到除该项目类型之外的其他项目类型。

3.2.2.8 自定义类型和生成规则

MPC允许用户根据需要自定义文件类型来支持自定义的生成规则。例如:

project {
Define_Custom(MOC) {
automatic = 0
command = $(QTDIR)/bin/moc
output_option = -o
inputext = .h
pre_extension = _moc
source_outputext = .cpp
keyword mocflags = commandflags
}

// Custom Component
MOC_Files {
QtReactor.h
}

Source_Files {
QtReactor_moc.cpp
}
}

上面的例子定义了一个名为“MOC”的自定义文件类型,其中描述了如何处理输入文件以及如何生成相应的输出文件,一旦该自定义文件类型被定义,用户可以使用MOC_Files来为该新文件类型指定输入文件。表5中列出了可以在Define_Custom块中使用的关键字。
表5. Define_Custom关键字
关键字描述
automatic如果设置为1,则会尝试为自定义类型搜索满足条件的文件并进行添加;如果设置为0,则不进行自动添加。
command用来处理自定义类型的输入文件的命令。
commandflags任何需要传递给命令的选项。
dependent如果有值,则在所有生成的文件中都会根据值来添加依赖项,缺省值是没有任何值。
inputext这是能够由该命令处理的用逗号分隔的输入文件扩展名列表。
keyword <name>该关键字允许用户映射<name>到项目级别的命名空间,设置到该结构的值必须是可以在Define_Custom语句中使用的关键字之一,使用该关键字的作用是修改通常只能在自定义组件中使用的关键字的值(如:command、commandflags等)。
libpath如果该命令需要一个不在通常的库搜索路径中出现的路径,该关键字可以用来保证命令能够找到在运行时需要的库。
output_option如果命令能够指定单一的输出文件名,则应该进行指定;否则,应该对其进行忽略。
pch_postrule如果设置为1,则会在自定义规则中添加一条规则,该规则将会修改源输出文件来包含相应的预编译头文件。
postcommand该命令允许用户在主命令处理完成自定义的输入文件之后,运行指定的命令。
pre_extension如果该命令生成多个拥有相同扩展名的文件,该命令可以用来在逗号分隔的列表中对其进行指定。例如,tao_idl分别为不同的扩展名生成两种类型的文件(C.h、S.h、C.cpp和S.cpp)。该关键字影响所有的扩展类型。
source_pre_extension该关键字功能同pre_extension,但是只影响source_outputext。
inline_pre_extension该关键字功能同pre_extension,但是只影响inline_outputext。
header_pre_extension该关键字功能同pre_extension,但是只影响header_outputext。
template_pre_extension该关键字功能同pre_extension,但是只影响template_outputext。
resource_pre_extension
该关键字功能同pre_extension,但是只影响resource_outputext。
documentation_pre_extension该关键字功能同pre_extension,但是只影响documentation_outputext。
pre_filename该关键字语法同pre_extension,不同之处在于是添加到文件名前面,而不是后面。该关键字影响所有的扩展类型。
source_pre_filename该关键字功能同pre_filename,但是只影响source_outputext。
inline_pre_filename该关键字功能同pre_filename,但是只影响inline_outputext。
header_pre_filename该关键字功能同pre_filename,但是只影响header_outputext。
template_pre_filename该关键字功能同pre_filename,但是只影响template_outputext。
resource_pre_filename该关键字功能同pre_filename,但是只影响resource_outputext。
documentation_pre_filename该关键字功能同pre_filename,但是只影响documentation_outputext。
source_outputext该关键字包含由逗号分隔的一系列可能的输出源文件扩展名,如果该命令不生成源文件,则不需要使用该关键字。
inline_outputext该关键字包含由逗号分隔的一系列可能的输出内联文件扩展名,如果该命令不生成内联文件,则不需要使用该关键字。
header_outputext该关键字包含由逗号分隔的一系列可能的输出头文件扩展名,如果该命令不生成头文件,则不需要使用该关键字。
template_outputext该关键字包含由逗号分隔的一系列可能的输出模板文件扩展名,如果该命令不生成模板文件,则不需要使用该关键字。
resource_outputext该关键字包含由逗号分隔的一系列可能的输出资源文件扩展名,如果该命令不生成资源文件,则不需要使用该关键字。
documentation_outputext该关键字包含由逗号分隔的一系列可能的输出文档文件扩展名,如果该命令不生成文档文件,则不需要使用该关键字。
generic_outputext如果该命令不生成前面列出的文件类型,那么在这里指定这些扩展名。

在自定义组件和源文件组件、头文件组件和内联文件组件之间有特殊的交互作用:如果一个custom定义被设置为“automatic”,并且自定义组件文件存在却没有直接指明,除非这些名字已经在这些组件中列出(或者部分列出),否则缺省行为将自动将这些生成的文件名自动添加到相应的源文件组件、头文件组件和内联文件组件列表中去。
针对自定义生成类型的个别输出扩展名并不是必须的。但是,至少需要指定一个输出扩展类型来告诉MPC如果生成目标。另一方面,使用的命令并不一定要生成输出,但是如果你想要你的输入文件能够在项目编译时被处理,则必须要指定一种扩展类型。
如果自定义输出类型不能在上面列出的扩展类型关键字(*_outputext)中列出,但是你能够预先知道输出文件名称,则可以用“>>”构造块来列出。下面是一个展示如何使用“>>”构造块的例子,在该例子中,该命令使用一个输入文件foo.prp,并生成两个没有任何关联的输出文件:hello.h和hello.cpp。

project {
Define_Custom {
automatic = 0
command = perl quogen.pl
commandflags = --debuglevel=1 --language=c++ \
      --kernel_language=c++
inputext = .cpp
keyword quogenflags = commandflags
}

Quogen_Files {
foo.prp >> hello.h hello.cpp
}

Source_Files {
hello.cpp
}
}

你可以使用“<<”结构在表示特定的自定义输入文件的依赖关系。例如,在上面的例子中,假定输入文件foo.prp依赖于foo.in,则我们可以通过“<<”来像下面例子一样的来对foo.in进行指定。

Quogen_Files {
foo.prp >> hello.h hello.cpp << foo.in
}

在Custom_Build块中,还可以使用optional结构,该结构用来在当特定的输出文件依赖于特定的可选命令行参数时,对其进行指定。

project {
Define_Custom(TEST) {
optional(keyword) {
flag_keyword(option) += value [, value]
}
}
}

在上面的片段中,keyword可以是任何形式的pre_extension、pre_filename或者是以_outputext结尾的关键字。flag_keyword可以是任何的自定义组件关键字(尽管只有commandflags有功能性的值),MPC会在在flag_keyword的值中搜索括号中的option值,如果有,则在+=后的value或者values将会被添加到keyword指定的列表中去,可以使用非操作(使用“!”)来达到相反的效果。
下面的例子optional结构是如何使用来影响tao_idl命令的自定义定义的(参见:ACE_wrappers/bin/MakeProjectCreator/config/taoidldefaults.mpb)。其中的-GA命令行选项将导致tao_idl基于idl文件的文件名生成额外的使用A.cpp扩展的源文件,-Sc命令行选项将导致tao_idl不生成与S_T有关的文件。

Define_Custom(IDL) {



inputext = .idl
source_pre_extension = C, S
header_pre_extension = C, S
inline_pre_extension = C, S
source_outputext = .cpp, .cxx, .c, .C
header_outputext = .h, .hpp, .hxx, .hh
inline_outputext = .inl, .i
keyword idlflags = commandflags

optional(source_pre_extension) {
commandflags(-GA) += A
}

optional(template_outputext) {
commandflags(!-Sc) += S_T.cpp, S_T.cxx, S_T.cc, S_T.C
}

optional(header_pre_extension) {
commandflags(!-Sc) += S_T
}

optional(inline_pre_extension) {
commandflags(!-Sc) += S_T
}
}

对于自定义文件类型,只有如下的的关键字可以在自定义文件类型组件列表中使用:command、commandflags、dependent、gendir、postcommand和recurse。其中:recurse关键字作用同赋值关键字中的相同;command、commandflags、dependent和postcommand可以用来增加或覆盖在Define_Custom节中的设置;gendir关键字用来指定存放输出文件的目录(只有在Define_Custom中设置了output_option时才起作用),例如:

MOC_Files {
commandflags += -nw
gendir = moc_generated
QtReactor.h
}

Source_Files {
moc_generated/QtReactor_moc.cpp
}

在上面的例子中,-nw命令行选项被添加到commandflags值中,并且输出文件(QtReactor_moc.cpp)被存放到moc_generated目录。如果MOC自定义组件并没有设置output_option,则命令行选项没必要添加到commandflags值中去,同时需要使用postcommand来保证输出文件被存放到moc_generated目录。

MPC使用介绍(三)

下面的内容紧接着MPC使用介绍(一)和MPC使用介绍(二)。


3.2.2.9 自定义生成后命令

当在Define_Custom中使用postcommand时,可以使用一些伪模板变量来增加灵活性,表6中列出了只能在postcommand中使用伪变量,同时请注意<%和%>是组成变量不可缺少的部分。
表6. 生成后命令伪变量
变量描述
<%input%>原始命令的输入文件。
<%input_basename%>原始命令的输入文件的基础名称。
<%input_noext%>原始命令的输入文件去除扩展名后的名称。
<%input_ext%>原始命令的输入文件的扩展名。
<%output%>原始命令产生的输出文件。
<%output_basename%>原始命令产生的输出文件的基础名称。
<%output_noext%>原始命令产生的输出文件去除扩展名后的名称。
<%output_ext%>原始命令产生的输出文件的扩展名。
输出文件可以是一般的输出文件,以及由下面的变量表示的文件,如果没有满足条件的文件,则为空。
<%source_file%>输出的源文件。
<%template_file%>输出的模板文件。
<%header_file%>输出的头文件。
<%inline_file%>输出的内联文件。
<%documentation_file%>输出的文档文件。
<%resource_file%>输出的资源文件。


表7描述了可以用于command、commandflags、dependent、output_option以及postcommand关键字的伪变量。
表7. 通用伪变量
变量描述
<%temporary%>临时文件文件名。生成的临时文件文件名并不包括路径信息,并且对于相同的变量设置均一致。
<%cat%>平台无关命令,用于将文件输出到终端。
<%cp%>平台无关命令,拷贝文件。
<%mkdir%>平台无关命令,创建目录。
<%mv%>平台无关命令,移动文件。
<%rm%>平台无关命令,删除文件。
<%nul%>平台无关的空设备。
<%gt%>平台无关及项目无关的大于符号。
<%lt%>平台无关及项目无关的小于符号。
<%quote%>项目无关的双引号。
<%and%>平台无关及项目无关的与操作符。
<%or%>平台无关及项目无关的或操作符。


3.2.3 特性文件

MPC中使用特性来表示不同的概念或者是为了能够正确生成项目所必须的外部命令。特性文件决定了MPC是否生成项目,或者在生成项目时应该使用的特性。
特性中允许使用注释(//),并且对不同特性名称赋值数字值。这些特性名称会对应的出现于mpc文件中requires和avoids关键字值中。
如果某个特性没有在特性文件中列出,或者拥有真值(1),则使该特性可用;如果某个特性拥有假值(0),则该特性将被禁用。
如果某个特性名称针对某种文件类型出现在requires关键字值中,并且该特性可用,则生成对应项目;如果该特性被禁用,则不生成对应项目。
对于avoids关键字,情况正好相反。如果某个特性名称针对某种文件类型出现在avoids关键字值中,并且该特性被禁用,则生成对应项目;如果该特性可用,则不生成对应项目。
MPC全局特性文件拥有如下预定义值:

boost = 0
mfc = 0
qt = 0
rpc = 0
zlib = 0
zzip = 0

在上面的内容中,boost、mfc、qt、rpc、zlib和zzip在生成每个项目的时候都将被禁用。如果这不是你想要的结果,则你必须采用如下四种方式中的一种来达到要求:
在config目录中创建一个项目相关的特性文件(如,make.features),并设置与项目相关的特性。
在config目录中创建一个default.features文件,并设置与项目相关的特性。
在任何喜欢的目录中创建一个特性文件,并使用-feature_file命令行选项来指定。
使用-features命令行选项来动态修改特性的值。
生成的项目文件将拥有在global.features文件中指定的特性值和你的特性文件中的值的组合。但是,如果某个特性在全局文件中被禁用,并且你想使其可用,则你只能显示的在某你的特性文件中使其可用。

3.2.4 特性项目

一个特性项目可以类似普通项目一样包含信息,但是特性项目只能作为基础项目来使用,并且用来决定子项目中哪些特性可用,哪些特性被禁用。
一个特性定义必须至少包含一个特性名称,该名称表示该特性应该可用;相反的,如果在特性名称前使用非操作(使用“!”),则表示该特性应该被禁用。在括号中可以包含多个用逗号隔开的特性。下面的例子描述了如何定义一个特性项目:

// ziparchive.mpb
feature(ziparchive) {
includes += $(ZIPARCHIVEROOT)
libpaths += $(ZIPARCHIVEROOT)/lib
libs += ziparch
}

在上面的例子中,所有从名为ziparchive的特性项目继承的项目都将拥有其中定义的信息仅当ziparchive特性可用的时候。

3.3 缺省行为

MPC被设计来尽量减少维护量,并且持续的保持着对最新的生成工具的支持。如果你的源文件有着适当的(原文properly)组织,则只需要你对mpc文件进行少量的维护就可以了。
通过使用继承以及适当的代码安排,一个TAO相关的项目的mpc文件可以简单到:

project : taoserver {
}

该项目定义可以用来生成一个拥有多个idl文件、头文件和源文件的TAO服务器项目。
适当的源文件布局可以简单总结为one directory per binary target(一个目录对应一个二进制目标),如果mpc文件所在的目录下只包含属于单一目标的文件,那么MPC的缺省行为即可满足项目需求。
当然,通常情况下,你的项目文件并不能满足这样的需求,因此,所有的缺省行为都是可以被覆盖的。下面的小节描述了MPC的缺省行为以及如何覆盖该缺省行为。

3.3.1 源文件

对一个正在开发中的项目来说,添加新的源文件以及删除旧的源文件是时常会发生的事情。如果在一个mpc文件中,遗漏了Source_Files组件列表,那么MPC会假定任何满足源文件扩展类型的文件都属于项目。对于大多数的项目类型来说,源文件扩展类型包括:.cpp、.cxx、.cc、.c和.C;但是对于vc6项目类型,Visual C++ 6只能识别.cpp、.cxx和.c,并不能识别.cc和.C。

3.3.2 模板文件

如果在一个mpc文件中,遗漏了Template_Files组件列表,那么MPC会假定任何满足模板文件扩展类型的文件都属于项目。对于大多数的项目类型来说,模板文件扩展类型包括:_T.cpp、_T.cxx、_T.cc、_T.c和_T.C;但是对于vc6项目类型,Visual C++ 6只能识别_T.cpp、_T.cxx和_T.c,并不能识别_T.cc和_T.C。

3.3.3 内联文件

同源文件一样,如果在一个mpc文件中,遗漏了Inline_Files组件列表,MPC会使用缺省值来生成。满足.i和.inl扩展类型的文件被认为是内联文件。
同时,Inline_Files组件与Source_Files组件有着特殊的交互作用:如果Source_Files组件列表有文件列出,并且Inline_Files组件被省略,则每一个源文件都会匹配一个内联文件。如果匹配的内联文件被发现或者能够从自定义命令中生成,则该文件会被添加到Inline_Files组件列表中去。

3.3.4 头文件

同源文件一样,如果在一个mpc文件中,遗漏了Header_Files组件列表,MPC会使用缺省值来生成。满足.h、.hpp、.hxx和.hh扩展类型的文件被认为是头文件。
同Inline_Files组件一样,Header_Files组件与Source_Files组件也有着特殊的交互作用:如果Source_Files组件列表有文件列出,并且Header_Files组件被省略,则每一个源文件都会匹配一个头文件。如果匹配的头文件被发现或者能够从自定义命令中生成,则该文件会被添加到Header_Files组件列表中去。

3.3.5 文档文件

如果Documentation_Files组件被省略,则缺省为以如下结尾的文件:README、readme、.doc、.html和.txt。

3.3.6 资源文件

如果Resource_Files组件被省略,则缺省为以.rc为扩展名且文件名与项目名称类似的文件。例如,如果一个目录中有三个.rc文件,同时项目名称为foo,则只有包含单词foo的.rc文件会被添加到Resource_Files组件列表中去。

3.3.7 自定义的定义文件(Custom Defined Files)

自定义的定义文件组件与Source_Files组件有着特殊的交互作用:如果automatic关键字被设置为1,则自定义命令生成的源文件将会被自动添加到Source_Files组件列表中去;另一方面,如果有任何已经在Source_Files组件列表中列出的源文件同生成的源文件相同,则不会向Source_Files组件列表中添加任何生成的源文件。

3.3.8 MPC文件举例

为了描述mpc文件的简单性,我们的例子中使用了$TAO_ROOT/orbsvcs/performance-tests/RTEvent/lib目录下的内容:

Auto_Disconnect.cpp Loopback_Supplier.h RTEC_Initializer.cpp
Auto_Disconnect.h Low_Priority_Setup.cpp RTEC_Initializer.h
Auto_Disconnect.inl Low_Priority_Setup.h rtec_perf_export.h
Auto_Functor.cpp Low_Priority_Setup.inl RTEC_Perf.mpc
Auto_Functor.h Makefile RTPOA_Setup.cpp
Auto_Functor.inl ORB_Holder.cpp RTPOA_Setup.h
Client_Group.cpp ORB_Holder.h RTPOA_Setup.inl
Client_Group.h ORB_Holder.inl RTServer_Setup.cpp
Client_Group.inl ORB_Shutdown.cpp RTServer_Setup.h
Client_Options.cpp ORB_Shutdown.h RTServer_Setup.inl
Client_Options.h ORB_Shutdown.inl Send_Task.cpp
Client_Pair.cpp ORB_Task_Activator.cpp Send_Task.h
Client_Pair.h ORB_Task_Activator.h Send_Task_Stopper.cpp
Client_Pair.inl ORB_Task_Activator.inl Send_Task_Stopper.h
Consumer.cpp ORB_Task.cpp Send_Task_Stopper.inl
Consumer.h ORB_Task.h Servant_var.cpp
Control.cpp ORB_Task.inl Servant_var.h
Control.h Peer_Base.cpp Servant_var.inl
EC_Destroyer.cpp Peer_Base.h Shutdown.cpp
EC_Destroyer.h PriorityBand_Setup.cpp Shutdown.h
EC_Destroyer.inl PriorityBand_Setup.h Shutdown.inl
Federated_Test.idl PriorityBand_Setup.inl Supplier.cpp
Implicit_Deactivator.cpp RIR_Narrow.cpp Supplier.h
Implicit_Deactivator.h RIR_Narrow.h SyncScope_Setup.cpp
Implicit_Deactivator.inl RT_Class.cpp SyncScope_Setup.h
Loopback_Consumer.cpp RT_Class.h SyncScope_Setup.inl
Loopback_Consumer.h RT_Class.inl TAO_RTEC_Perf.dsp
Loopback.cpp RTClient_Setup.cpp TAO_RTEC_Perf.dsw
Loopback.h RTClient_Setup.h Task_Activator.cpp
Loopback_Pair.cpp RTClient_Setup.inl Task_Activator.h
Loopback_Pair.h RTCORBA_Setup.cpp Task_Activator.inl
Loopback_Pair.inl RTCORBA_Setup.h
Loopback_Supplier.cpp RTCORBA_Setup.inl

下面的mpc文件(RTEC_Perf.mpc)显示了为了生成可用于生成工具的项目文件,只需要做少量工作即可:

project(RTEC_Perf) : strategies, rtcorbaevent, minimum_corba {
sharedname = TAO_RTEC_Perf
idlflags += -Wb,export_macros=TAO_RTEC_Perf_Export \
      -Wb,export_includes=rtec_perf_export.h
dllflags += TAO_RTEC_PERF_BUILD_DLL

Template_Files {
Auto_Disconnect.cpp
Auto_Functor.cpp
Low_Priority_Setup.cpp
RIR_Narrow.cpp
Servant_var.cpp
Shutdown.cpp
Task_Activator.cpp
}
}

下面,我们将逐行对其进行解释:

project(RTEC_Perf) : strategies, rtcorbaevent, minimum_corba {
第1行定义了一个名为RTEC_Perf的项目,并继承了冒号后列出的基础项目。

sharedname = TAO_RTEC_Perf
第2行表示该项目为共享库项目,并且库名称为TAO_RTEC_Perf。

idlflags += -Wb,export_macros=TAO_RTEC_Perf_Export \
      -Wb,export_includes=rtec_perf_export.h
第3-4行则向IDL编译器添加了一些命令行选项。

dllflags += TAO_RTEC_PERF_BUILD_DLL
接下来的第5行,向dllflags添加了在rtec_perf_export.h
头文件中需要的TAO_RTEC_PERF_BUILD_DLL宏定义。

Template_Files {
Auto_Disconnect.cpp
Auto_Functor.cpp
Low_Priority_Setup.cpp
RIR_Narrow.cpp
Servant_var.cpp
Shutdown.cpp
Task_Activator.cpp
}
第7-15行指定将列出的cpp文件作为Template_Files组件中的一部分来处理。

你可能已经注意到了,上面的文件列表中被没有列出多少文件。通过MPC中内置的缺省行为,我们并不需要这样做。我们只需要IDL_Files、Source_Files、Inline_Files和Header_Files的缺省实现即可。由于模板文件并不满足MPC内置的关于模板文件的缺省行为,所有我们在这里显示的将它们列举出来。同时我们还使用了继承来获取了许多与TAO有关的选项。


MPC使用介绍(四)

下面的内容紧接着MPC使用介绍(一)、MPC使用介绍(二)和MPC使用介绍(三)。


4. 增加新类型
如果MPC并不支持某个特定的生成工具,你可能会考虑为其增加一个新的项目类型。例如,我们可以为MPC添加对Boost Jam、Eclipse、Xcode以及其他很多类型生成工具的支持。要达到这样的要求,我们需要有关于MPC输入文件以及面向对象Perl的知识。

4.1 输入文件语法
在这一节中,我们将为大家介绍在项目生成是使用的输入文件语法:

4.1.1 模板文件(mpd)
模板文件由大量MPC放入到每个生成的项目文件中去的信息构成,这些文件通过不同的模板指令来根据mpc文件的内容来提供相应的文本和数据的布局。
模板指令由<%%>结构来进行声明,该结构用来提供if语句、foo循环以及变量访问功能,需要注意的是,所有没有被<%%>包围的文本,包括空白都会原封不动的传递到生成的项目文件中去。
if语句可以只占用一行,也可以占用多行。例如,下面这行:

<%if(exename)%>BIN = <%exename%><%else%>LIB = <%sharedname%><%endif%>

同下面几行是一样的:

<%if(exename)%>
BIN = <%exename%>
<%else%>
LIB = <%sharedname%>
<%endif%>

foreach语句同样可以占用一行或者多行,如同关键字小节中描述的,foreach语句在空格分隔的列表环境中计算变量。foreach循环可以采用多种书写方式,第一种方式是对循环变量进行命名并列出每个变量值,这也是比较推荐的做法:

FILES=<%foreach(fvar, idl_files source_files header_files)%> <%fvar%><%endfor%>

第二种方式是让foreach来决定变量名称,在这种方式中,每个值都可以用传递给foreach的第一个变量的变量名称去除掉结尾的“s”字符来访问:

FILES=<%foreach(idl_files source_files header_files)%> <%idl_file%><%endfor%>

需要注意的是,<%idl_file%>变量会循环迭代idl_files、source_files和header_files列表中的每一个值。如果foreach的第一个变量并不以“s”字符结尾,则使用同第一个变量名相同的名称来表示。如:

<%foreach(filelist)%> <% filelist%><%endfor%>

表8中列出了可以在模板文件中使用的关键字。
表8. 模板文件关键字
关键字描述
if用来决定某个变量是否被定义。非操作(!)可以用来反转结果。该结构只检查在mpc文件或mpt文件中出现的值定义,缺省值(即使是由项目创建器实现的)并不会被if语句考虑。
else同if语句一起使用,只有当if语句不为真的时候,else语句才会被执行。
endif同if语句一起使用,用来结束if或者if/else块。
noextension将变量值解释为文件名,并从变量中移除扩展名以及表示扩展名的点号。
dirname将变量值解释为文件名,并从变量中移除basename。
basename将变量值解释为文件名,并从变量中移除目录信息。
basenoextension作用类似basename,不过同时从变量中移除扩展名以及表示扩展名的点号。
foreach在空格分隔的列表环境中计算变量。
forfirst同foreach一起使用,传递给forfirst的文字值会被放置到foreach中第一个迭代中去。
fornotfirst同foreach一起使用,传递给fornotfirst的文字值会被放置到foreach中除第一个之外的每一个迭代中去。
forlast同foreach一起使用,传递给forlast的文字值会被放置到foreach中最后一个迭代中去。
fornotlast同foreach一起使用,传递给fornotlast的文字值会被放置到foreach中除最后一个之外的每一个迭代中去。
endfor同foreach一起使用,用来结束foreach块。
comment传递给comment的任何值都将被忽略(注释),可以使用任何除去换行和语句结束符之外的任何字符。
flag_overrides直接覆盖在mpc文件中的项目范围内的设置,使用两个由逗号分隔的变量,第一个变量对应一个文件名,第二个可以是任何变量。
marker同mpc语法中的verbatim关键字,可以用来在模板中指定占位符。如,<%marker(local)%>。
uc返回变量值的大写字母形式。
lc返回变量值的小写字母形式。
ucw返回变量值中每个单词首字母大写的形式,单词使用空格或者下划线来进行分隔。
normalize将连字号、斜杠、美元符号、括号以及点号转换为下划线。
reverse反转数组参数值的顺序。
sort对数组参数值进行排序。
uniq返回数组参数值的唯一集合。
multiple如果数组参数包含多个值,则返回真。
starts_with如果参数值(第一个参数)以给出的正则表达式(第二个参数)开始,则返回真。
ends_with如果参数值(第一个参数)以给出的正则表达式(第二个参数)结尾,则返回真。
contains如果参数值(第一个参数)匹配给出的正则表达式(第二个参数),则返回真。

表9中列出了在部分模板文件中可以作为参数的一些特殊名称。在表7中列出的变量也可以使用(除了<%temporary%>)。
表9. 可在模板文件中使用的特殊值
值描述
custom_types包含自定义生成类型的列表。更多信息参见自定义类型。
cwd完整的当前工作目录。
forcount仅可与foreach一起使用,表示从1开始计数的foreach元素索引。
project_name包含了当前正在被处理的项目名称。
project_file包含了当前正在被处理的项目的输出文件名。
ciao在GNUACE项目创建器模块中实现,指定项目使用了CIAO。
cppdir在BMake项目创建器模块中实现,返回由分号分隔的Source_Files列表值。
rcdir在BMake项目创建器模块中实现,返回由分号分隔的Resource_Files列表值。
make_file_name在VC6和EM3项目创建器模块中实现,返回对应到该特定项目类型的由项目名称与make文件扩展组合在一起的文件名。
tao在GNUACE项目创建器模块中实现,指定项目使用了TAO。
guid在VC7项目创建器模块中实现,返回一个在VC7项目文件中使用的基于项目的guid值。
vcversion在VC7项目创建器模块中实现,返回被创建项目类型的版本号:vc7返回7.00,vc71返回7.10,vc8返回8.00。
vpath在GNUACE项目创建器模块中实现,返回源文件相对于在GNU Make中的VPATH设置的路径值。

4.1.1.1 自定义类型
为了支持多个自定义生成类型,我们使用了一个特殊的关键词。custom_types关键词用来访问一系列的用户自定义类型。在foreach循环中,可以使用custom_type关键词来访问每一个自定义类型。
通过对custom_type关键词使用->操作符,我们可以访问一些常用的信息:输入文件、输入文件扩展名、命令、命令输出选项、命令标志以及输出文件目录,这些信息可以通过对应该类型的字段名来进行访问。同该自定义类型相关的输入文件可以使用custom_type->input_files来进行访问,每一个输入文件都会对应以系列的输出文件,这些输出文件可以在foreach循环中通过custom_type->input_file->output_files来访问。表10中列出了自定义类型字段。
表10. 自定义类型字段
值描述
command自定义类型中使用的命令。
commandflags不包括输出选项的命令行选项。
dependent告诉命令哪些生成的文件需要有依赖。
gendir针对特定文件类型的输出目录,该字段直接通过custom_type来访问则没有任何意义,应该始终在flag_overrides上下文中使用。
input_files与自定义类型关联的输入文件。
inputexts与自定义类型关联的输入文件扩展名。
libpath命令相关的库路径设置。
output_option可选的命令输出选项。
pch_postrule用来决定命令是否需要支持预编译头。
postcommand该命令允许用户在主命令处理完成自定义的输入文件之后,运行指定的命令。

例如,在下面的例子中,为生成自定义的输入文件创建了一个通用的makefile规则,显示了自定义类型的基本用法以及可以访问的一些字段,使用custom_types关键词的一个主要局限(可以在下面的例子中看到)就是不能为foreach指定别的变量名。

<%if(custom_types)%>
<%foreach(custom_types)%>
<%foreach(custom_type->input_files)%>
<%foreach(custom_type->input_file->output_files)%>
<%custom_type->input_file_output_file%>: <%custom_type->input_file%>
<%custom_type->command%> <%custom_type->commandflags%> $@
<%endfor%>
<%endfor%>
<%endfor%>
<%endif%>

4.1.1.2 群组文件
mpc文件语法中支持文件群组:可以在一个mpc文件中将一系列文件作为群组并在mpd文件中作为一个整体来进行访问。
文件(例如:Source_Files、Header_Files)可以按照组件小节中介绍的第二种方式来群组到一起。在mpd文件中,不同的组件可以通过在前面加上grouped_前缀来进行访问(如:grouped_source_files、grouped_header_files等)。表11. 群组文件字段名
字段名描述
files组中的输入文件。
component_name群组文件的组名。

下面是一个如何针对每一个组创建make宏的例子,展示了群组的基本用法和如何对字段进行访问。使用文件群组功能的主要局限(可以在下面的例子中看到)就是不能为foreach指定别的变量名。下面的例子中只使用了源文件,但是所有在mpc和mpd小节中列出的组件均可以使用。

<%if(grouped_source_files)%>
<%comment(Get back each set of grouped files)%>
<%foreach(grouped_source_files)%>
<%comment(This will provide the name of the group)%>
<%grouped_source_file%> = \
<%comment(Get all the source files in a single group)%>
<%foreach(grouped_source_file->files)%>
<%grouped_source_file->file)%><%fornotlast(“ \\”)%>
<%endfor%>
<%endfor%>
ifndef <%grouped_source_files->component_name%>
<%grouped_source_files->component_name%> = \
<%foreach(grouped_source_files)%>
<%grouped_source_file%><%fornotlast(“ \\”)%>
<%endfor%>
endif
<%endif%>

4.1.2 模板输入文件(mpt)
模板输入文件对于所有项目来说都通用的编译工具相关的特定信息。例如:编译器开关、中间文件目录、编译器宏等。每一个项目类型均可以为动态库、静态库、动态可执行文件和静态可执行文件提供相应的模板输入文件。但是,所有这些都不被MPC所实际需要。
模板输入文件相对于其他的MPC文件类型来说格式更加自由。它的语法类型mpc文件的语法,区别在于没有项目定义,并且只能使用一个关键词:conditional_include。该关键词用来包含其他可以在MPC包含文件搜索路径中找到的mpt文件,如果在conditional_include关键词后用双引号引起的名称没有找到,则忽略该名称,并且不会有任何的警告提示。mpt后缀会被自动添加到提供的名称后。
模板输入文件包含一系列的变量赋值操作。该变量赋值操作可以是下面两种方式中的任何一种:

variable_name = value1 “value 2”
variable_name += another_value

这些变量可以在对于的mpd文件中进行使用。
在一个mpt文件中,变量赋值操作可以组合到一起并进行命名,并在mpd文件中作为范围变量来进行使用。下面的例子显示了变量赋值组合使用的方式:

// mpt file
configurations = Release Debug
common_defines = WIN32 _CONSOLE
Release {
compiler_flags = /W3 /GX /O2 /MD /GR
defines = NDEBUG
}

Debug {
compiler_flags = /W3 /Gm /GX /Zi /Od /MDd /GR /Gy
defines = _DEBUG
}

conditional_include “vcfullmacros”

下面mpd文件片段使用了上面的mpt文件所提供的信息:

<%foreach(configurations)%>
Name = <%configuration%>
<%compile_flags%><%foreach(defines common_defines)%> /D <%define%>=1<%endfor%>
<%endfor%>

下面的内容则显示了上面例子的输出结果:

Name = Release
/W3 /GX /O2 /MD /GR /D NDEBUG=1 /D WIN32=1 /D _CONSOLE=1

Name = Debug
/W3 /Gm /GX /Zi /Od /MDd /GR /Gy /D _DEBUG=1 /D WIN32=1 /D _CONSOLE=1

如果一个foreach变量对应一个变量组名,则该变量组在该foreach范围类可用。

到这里,关于MPC的介绍就告一段落了。




wesom 发表于 2012-7-18 10:38:43

cmake也是很不错的选择
页: [1]
查看完整版本: MPC自动生成make文件普及贴