在新的VS2010 C++工程文件中会发现有后缀为.vcxproj的文件,用notepad打开后可看到,有许多顶级MSBuild元素以一定的顺序布局。在Microsoft.Cpp.Default.props导入后,产生了大多数的组属性以及项目定义属性。还有,大多数的target在工程文件结尾处被导入。它们以Lable标志来区分。
这些有序的布局到底有什么作用?为什么会有多种属性组而不是只有一个?
有序布局是MSBuild的顺序评估模型的自然结果。如果一个工程文件由一个属性的两个定义构成,比如以下的,则后一个定义会覆盖掉前面的。因此,值"xyz"会在编译期被使用到。
abc
xyz
话说回来,让我们看看整个的布局。以下的MSBuild结构文件描述了一个简洁的布局。VS生成的任何.vcxproj文件以这种特定顺序包含了这些顶级MSBuild元素(或者它们可能包含每一个这种顶级元素的拷贝)。需要注意的是Label是一个只能由VS读取和写入的自定义标签且作为一个为编辑而使用的标记。它们没有其他的功能。
以下面的例子为参照,分析一下上面每一个元素是什么以及它们为什么会以这种方式进行排列布局。
Debug
Win32
Debug
x64
Release
Win32
Release
x64
{2B4C8F7A-A827-41E5-B80A-8EE6C0D3AF03}
Win32Proj
test
Application
true
v120
Unicode
Application
true
v120
Unicode
Application
false
v120
true
Unicode
Application
false
v120
true
Unicode
true
true
false
E:\osvr-build-ok\boost_1_62_0;$(IncludePath)
E:\osvr-build-ok\boost_1_62_0\lib64-msvc-12.0;$(LibraryPath)
false
E:\osvr-build-ok\boost_1_62_0;$(IncludePath)
E:\osvr-build-ok\boost_1_62_0\lib64-msvc-12.0;$(LibraryPath)
Use
Level3
Disabled
WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
Use
Level3
Disabled
WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
Level3
Use
MaxSpeed
true
true
WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
true
true
Level3
Use
MaxSpeed
true
true
WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
true
true
Create
Create
Create
Create
这是一个根节点。它指出了MSBuild的版本号,以及当该文件传入到MSBuild.exe中时,要执行的默认target名。
--------------------------------------------------------------------------------------------------
此处会包含大家所熟知的工程配置选项(比如Debug|Win32以及Release|Win32),比如:
Debug
Win32
Debug
x64
Release
Win32
Release
x64
此处包含工程的层级设置比如ProjectGuid,RootNamespace等。这些属性一般不会在该工程文件中的其他地方被覆盖。该组不是独立的配置,因此一般在工程文件中一般只有一Global组存在。
比如:
{2B4C8F7A-A827-41E5-B80A-8EE6C0D3AF03}
Win32Proj
test
---------------------------------------------------------------------------------------------
该属性列表包含VC++工程的默认设置。它包含了所有工程设置的定义,比如Platform,PlatformToolset,OutputPath,TargetName,UseofAtl等。以及同样也是所有的项目定义组属性都为每个已知项目组所知。一般来说,该文件中的属性不与特定工具相关的。
该属性组有一个附加的配置条件(比如Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"),且呈现多种版本,每个配置中一种。该属性组包含配置广泛的属性,这些属性在Microsoft.Cpp.props中控制系统属性列表的包含情况。比如,如果定义属性
比如:
Application
true
v120
Unicode
Application
true
v120
Unicode
Application
false
v120
true
Unicode
Application
false
v120
true
Unicode
---------------------------------------------------------------------------------------------
该属性列表直接或通过导入的方式,为许多工具相关的属性定义了默认值,比如编译器的优化,警告级别属性等。MIDL(微软接口定义语言)工具的类库名属性等。另外,它还导入许多的系统属性列表,这些列表是基于在上面的属性组中定义的配置属性。
比如:
---------------------------------------------------------------------------------------------
该组包含属性列表的导入,Build客制化部分(或客制化的Build规则,由于这些特性在早期的版本被调用过)。build客制化由三个文件来定义:a.targets文件,a.props文件以及.xml文件。而该导入组包含了.props文件。
---------------------------------------------------------------------------------------------
该组包含用户属性列表的导入。这些属性列表是通过VS的属性管理添加的。这些导入的排列顺序是与属性管理中相对应的。工程文件通常包含多种此类导入组的实例,每个工程配置都有一个相对应。
---------------------------------------------------------------------------------------------
UserMacros作为变量用来客制化build过程。比如,可定义一个用户宏$(CustomOutputPath)来定义客制化的输出路径,且也可用它来定义其他变量。需要注意的是,在vs2010中,尽管支持属性列表,但是由于不支持配置用户宏,IDE也没有去处理该组属性。
---------------------------------------------------------------------------------------------
该属性组通常以一个附加配置条件出现,也会看到多种属性组的实例,每个配置有一个。它与其他属性组的不同点是它没有label标签(实际上也可把它看作有一个label标签,只是这个标签是一个空字符串而已)。该组包含工程配置的层级设置。这些设置应用到指定item组部分的所有文件。build客制化item定义的元数据也是在这里初始化的。
比如:
true
true
false
E:\osvr-build-ok\boost_1_62_0;$(IncludePath)
E:\osvr-build-ok\boost_1_62_0\lib64-msvc-12.0;$(LibraryPath)
false
E:\osvr-build-ok\boost_1_62_0;$(IncludePath)
E:\osvr-build-ok\boost_1_62_0\lib64-msvc-12.0;$(LibraryPath)
---------------------------------------------------------------------------------------------
与上一条的属性组类似,只是它包含的是item的定义以及item定义的元数据,而不是属性的。
比如:
Use
Level3
Disabled
WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
Use
Level3
Disabled
WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
Level3
Use
MaxSpeed
true
true
WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
true
true
Level3
Use
MaxSpeed
true
true
WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
true
true
---------------------------------------------------------------------------------------------
包含工程中的item(源文件等),一般会有多个item组,每个item类型对应一个。
比如:
Create
Create
Create
Create
---------------------------------------------------------------------------------------------
以直接或导入方式,定义VC++的target,比如build,clean等
比如:
---------------------------------------------------------------------------------------------
该组包含了build客制化target文件的导入。
---------------------------------------------------------------------------------------------
以上的排序是确保编辑的工程文件,可通过IDE给出期望的结果。比如,当在属性页定义一个属性值时,IDE通常会在默认空标签的属性组中替换上所定义的属性。这确保系统属性列表自带的默认值会被用户自定义的值所覆盖。同样地也适用于在尾部引入的target文件,这是由于target文件使用了前面定义的属性以及它们自身通常不定义属性导致的。与此类似,在系统属性列表(包含在Microsoft.Cpp.props中)之后导入的用户属性列表也一样。这确保了用户能够覆盖任何由系统属性列表自带的默认值。
如果一个a.vcxproj文件不是依据这个布局来排列,则build结果可能不会跟所期望的一样。比如,可能在这个不同的布局文件里面,用户通过使用属性页定义的属性由于被系统属性页覆盖掉了,导致在编译期间可能不会被使用。
即使IDE设计的时长可以容忍,即使VS在任何.vcxproj文件上工作都非常顺利。若.vcxproject文件没有导入组属性列表,也将不会在属性管理视图中看到任何的用户属性列表。然而,当通过属性管理器视图添加一个属性列表时,VS会创建一个这样的标签导入组到.vcxproj文件的对应正确位置,且为新的属性列表添加导入。然而,如果.vcxproj文件与上面所描述的布局非常不一致,那么vs可能不知道正确位置是什么,因此,它可能会在一个随意的位置上去创建它。一般来说,VS IDE使用的自动推测会在不太重要的属性上协调.vcxproj文件的一致性。
可能会想知道VS是如何知道往哪儿写一个特定属性的。比如,当在一般属性页中设置UseOfAtl属性时,它会被写入Configuration属性组,而在同一个一般属性页中的TargetName属性则被写入缺少label的属性组中。那些都是通过属性自己告诉给vs的。实际上是通过属性页xml文件的属性模式来进行的。一个这样的信息片段包含一个在目标文件中(要写入值的那个文件)的Rule规则属性的优先位置信息。通过利用已经存在的label标签来定义该优先位置。label标签用来区分同样类型(比如两个属性组)的不同顶级元素。因此,一个属性能够在属性页xml文件中被描述,当在工程文件中要定义时,属性组存放在该xml文件中,
最后,以下是MSBuild文件的架构来说明一个vs2010的属性列表(.props)文件的布局。基于以上的讨论来推断布局元素的功能。