在 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)\MSBuild\Microsoft.Cpp\v4.0 目錄下( Windows 64 上的默認安裝位置,在你的機器上可能會有所不同,可以通過注冊表 HKEY_LOCAL_MACHINE\Software\Microsoft\MSBuild\ToolsVersions\4.0 中的屬性 VCTargetsPath 找到位置);
4、根據編譯的配置不同進行一些本工程的特定配置,例如“ConfigurationType”、“CharacterSet”和"WholeProgramOptimization"
5、導入C++的屬性文件 Microsoft.Cpp.props , 和默認屬性文件 Microsoft.Cpp.Default.props 在相同的位置
6、根據編譯的配置不同導入不同的用戶配置屬性文件,主要是根據平台的不同進行屬性文件的導入,路徑為 %LOCALAPPDATA%\Microsoft\MSBuild\v4.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 文件;
http://www.cnblogs.com/WonKerr/archive/2010/06/27/VS2010_VCXPROJ_1.html
下面我們來了解一下 props 文件的基本規則:
1、在 PropertyGroup 元素中放置子元素就是定義屬性,屬性的值就是子元素的內容;例如
< OutputDriver > D:\ </ OutputDriver >
</ PropertyGroup >
定義來一個屬性 OutputDriver 值為 D:\
2、定義元素時可以通過條件來讓屬性定義在特定的條件下起作用,例如:
< 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、值的定義可以進行繼承,其實就是在原有的值的基礎上增加新的值,如下所示:
< LibraryPath > $(ImportFolder);$(LibraryPath) </ LibraryPath >
</ PropertyGroup >
如果屬性“ImportFolder”指定的目錄存在,那么將該目錄增加到“庫”的搜索路徑中,並且優先級在默認搜索路徑的前面;
5、可以使用 .net 的函數進行判斷或者屬性值的定義,例如定義解決方案文件所在的驅動器可以使用如下的定義:
< SolutionDriver > $([System.IO.Path]::GetPathRoot($(SolutionDir))) </ SolutionDriver >
</ PropertyGroup >
6、可以使用 import 元素導入其他的屬性設置文件,例如需要導入一個解決方案同名的屬性文件:
< Import Project ="$(UserRootDir)\Microsoft.Cpp.Common.user.props" />
</ ImportGroup >
說明:UserRootDir 是 C++ 默認屬性文件中定義的屬性,值為 $(LOCALAPPDATA)\Microsoft\MSBuild\v4.0\
7、如果導入的屬性文件需要在 Visual Studio 2010 的 "屬性管理器" (Property Manager)窗口中可以看到,那么在 ImportGroup 元素中增加特定的屬性即可,如下所示:
< Import Project ="$(ProjectPath).props" Condition ="Exists('$(ProjectPath).props')" />
</ ImportGroup >
則如果一個工程文件的目錄下存在 .vcxproj.props 將導入該文件,例如工程文件為 MyProj.vcxproj 並且在工程目錄下存在 MyProj.vcxproj.props文件則會自動導入文件 MyProj.vcxproj.props 屬性文件
8、如果在 Property Manager 窗口中希望看到的屬性文件名稱不是文件名,而是自定的名稱,那么可以再屬性文件中增加一個特定的屬性定義來實現,例如:
< _PropertySheetDisplayName > Cpp Common Properties </ _PropertySheetDisplayName >
</ PropertyGroup >
好了,基本規則我們已經掌握了(復雜的可以研究 VS2010 的 props 文件和查看 MSDN),下面來看看編譯和鏈接的選項如何設置:
1、編譯的選項放置在 ItemDefinitionGroup 元素的 ClCompile 子元素中,例如下面的定義:
< 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 子元素中,例如下面的定義:
< Link >
< ProgramDatabaseFile > $(SymbolFolder)$(OutputName).pdb </ ProgramDatabaseFile >
< ImportLibrary > $(ImportFolder)$(OutputName).lib </ ImportLibrary >
</ Link >
</ ItemDefinitionGroup >
在這個定義中,指定了輸出的私有的 pdb 文件的輸出全路徑和動態庫的導入庫的輸出全路徑;
3、庫的選項放置在 ItemDefinitionGroup 元素的 Lib 子元素中,例如下面的定義:
< Lib >
< LinkTimeCodeGeneration > true </ LinkTimeCodeGeneration >
</ Lib >
</ ItemDefinitionGroup >
定義了靜態庫生成時的代碼生成行為是“連接時代碼生成”;
參考: