在 VS2010 中,C 的工程文件已经和 2005 / 2008 有了很大的不同,而是完全采用 MSBUILD 的属性方式进行表达,并且可以让用户通过一次性的配置而对所有的属性进行自定义;
根据我的理解和实际使用中发现的一些信息,现整理成文与大家分享:
一、工程文件分为三个主要部分:初始化配置信息、默认配置信息和本地配置信息;
二、配置信息全部采用属性文件的方式进行组织,并且可以通过“导入”进行引用;
三、属性文件中可以包含工程属性、编译属性和链接属性的配置;
四、所有的配置项可以通过使用过滤条件来决定配置是否生效;
五、通过内置的表达式可以访问注册表或者调用.NET函数;
首先来说工程文件的组成
1、工程文件的起始部分是 ProjectConfigurations , 里面包含 Configuration(Debug 或者 Release)和 Platform(Win32、x64等),这个部分配置了工程编译目标的个数,默认配置只有两个:Win32 平台下的 Debug 和 Release;
2、其后是一切全局属性的配置,关键是 Keyword 属性,该属性决定了很多后续的默认属性文件的导入行为,例如是否导入 MFC 的默认属性设置;
3、导入 C 默认属性文件 Microsoft.Cpp.Default.props , 该文件在 C:Program Files (x86)MSBuildMicrosoft.Cppv4.0 目录下( Windows 64 上的默认安装位置,在你的机器上可能会有所不同,可以通过注册表 HKEY_LOCAL_MACHINESoftwareMicrosoftMSBuildToolsVersions4.0 中的属性 VCTargetsPath 找到位置);
4、根据编译的配置不同进行一些本工程的特定配置,例如“ConfigurationType”、“CharacterSet”和"WholeProgramOptimization"
5、导入C 的属性文件 Microsoft.Cpp.props , 和默认属性文件 Microsoft.Cpp.Default.props 在相同的位置
6、根据编译的配置不同导入不同的用户配置属性文件,主要是根据平台的不同进行属性文件的导入,路径为 %LOCALAPPDATA%MicrosoftMSBuildv4.0,这些文件没有配置任何的内容,如果你删除它们,那么在打开 C 工程文件时,IDE 会自动的创建它们;请注意关键就在这里,这些文件的目的就是为了用户可以对C 工程的属性进行自定义,并且是“用户”级别的,也就是说在这些文件中增加的属性将对你所有的工程起作用;
7、根据编译的配置不同对“编译”、“连接”、“库”等选项进行工程的本地配置,例如:“警告等级”、“优化选项”和“宏”的定义等;
8、工程中包含的文件,例如 ClCompile 包含的是需要进行 C/C 编译的文件,ResourceCompile 包含的是需要 RC 进行编译的资源文件,等等;
9、导入C 的编译目标属性定义文件“Microsoft.Cpp.targets”,和默认属性文件的位置相同;
好了,到此为止,工程文件的9个组成部分已经大致的分析出来了,如果对 Microsoft.Cpp.Default.props 、Microsoft.Cpp.props 和 Microsoft.Cpp.targets 进行简单分析就会发现,它们都是首先根据编译的目标平台导入对应的平台定义的文件,然后是自己的属性定义,并且这些属性都会在没有定义的情况下才会生效,避免冲掉前面的定义;并且还可以发现一个秘密,那就是在编译系统还为每一个工程提供了一个免费的属性定会以文件,*.vcxproj.user ,这个文件如果存在,那么将会被自动导入;
通过以上分析,我们可以发现C 的编译系统其实除了工程文件本身之外(毕竟不可能把所有的编译选项都放到工程文件中)还另外提供来三个级别的属性配置文件:
1、工程级别的,就是 *.vcxproj.user 文件;
2、用户级别的,就是 Microsoft.Cpp.$(Platform).user.props 文件
3、系统级别的,就是 前面提供到 Microsoft.Cpp.Default.props 、Microsoft.Cpp.props 和 Microsoft.Cpp.targets 文件;。
下面我们来了解一下 props 文件的基本规则:
1、在 PropertyGroup 元素中放置子元素就是定义属性,属性的值就是子元素的内容;例如:
代码语言:javascript复制//添加头文件和库
<PropertyGroup>
<OutputDriver>D:</OutputDriver>
</PropertyGroup>
定义来一个属性 OutputDriver 值为 D:
2、定义元素时可以通过条件来让属性定义在特定的条件下起作用,例如:
代码语言:javascript复制//数据
<PropertyGroup>
<SingleFolder Condition="'$(SingleFolder)' == ''">false</SingleFolder>
</PropertyGroup>
只有当 SingleFolder 属性没有定义或者定义的值为空时,将 SingleFolder 的属性定义为 false ; 如果属性组中只有一个属性,或者一个属性组中的多个属性有相同的条件,则可以把条件放置到 PropertyGroup 元素中;
3、条件表达式可以使用 “==”、“!=” 进行判断,也可以对数值进行“>”、“>=”、“<”、“<=”判断,并且可以对目录进行“Exists”和“HasTrailingSlash”判断,同时可以对表达式进行 “!” 或对多个表达式进行 “AND”、“OR”等操作,可以参考 http://msdn.microsoft.com/en-us/library/7szfhaft.aspx
4、值的定义可以进行继承,其实就是在原有的值的基础上增加新的值,如下所示:
代码语言:javascript复制//如果使用当前数据库,一定要保存文件否则会出错,
//当前数据库有自动保存操作,即使不操作,如果不保存,也会出错。
<PropertyGroup Condition="Exists('$(ImportFolder)')">
<LibraryPath>$(ImportFolder);$(LibraryPath)</LibraryPath>
</PropertyGroup>
如果属性“ImportFolder”指定的目录存在,那么将该目录增加到“库”的搜索路径中,并且优先级在默认搜索路径的前面;
5、可以使用 .net 的函数进行判断或者属性值的定义,例如定义解决方案文件所在的驱动器可以使用如下的定义:
代码语言:javascript复制在这里插入代码片
<PropertyGroup Condition="'$(SolutionDriver)' == ''" >
<SolutionDriver>$([System.IO.Path]::GetPathRoot($(SolutionDir)))</SolutionDriver>
</PropertyGroup>
6、可以使用 import 元素导入其他的属性设置文件,例如需要导入一个解决方案同名的属性文件:
代码语言:javascript复制<ImportGroup>
<Import Project="$(UserRootDir)Microsoft.Cpp.Common.user.props" />
</ImportGroup>
说明:UserRootDir 是 C 默认属性文件中定义的属性,值为 $(LOCALAPPDATA)MicrosoftMSBuildv4.0
7、如果导入的属性文件需要在 Visual Studio 2010 的 “属性管理器” (Property Manager)窗口中可以看到,那么在 ImportGroup 元素中增加特定的属性即可,如下所示:
代码语言:javascript复制<ImportGroup Label="PropertySheets">
<Import Project="$(ProjectPath).props" Condition="Exists('$(ProjectPath).props')" />
</ImportGroup>
则如果一个工程文件的目录下存在 .vcxproj.props 将导入该文件,例如工程文件为 MyProj.vcxproj 并且在工程目录下存在 MyProj.vcxproj.props文件则会自动导入文件 MyProj.vcxproj.props 属性文件
8、如果在 Property Manager 窗口中希望看到的属性文件名称不是文件名,而是自定的名称,那么可以再属性文件中增加一个特定的属性定义来实现,例如:
代码语言:javascript复制<PropertyGroup>
<_PropertySheetDisplayName>Cpp Common Properties</_PropertySheetDisplayName>
</PropertyGroup>
好了,基本规则我们已经掌握了(复杂的可以研究 VS2010 的 props 文件和查看 MSDN),下面来看看编译和链接的选项如何设置:
1、编译的选项放置在 ItemDefinitionGroup 元素的 ClCompile 子元素中,例如下面的定义:
代码语言:javascript复制<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>ISF_BUILD_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>ISF/Stable_Headers.h</PrecompiledHeaderFile>
<PrecompiledHeader>Use</PrecompiledHeader>
</ClCompile>
</ItemDefinitionGroup>
在这个定义中,首先增加来一个宏定义“ISF_BUILD_DLL”,然后是定义预编译头文件的名称“ISF/Stable_Headers.h”,而不是默认的“stdafx.h”,最后定义来预编译头文件的使用规则是“使用预编译头文件”,这就要求所有的C/C 文件必须在最前面包含头文件 “ISF/Stable_Headers.h”,除非在文件上指定不使用预编译头文件;
2、链接的选项放置在 ItemDefinitionGroup 元素的 Link 子元素中,例如下面的定义:
代码语言:javascript复制<ItemDefinitionGroup>
<Link>
<ProgramDatabaseFile>$(SymbolFolder)$(OutputName).pdb</ProgramDatabaseFile>
<ImportLibrary>$(ImportFolder)$(OutputName).lib</ImportLibrary>
</Link>
</ItemDefinitionGroup>
在这个定义中,指定了输出的私有的 pdb 文件的输出全路径和动态库的导入库的输出全路径;
3、库的选项放置在 ItemDefinitionGroup 元素的 Lib 子元素中,例如下面的定义:
代码语言:javascript复制<ItemDefinitionGroup>
<Lib>
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
</Lib>
</ItemDefinitionGroup>
定义了静态库生成时的代码生成行为是“连接时代码生成”;
嗯,到了该结束的时候了,更多的选项还是留待读者自己研究发现吧…
总结
提示:这里对文章进行总结: 例如:以上就是今天要讲的内容,本文仅仅简单介绍了的函数和方法。