下面的内容紧接着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. 赋值关键字
赋值语句可以通过+=或者-=操作符来在关键字值中添加或者删除指定的值。 如果在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. 组件名称和缺省值
如果某个组件未在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( .. } 当MPC在生成由 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( source1.cpp … } conditional( 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关键字
在自定义组件和源文件组件、头文件组件和内联文件组件之间有特殊的交互作用:如果一个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目录。 |