Visual Studio 2010 C++ Project Upgrade Guide
Hi, my name is Li Shao. I am Software Design Engineer in Test on the C++ team. As has been mentioned in many of the blogs, for example, recent blog by Felix Huang, the blog by Andreea Issac and my earlier blog, one of the major improvements in this release is that the C++ build system is moving from the VCBuild based system to the MSBuild based build system. The C++ project system is also built on top of the MSBuild build system. There were a large amount of changes that went in to make this happen. Our goal is to make the upgrade process as smooth as possible from the end user point of view. There are some limitations, known issues or by design changes that you may run into during the upgrade process. The goal of this post is to give you some guidance and explain some of the issues that you may run into when upgrading to VS2010.
How to Upgrade?
VS2010 supports upgrading from VC6, VS2002, VS2003, VS2005 and VS2008. As in previous versions of Visual Studio, upgrade can be done either through IDE conversion wizards or from the command line (Devenv /upgrade).
Here are the recommendations for upgrading your applications:
1) Set up the upgrade environment the same as your build environment
The upgrade process will try to load files and evaluate values during upgrade. If your projects use values that are not defined by the project files themselves, for example, values defined by environment variables, it is required that these environment variables are set up before doing the upgrade. Without these environment variables properly set up, you may get conversion warnings or errors caused by unevaluated values.
2) Make sure you have the required platforms installed before doing upgrade
Converting a project on a machine without all the available platforms for the project will result in a conversion error. For example, if you try to convert a project with Itanium Platform on Visual Studio Professional SKU, which does not support the Itanium platform, you will see a conversion error like the following:
Failed to upgrade ‘Debug|<Itanium>’. Please make sure you have the corresponding platform installed under ‘%vctargetspath%\platforms\Itanium‘. Cannot load the project due to a corrupt project file. The following error has occurred during XML parsing:
System error: -2147154677.
The file ‘D:\Sample\ConsoleApp\ConsoleApp.vcproj’ has failed to load.
This is by design as the conversion needs to evaluate the properties in the missing platforms to do a successful conversion. You can verify which platforms are installed on your machine by looking in the following directories: %ProgramFiles%\MSBuild\Microsoft.cpp\V4.0\Platforms (or %ProgramFiles(x86)%\MSBuild\Microsoft.cpp\V4.0\Platforms on x64 machine) for the Platforms installed on the machine.
3) Use native Multi-Targeting to build against VS2008 toolset first if possible
In VS 2010, we added Native multi-targeting which allows you to build against the Visual Studio 2008 toolset from within the VS2010 IDE using the new MSBuild-based project system. You can take a look of this blog for the details about this feature. We recommend customers, especially customers with large codebases, take advantage of this feature by using VS2010 to build against VS2008 toolset first when upgrading. This can help isolate any project system/build system related issues from the tools issues that you may run into after upgrade. This will make the move to the VS2010 toolset much smoother.
Upon upgrade, the property sheet files (.vsprops) are converted to their new format (.props). Likewise, project files (.vcproj) are converted to their new format (.vcxproj). Note, the new project files are generated alongside the old project files. A new file type (.filter.vcxproj) is also generated during conversion. The filter files contain the information that is used to display folders in the solution explorer. This filter information was originally part of the project file. This change was necessary because MSBuild requests a rebuild whenever the project files changes. By storing filter information in a separate file, the filter can be changed without triggering a rebuild of the entire project.
Note: Upgrade process will not convert the .user file. As a result, your debugging and deployment settings will not be preserved after conversion.
In VS2010, a new command line upgrade tool, VCUpgrade.exe is introduced. This command line tool is suitable for upgrading applications with only one project as it cannot take in solution file as input and parse solution information into project files. VCUpgrade.exe is located at: $(VSInstallDir)\common7\Tools directory. This tool will also be shipped in the next release of WinSDK so that the users can do command line upgrade of the project files shipped in WinSDK without Visual Studio IDE.
Warnings during upgrade
Here are some of the common warnings that you may run into during conversion:
1) Linker output directory
One of the warnings you may see when upgrading you applications is MSB8012: $(TargetPath) and Linker’s OutputFile property value does not match:
– MSB8012: $(TargetExt) (‘.dll’) does not match the Linker’s OutputFile property value ‘C:\foo\Debug\MFCActiveX.ocx’ (‘.ocx’) in project configuration ‘Debug|Win32’. This may cause your project to build incorrectly. To correct this, please make sure that $(TargetExt) property value matches the value specified in %(Link.OutputFile).
– MSB8012: $(TargetPath) (‘C:\foo\Debug\MFCActiveX.dll’) does not match the Linker’s OutputFile property value ‘C:\foo\Debug\MFCActiveX.ocx’ (‘C:\foo\Debug\MFCActiveX.ocx’) in project configuration ‘Debug|Win32’. This may cause your project to build incorrectly. To correct this, please make sure that $(TargetPath) property value matches the value specified in %(Link.OutputFile).
Link.OutputFile is the value defined at Linker -> General -> Output File on the property page. By default, its value is $(OutDir)$(TargetName)$(TargetExt), which is the same as the value of $(TargetPath). When we convert an application from a previous version, however, there is not an easy way for conversion to parse Link.OutputFile to figure out what exactly the values are for $(TargetName) and $(TargetExt), as different customers may have formatted them in different ways. To work around that, we decided to preserve the value of Linker.OutputFile during conversion. After conversion, $(TargetName) will default to $(ProjectName). $(TargetExt) will default to the default extension for the application type: .dll for Dynamic Library, .lib for Static Library and .exe for Application. Link.OutputFile value will be preserved. Warning MSB8012 will be issued in the conversion log if Link.OutputFile and $(TargetPath) are not the same. You will get the same warnings when building the application.
$(OutDir), $(TargetName) and $(TargetExt) are exposed on the “General” property page, as “Output Directory”, “Target Name”, “Target Extension”, respectively. You can manually change the values of these properties so that you no longer get the warning.
-If your project produces Import Library (Linker -> Advanced -> Import Library), you may need to change the Output folder of the Import Library as well after conversion if the Linker output directory is not the default output directory. Otherwise, the generated import lib maybe in a different directory than the linker output.
-Debugging.Command is set to default $(TargetPath) after conversion. You may need to make changes so that the right executable will be launched upon F5 (Debugging) or Ctrl + F5 (Start without debugging).
2) Property sheet ordering
If you have property sheets in your application, you may come across the following warning during conversion:
– All user macros reported below for configuration ‘Debug|Win32’ are used before their definition, which can cause undesirable build results; this is not supported in this release. You can resolve this by changing the inclusion order of the consuming property sheets and making sure they come after the property sheets defining the user macros.
– MSB4211: C:\foo\PropertySheet\foo.props; The property “MyIncludePath” is being set to a value for the first time, but it was already consumed at “C:\foo\PropertySheet\bar.props”.
The warning is due to the way that MSBuild evaluates its properties. MSBuild evaluates its properties in a sequential order. The properties defined in the inherited property sheets are evaluated as empty if they are used in the parent property sheets. VCBuild, however, does late evaluation, which enables the usage of the properties in the parent property sheets even if they are defined in the inherited property sheets. To work around the issue, please follow the warning message to change the inclusion order of the property sheets to ensure that properties are defined before they are used.
Behavior changes after upgrade
We strived to maintain a similar experience for our customers migrating to VS2010 in spite of the underlying build system change. On the other hand, we have made changes to improve some specific build experiences or accommodate MSBuild specific requirement. As a result, you may notice a few behavior changes once you migrate to VS2010.
1) Solution Dependencies into Project to project reference
When a C++ application from a previous version of Visual Studio is converted to VS2010, project dependencies defined at the solution level are converted to project to project references. This change ensures that C++ project dependencies are captured in the project file. Below is how a Project to project reference looks in the project file:
There are several advantages of having dependency information in the project file. First, user can build a project without the solution and the dependent projects will be built automatically. Second, it sets up the customer for large trees, where they might not use solution files. In addition, many customers have several solution files, each with different subsets of the projects. This can save the customers from setting project dependencies for each of the solutions. Another important factor is that, build dependencies are more reliable when the dependencies are set through project to project reference, especially when building with multiple cores. This has been the case with previous versions of Visual Studio as well.
-If you have a C# application that is dependent on a C++ application and this dependency is only expressed through solution dependency, the current conversion process won’t change the solution dependencies to the project to project references. You may run into build errors caused by incorrect build orders especially when building at the command line with MSBuild directly. To fix the issue, you will need to manually se up a proper project to project reference for C# and C++ applications.
-Use project to project reference instead of solution dependencies when setting up new build dependencies in VS2010 in general.
2) Project to project reference property change
After conversion, CopyLocalDependencies and UseDependenciesInBuild properties are removed. “Use in Build” property is changed to “Reference Assembly Output” to better represent the functionality of the property. Two more properties: “Link Library Dependencies” and “Use Library Dependency Inputs” are added for the referenced projects so that the referenced projects can control whether its output will be passed to the referencing projects. Below is the side by side image of VS2008 and VS2010 project to project reference properties.
-Setting “Reference Assembly Output” to “false” allows the project to be part of the project to project reference, so that the build dependency is set up, while its output is not passed to the CL of the referencing project. This property is used by managed application.
-Setting “Link Library Dependencies” to “false” allows the project to be part of the project to project reference, so that the build dependency is set up, while its output is not passed to the linker of the referencing project.
3) VC++ Directories change
VC++ Directories are no longer supported in VS2010 through Tools->Options page. Instead, VS2010 introduces the user settings file (Microsoft.cpp.<Platform>.users.props) to control global settings including Global search path. These files are located at $(USERPROFILE)\appdata\local\microsoft\msbuild\v4.0 directory. Upon migration to VS2010, the custom settings of VC++ Directories from VS2005 or VS2008 are migrated to these user files. These global settings files are imported into all the converted and newly created projects.
Here are the steps to change the settings file through UI:
· Open up property manager by clicking on View.Property Manager.
· Expand the project node and then the Configuration|Platform nodes, you will see “Microsoft.cpp.<Platform>.users” file for each Configuration|Platform. These are the files for the global settings, similar to the old tools/Options/VC++ Directories.
· Multi-Select “Microsoft.cpp.<Platform>.users”, right click and bring up the property page window
· In the property page window, click on “VC++ Directories” (for example) in the left pane, add new paths for the directories such as “Include Directories”. separated by semicolons
· Make sure to save the settings before shutting down Visual Studio.
· Re-launch Visual Studio and the new settings will be in effect.
-Note: If you would like to only change the settings for one project, you can right click on the project and bring up the property page. Change the settings for “VC++ Directories”, these settings will be persisted to the project file.
4) Custom Build Rule Change
In VS2008, a custom build rule is defined by a .rules file. Conversion will convert these .rules files into three separate files: .targets, .xml and .props. You can find these files in the same directory as the rules file after conversion. Note that there is no UI available to add new custom build rules. You can take a look of this blog for more information about VS2010 custom build rule.
5) Up to date check change
When you hit F5, you may run into the situation that Up to check dialog always coming up even after a fresh rebuild. You can refer to this blog to troubleshoot the problem. Most likely, the issue is caused by the fact that some files are listed as part of the project files but are missing on the disk. Because these files are part of the project files, the new up to date check mechanism will check for their existence. If the files are not on the disk, the up to date check will assume that a new build is needed. The fix is to remove the files from the project files if they are not on the disk.
One limitation we had for VS2010 is that Managed Incremental build is not supported. We are investigating how to bring back this feature in future release.
Known issues for conversion in VS2010
Here are some of the known issues for the conversion in VS2010:
After conversion, managed C++ projects will target the 4.0 Framework by default. The reason behind this design is that the VS2010 compiler cannot target Framework 2.0, 3.0 or 3.5. The VS2008 compiler must be used to target 2.0, 3.0 or 3.5. To make the converted C++ application build out of box, we decided to move the TargetFrameWorkVersion to 4.0 by default for C++ applications. The C++ applications can be retargeted to other frameworks (say 3.5 for example) by one of the following methods:
• Edit the vcxproj file and in the first property group define add the following:
• Open the VS2010 command line, set TargetFrameworkVersion=v3.5, and then start devenv.exe from the commandline. This will target all your C++ applications to v3.5 framework.
• Pass /p:TargetFrameworkVersion=v3.5 to MSBuild when building applications: MSBuild my.vcxproj /p:TargetFrameworkVersion=v3.5
Note that VS2008 has to be installed on the machine for the application to target 2.0, 3.0 or 3.5.
For C#/VB applications, conversion does not change the target Framework version if the targeted Framework is installed on the machine. If the targeted Framework is not installed, you will have the choice of either downloading the required Framework or upgrading the target Framework to 4.0.
-If you have mixed CSharp/VB/managed C++ projects in your solution, you may run into a situations where CSharp/VB applications and managed C++ applications are targeting a different framework version. You may get warning MSB3258 if the referenced project has a higher Framework version than the Framework version of referencing project:
The primary reference “foo, Version=x.x.xxx.xxxx, Culture=neutral, processorArchitecture=x86” has an indirect dependency on the .NET Framework assembly “mscorlib, Version=220.127.116.11, Culture=neutral, PublicKeyToken=b77a5c561934e089” which has a higher version “18.104.22.168” than exists in the current target framework. The version found in the current target framework redist list is “22.214.171.124”. By including the primary reference you may get compile and/or runtime errors.
-Or you may run into warnings such as 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.
-You need to retarget all projects in your solution to the same framework version.
2) Quotes used in makefile “Output” Property
If you have a make file project with quotes used in the “Output” property, the conversion will fail. The fix is to remove the quotes in the “Output” property before doing the conversion.
BuildCommandLine=“nmake /nologo "OUT=$(OutDir)" "OBJ=$(IntDir)"“
ReBuildCommandLine=“nmake /a /nologo "OUT=$(OutDir)" "OBJ=$(IntDir)"“
CleanCommandLine=“nmake /nologo "OUT=$(OutDir)" "OBJ=$(IntDir)" clean“
Output=“"$(OutDir)hlekernel.rom" "$(IntDir)hlekernel_gen.inl" "$(IntDir)hlekernel_gen.h"“
3) Backslash in $(IntDir) and $(OutDir)
$(IntDir) and $(OutDir) are exposed at “General -> Intermediate Directories” and “General -> Output Directories” on the property page, respectively. To unify the format for $(IntDir) and $(OutDir), conversion has intentionally appended “\” to their property values if they don’t have one when converting them. Trailing “/”s are removed if there are any.
This, however, may break the build scenarios if $(IntDir) or $(OutDir) are used in makefile or custom build, where Exec task is used.
In the makefile case, nmake tool cannot evaluate the values if they are ended with “\”. For example, with this command,
BuildCommandLine=“nmake /nologo "OUT=$(OutDir)" "OBJ=$(IntDir)"”
Because $(OutDir) and $(IntDir) has a trailing “\”, nmake tool cannot properly expand them and OUT and OBJ will be evaluated as empty as a result. To fix that, you need to remove the “\” for $(IntDir) and $(OutDir) at the property page.
If $(OutDir) or $(IntDir) are passed to the custom build, including build event, custom build tool, custom build steps, you may run into build failure due to the fact that “\” may be treated as an escape character by the tools. For example, if you have a prebuild event like the following:
<Command>cl /c /Fo”$(IntDir)” “$(ProjectDir)\win32.cpp”</Command>
The command line will be: cl /c /Fo”Debug\” “C:\foo\win32.cpp” instead of cl /c /Fo”Debug\\” “C:\foo\win32.cpp”
To fix the issue, add an extra “\” in the value passed to /Fo (C/C++ -> Output Files -> Object File Name) so to compensate the escaping.
4) Solution Explorer UI
The display of the folders and files in solution explorer is controlled by .vcxproj.filters file in VS2010. If you have folders with the same name but under different scopes, for example, “Native\Include” and “Include”, the second “Include” and the files under “Include” will not show up in solution explorer after conversion. To work around the issue, you can recreate the folder in the solution explorer and add the respective files into this folder.
5) Read-only Project file or directories
You may run into issues when upgrading from a read-only directory or reconverting when the previously converted project files are read-only. Be sure to make the directory and files writable unless the files can be automatically checked out, as in the case of integrated source control system.
Related Community Posts
Here are the collections of community post/connect bugs that may help troubleshooting build issues once upgraded to VS2010:
Project and Build Team