Visual Studio 2010中C++项目升级指南

如何升级?

Visual Studio 2010支持来自VC6、Visual Studio 2002、Visual Studio 2003、Visual Studio 2005和Visual Studio 2008的升级。同Visual Studio的早期版本一样,你可以选择通过IDE的转换向导或者是命令行工具(Devenv/upgrade)来完成升级。

以下是升级过程中的一些建议:

1) 把升级环境与构建环境设置成相同的

升级过程中会尝试加载一些文件,另外还会评估一些值。如果你的项目中所使用的值不是在项目文件中自己定义的,例如在环境变量中定义的值,那就需要在升级之前事先设置好这些环境变量。如果这些环境变量设置不当,则可能会因为无法对值进行评估而出现警告或错误。

2)  在升级之前确保你已经安装了必要的平台

在一台没有可用平台的机器上对项目进行转换,可能会导致转换错误。例如,如果你想在Visual Studio Professional SKU中对安腾平台的一个项目进行转换,由于它不支持安腾平台,你就会看到像下面这样的转换出错信息:

  1. Failed to upgrade 'Debug|<Itanium>'. Please make sure you have the corresponding platform 
  2. installed under '%vctargetspath%\platforms\Itanium'. Cannot load the project due to a corrupt 
  3. project file. The following error has occurred during XML parsing:  
  4. File: D:\Sample\ConsoleApp\ConsoleApp.vcproj  
  5. Line: 28  
  6. Column: 5  
  7. Error Message:  
  8. System error: -2147154677.  
  9. The file 'D:\Sample\ConsoleApp\ConsoleApp.vcproj' has failed to load.

  这是因为转换过程需要那些平台的属性值才能成功进行。你可以通过查看以下目录获悉已安装了哪些平台:%ProgramFiles%\MSBuild\Microsoft.cpp\V4.0\Platforms (或者查看x64平台下的 %ProgramFiles(x86)%\MSBuild\Microsoft.cpp\V4.0\Platforms )。

  3) 如果可能的话,使用本地的多定向支持来对Visual Studio 2008工具集进行构建

  在Visual Studio  2010中,我们新增了本地的多定向支持(Multi-Targeting),通过使用新的基于MSBuild的项目系统,你可以在Visual Studio 2010的IDE中为Visual Studio 2008工具集构建目标。关于这一特性,你可以看看这篇博客。我们建议用户,特别是拥有大型代码库的用户,在升级时先利用这一特性在Visual Studio 2010中构建Visual Studio 2008工具集的程序。这样,当你在升级过程中碰到问题,就可以把那些跟项目系统或构建系统相关的问题与工具的问题区别开来。这将会让你更加平滑地升级到Visual Studio 2010 工具集。

  一旦升级完成,属性表文件(.Visual Studio props)就会被转换成新的格式(.props)。同样,项目文件(.vcproj)也会被转换为新的格式(.vcxproj)。值得注意的是,新的项目文件会与旧的项目文件同时生成。在转换过程中还产生了一种新的文件类型(.filter .vcxproj),该过滤器文件包含用来显示解决方案的资源管理器文件夹信息。该过滤器信息原本是项目文件的一部分,然而这种变化是必要的,因为只要项目文件发生变化,MSBuild就会请求重新构建。通过在一个单独的文件中保存过滤器信息,就可以在避免重新构建整个项目的情况下修改过滤器。

  注意:升级过程不会转换.user文件。因此,你的调试和部署设置在转换后将不会被保留。

  在Visual Studio 2010中,一个新的命令行升级工具 VCUpgrade.exe也被加入了进来。此命令行工具,适合在只有一个项目的情况下进行升级,因为它无法把解决方案文件作为输入,并将其解析成项目文件。VCUpgrade.exe位于 $(Visual Studio InstallDir)\ common7 \ Tools目录下,该工具也将附带在WinSDK的下一个版本中,这样用户就可以在没有Visual Studio IDE的情况下用命令行对WinSDK中的项目文件进行升级。

  升级过程中的警告

  以下是转换过程中,你可能会遇到的一些常见警告:

  1) 链接器输出目录

  在升级时你可能会看到的一个警告是MSB8012:$(TargetPath)和链接器的OutputFile属性的值不匹配:

  1. - MSB8012: $(TargetExt) ('.dll') does not match the Linker's OutputFile 
  2. property value 'C:\foo\Debug\MFCActiveX.ocx' ('.ocx') in project configuration 'Debug|Win32'. 
  3. This may cause your project to build incorrectly. To correct this, please make sure that $(TargetExt) 
  4. property value matches the value specified in %(Link.OutputFile).  
  5. - MSB8012: $(TargetPath) ('C:\foo\Debug\MFCActiveX.dll') does not match the Linker's OutputFile 
  6. property value 'C:\foo\Debug\MFCActiveX.ocx' ('C:\foo\Debug\MFCActiveX.ocx') in project 
  7. configuration 'Debug|Win32'. This may cause your project to build incorrectly. 
  8. To correct this, please make sure that $(TargetPath) property 
  9. value matches the value specified in %(Link.OutputFile).

  Link.OutputFile是在属性页中Linker-> General -> Output File 这一项中定义的值。默认情况下,它的值是$(OutDir)$(TargetName)$(TargetExt),与$(TargetPath)相同。当我们把一个应用程序从之前的版本转换过来时,并没有办法可以很好地解析出Link.OutputFile被$(TargetName)和$(TargetExt)的值,因为不同的用户可能用不同的方法对其进行了赋值。为了解决这一点,我们决定在转换过程中保留Linker.OutputFile中的值。在转换之后,$(TargetName)将默认为$(ProjectName),$(TargetExt)将默认为该类应用程序的默认扩展名:动态库文件为.dll,静态库为.lib,应用程序则为.exe,而Link.OutputFile值则将被保留。如果Link.OutputFile与$(TargetPath)不同,警告MSB8012会被记录在转换日志中。在构建应用程序时你也会看到同样的警告。

  $(OutDir),$(TargetName)和$(TargetExt)在“常规”属性页中分别对应“Output Directory”,“Target Name”,“Target Extension”。你可以手动更改这些属性的值,这样你就不会再看到警告了。

  -如果你的项目生成了导入库(Linker -> Advanced -> Import Library),而且链接器的输出目录不是默认目录,那么你可能还需要更改导入库的输出文件夹。否则,生成的导入库所在的目录就可能会与链接器的输出不同。

  -调试。转换后命令被设为默认的$(TargetPath)。你可能需要做一些改动,这样当按下F5 (Debugging)或 Ctrl + F5 (Start without debugging)之后才能加载正确的可执行程序。

 

 2) 属性表的排序

  如果你的应用程序有属性表,那么在转换过程中你可能会遇到下面的警告中:

  1. - All user macros reported below for configuration 'Debug|Win32' are used before their definition, 
  2. which can cause undesirable build results; this is not supported in this release. 
  3. You can resolve this by changing the inclusion order            
  4. of the consuming property sheets and making sure they come after the property sheets defining the user macros.  
  5.             - MSB4211: C:\foo\PropertySheet\foo.props; 
  6. The property "MyIncludePath" is being set to a value for the first time, 
  7. but it was already consumed at "C:\foo\PropertySheet\bar.props".

  该警告归因于MSBuild对其属性的评估方式:MSBuild按顺序依次评估其属性值。如果在派生的属性表中定义的属性在父属性表中被使用,那么其值就会被设为空。然而,VCBuild采用的是延迟评估方式。这样,即使是在派生属性表中定义的属性也可以在父属性表中使用。若要解决此问题,请按照警告消息改变属性表中的顺序,从而确保属性在定义后才被使用。

  升级后的行为变化

  尽管底层的构建系统已经发生了改变,但我们仍尽力让用户在迁移到Visual Studio 2010后有相同的使用体验。另外,我们还采取了一些措施来改进构建体验或是迎合MSBuild的某些特定要求。因此,一旦你迁移到Visual Studio 2010,就可能就会注意到以下一些变化。

  1) 从“解决方案依赖”到“项目到项目的引用”

  如果在一个旧版本Visual Studio版本下进行编译的C++程序被转换到Visual Studio 2010后,解决方案级定义的项目依赖关系便会被转化成项目到项目的引用(project to project references)。这一变化确保了C + +项目依赖关系能够在项目文件中被捕获。下面就是一个项目到项目的引用在项目文件中的样子:

  1. <ItemGroup>
  2. <ProjectReference Include="..\Cpp\Cpp.vcxproj">
  3. <Project>{c58574bf-9dd8-4cf8-b5b6-6551f2f3eece}</Project>
  4. <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
  5. </ProjectReference>
  6. </ItemGroup>

  在项目文件中存放依赖关系有几个优点。首先,用户可以在不用解决方案的情况下构建一个项目,相关的项目会自动被构建。第二,它为那些拥有巨大的代码树又可能不会使用解决方案文件的用户提供了方便。 此外,许多客户都有好几个解决方案文件,每个文件都包含项目的不同子集。通过这种方式就可以让用户避免为每个解决方案设置依赖关系。另一个重要因素就是,使用项目到项目的引用使得构建过程更加可靠,尤其是在多核的环境下。这与Visual Studio早期版本的情况也是一致的。

  -如果某个C#程序依赖于一个C++程序,并且这一依赖只使用了解决方案依赖关系表示,那么现在的这种转换并不会把解决方案依赖关系转换成项目到项目的引用。你可能会碰到因为不正确的构建顺序而导致的构建错误,尤其是使用命令行直接进行MSBuild构建的时候。要解决该问题,你得为C#和C++程序手动设置项目到项目的引用。

  -在Visual Studio 2010中设置新的构建依赖关系时,通常都要用项目到项目的引用替代解决方案依赖。

  2)  项目到项目引用属性的变化

  转换后,CopyLocalDependencies和UseDependenciesInBuild属性会被去除。“Use in Build”属性改为“Reference Assembly Output”,以更好地表示该属性的作用。另外两个属性:“Link Library Dependencies”和“Use Library Dependency Inputs”增加到了被引用的那个项目中,以便被引用的项目控制它的输出是否被传递到引用它的那个项目中。以下是Visual Studio 2008和Visual Studio 2010项目到项目引用属性的对比图。

对比图

  -把“Reference Assembly Output”设为“false”会允许该项目成为项目到项目引用的一部分,从而设置构建依赖关系,而它的输出不会被传递到引用它的那个项目的CL中。此属性用于被托管的程序。

  -把“Link Library Dependencies”设为“false”,会允许该项目成为项目到项目引用的一部分,从而设置构建依赖关系,而它的输出不会被传递到引用它的那个项目的链接器中。

  3)VC + +目录变化

  Visual Studio 2010不再支持在Tools->Options中设置VC + +目录。相反,Visual Studio 2010引入了用户设置文件(Microsoft.cpp.<Platform>.users.props)以控制包括全局搜索路径在内的全局设置。这些文件位于$(USERPROFILE)\appdata\local\microsoft\msbuild\v4.0目录。当迁移到Visual Studio 2010后,Visual Studio 2005或Visual Studio 2008的目录设置将会被移动到这些用户文件中。这些文件全局设置也会被导入到所有转换而来的以及新创建的项目中。

  下面是通过用户界面更改设置文件的步骤

  ◆点击View.Property Manage打开属性管理器。

  ◆展开项目节点,然后是Configuration|Platform 节点,你将会看到针对每个Configuration|Platform 的"Microsoft.cpp.<Platform>.users" 文件。这些就是提供全局设置的文件,跟以前的 tools/Options/VC++ 目录是类似的。

  ◆复选"Microsoft.cpp.<Platform>.users",右键点击弹出窗口的属性页

  ◆在属性窗口页中,单击左窗格中的"VC++ Directories",添加诸如“Include Directories"的目录的路径,以分号隔开。

  ◆请确保关闭Visual Studio之前保存了刚才的设置。

  ◆重新启动Visual Studio,新的设置将会生效。

  -注意:如果你想只改变一个项目的设置,你可以右键单击该项目,弹出属性页。更改“VC++ Directories”的设置,这些设置便被保存到项目文件中。

  4) 自定义生成规则的改变

  在Visual Studio 2008中,自定义生成规则是由.rules文件定义的。转换后,.rules文件将会变成三个单独的文件:.targets, .xml以及.props。转换后,你会在.rules所在的目录中发现这三个文件。请注意,没有UI可用于添加新的自定义生成规则。

  5) 改动检查的变化

  当你按下F5键,检查改动情况的窗口可能每次会弹出,即使是刚刚才重新构建了一次。你可以参照这个博客来解决这一问题。最有可能的的原因是某些文件被列为了该项目的的一部分,但却不在磁盘上了。由于这些文件是项目文件的一部分,更新检查机制每次都会检查它们是否存在。而如果在磁盘上不存在该文件,Visual Studio 2010就会认为需要重新构建一次。解决方法是,如果那些文件不存在了,请把它们从项目文件中删除掉。

  Visual Studio 2010中的一个限制是,它还不支持可控增量构建(Managed Incremental build)。我们正在研究如何在未来版本中支持这一功能。

  在Visual Studio 2010转换过程中的已知问题

  以下是在Visual Studio 2010的转换过程中已知存在的一些问题:

  1)  TargetFrameworkVersion

  转换后,托管的C++项目将设为默认为针对4.0 Framework。这种设计背后的原因是,Visual Studio 2010构建器的不支持2.0,3.0或3.5 Framework。Visual Studio 2008的构建器则必须使用2.0,3.0或3.5 Framework。为了使转换后的C++应用程序能马上就能运行,我们决定将C++程序的默认TargetFrameWorkVersion 属性改成4.0。可以通过下面其中一种方法把C++应用程序重新定向到其它Framework(如3.5):

  编辑vcxproj文件,并在第一组定义中加入以下内容:

<TargetFrameworkVersion>器v3.5“/ TargetFrameworkVersion”

  打开Visual Studio 2010的命令行,设置TargetFrameworkVersion = v3.5,然后从命令行运行devenv.exe。这将把所有C++应用的目标框架设为v3.5。

  在构建程序时传递/p:TargetFrameworkVersion = v3.5参数给MSBuild:MSBuild my.vcxproj /p:TargetFrameworkVersion =v3.5

  请注意,要在安装了Visual Studio 2008的情况下才能把应用程序的定向到2.0,3.0或3.5 Framework。

  对于C#/ VB应用程序,如果目标Framework已经安装在了计算机上,转换过程中则不会改变目标Framework。 如果目标Framework没有安装,你可以选择下载所需要的Framework或将目标Framework升级到4.0版。

  -如果在你的解决方案中混合有CSharp / VB/托管C++项目,你可能会碰到它们分别针对不同目标Framework的情况。如果被引用项目的目标Framework比引用它的项目的Framework版本还高,你可能会收到警告MSB3258:

-The primary reference “foo, Version=x.x.xxx.xxxx, Culture=neutral, processorArchitecture=x86” has an indirect dependency on the .NET Framework assembly “mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089” which has a higher version “4.0.0.0” than exists in the current target framework. The version found in the current target framework redist list is “2.0.0.0”. By including the primary reference you may get compile and/or runtime errors.

  -你也可能如C4691这样的警告:type referenced was expected in the unreferenced assembly ‘file’, type defined in current translation unit used instead, if the Framework version of the referencing project is higher.

  -你需要把解决方法中的所有项目设为同一个版本的Framework。

  2)     在makefile的"Output"属性中使用引号

  如果某个项目的makefile中"Output"属性里有引号,转换将无法进行。解决方法是在转换前把"Output"属性里的引号去掉。

  1. <Tool
  2. Name="VCNMakeTool"
  3. BuildCommandLine="nmake /nologo &quot;OUT=$(OutDir)&quot; &quot;OBJ=$(IntDir)&quot;"
  4. ReBuildCommandLine="nmake /a /nologo &quot;OUT=$(OutDir)&quot; &quot;OBJ=$(IntDir)&quot;"
  5. CleanCommandLine="nmake /nologo &quot;OUT=$(OutDir)&quot; &quot;OBJ=$(IntDir)&quot; clean"
  6. Output="&quot;$(OutDir)hlekernel.rom&quot; &quot;$(IntDir)hlekernel_gen.inl&quot; &quot;$(IntDir)hlekernel_gen.h&quot;"
  7. PreprocessorDefinitions="WIN32;_DEBUG"
  8. IncludeSearchPath=""
  9. ForcedIncludes=""
  10. AssemblySearchPath=""
  11. ForcedUsingAssemblies=""
  12. CompileAsManaged=""
  13. />

  3)  $(IntDir) 和 $(OutDir)中的反斜杠。

  $(IntDir)和$(OutDir)分别对应属性页中的“General -> Intermediate Directories”和“General -> Output Directories”。为了统一$(IntDir)和$(OutDir)的格式,如果没有以"\"结尾的话,转换过程中会自动在它们的属性值末尾追加“\”。如果末尾有“/”的话将会被删掉。

  不过,如果$(IntDir)或$(OutDir)用在makefile或自定义生成中,Exec任务会用到它,这就可能导致构建过程中断。

  在makefile中,如果它们以"\"结尾,nmake工具就无法评估它的值。例如,使用如下命令,

  BuildCommandLine="nmake /nologo &quot;OUT=$(OutDir)&quot; &quot;OBJ=$(IntDir)&quot;"

  因为$(OutDir)和$(IntDir)末尾有“\”,nmake工具不能正确地将其展开,OUT和OBJ的值会被置为空。为了解决这个问题,你需要在属性页删除$(IntDir)和$(OutDir)中的"\"。

  如果$(OutDir)或$(IntDir)被传递给自定义生成,包括生成事件、自定义生成工具,以及自定义生成步骤,你可能会遇到构建失败,其原因是“\”可能被这些工具当成了转义字符。例如,如果你有一个跟下面类似的预生成事件:

  1. <PreBuildEvent>
  2. <Command>cl /c  /Fo"$(IntDir)" "$(ProjectDir)\win32.cpp"</Command>
  3. </PreBuildEvent>

  命令行是:cl /c /Fo"Debug\" "C:\foo\win32.cpp" instead of cl /c /Fo"Debug\\" "C:\foo\win32.cpp"

  要解决这个问题,在传递给/Fo的值中的添加一个额外的“\”(C/C++ -> Output Files -> Object File Name),以补偿被当作转义字符的"\"。

  4) 解决方案资源管理器界面

  在Visual Studio 2010中,解决方案资源管理器里的文件和文件夹显示由.vcxproj.filters文件控制。如果你有名字相同但位置不同的文件夹,例如“Native\Include”和 "Include",那么在转换后,第二个"Include"和它里面的文件将不会显示在解决方案资源管理器中。若要解决此问题,你可以重新在解决方案资源管理器创建这个文件夹,并将相应的文件添加到文件夹中。

  5) 只读的项目文件或目录

  当你从一个只读目录中开始升级,或是对以前只读的项目文件进行重新转换时,都有可能碰到问题。务必确保这些目录和文件都是可写的,除非你能对这些文件自动进行检查,就像在集成资源管理系统(integrated source control system)中能做到的那样。

posted @ 2010-04-28 09:36  至尊王者  阅读(10127)  评论(2编辑  收藏  举报