C++ Native Multi-Targeting

C++ Native Multi-Targeting

Rate This
  • Comments 24

Hello, my name is Li Shao. I am a Software Design Engineer in Test on the C++ team. For the past two years, I have been part of the team working on migrating the C++ build system from VCBuild to MSBuild as well as the new project system which is also built on top of MSBuild. You can see one of my earlier blogs about the task work that we have done. In this blog, I am going to give an overview of the Native Multi-targeting feature. I hope you will find it useful.

 

Multi-targeting is the ability to use the current version of Visual Studio to build your application with a different set of installed tools or Frameworks. In VS2010, C++ applications support two types of Multi-targeting: Native Multi-targeting and Managed Multi-targeting. For more details on Managed Multi-targeting, please visit this blog post by one of my colleagues, Pavan Adharapurapu.

Native Multi-targeting

 

VS2010 can support building against VS2008 toolsets if VS2008 is installed on the same machine. Soma has mentioned this feature in his blog some time ago. This feature enables the C++ customers to use VS2008 toolsets while working in the VS2010 environment. Note that your application created with earlier versions of Visual Studio will need to be converted to the VS2010 version.

 

Why Native Multi-targeting?

An important scenario for C++ customers (let’s call them “large C++ shops” here) is that they develop and build their applications in Visual Studio and then ship the applications to their respective customers. These customers, however, maybe using different versions of Visual Studio for development. Given that, the “large C++ shops” have to keep multiple versions of Visual Studio and multiple versions of the project files, so that they can build binaries targeting different versions of the toolset.

 

With the native Multi-targeting feature, once the “large C++ shops” migrated their applications to VS2010, they have the ability to use VS2010 to target any versions of the toolset theoretically. This eliminates the need to maintain multiple versions of project files and the development work can be done all within VS2010, as long as the targeted toolsets are installed on the same machine.

 

How to enable native Multi-targeting?

The Multi-Targeting feature is available both when building from the IDE and building on the command line. Below is a screen shot of the native Multi-targeting settings on the property page: Platform Toolset controls which version of the toolsets you want to target: v100 targets VS2010 and v90 targets the VS2008 level toolsets and libraries.

 

 

The Platform toolset version is v100 by default for both newly created projects and projects converted to VS2010. In both cases, the “PlatformToolset” property is not written to the project file. There are a couple of ways you can re-target to different PlatformToolset (v90 is used as an example below):

 

For single project, you can choose your targeted platform toolset to be v90 in the property page, the “PlatformToolset” property will be written to your project file as the following:

 

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

    <ConfigurationType>Application</ConfigurationType>

    <UseDebugLibraries>true</UseDebugLibraries>

    <CharacterSet>Unicode</CharacterSet>

    <PlatformToolset>v90</PlatformToolset>

</PropertyGroup>

 

 

 

In order to re-target multiple projects at once, you can multi-select them in Solution Explorer and then bring up the Property Pages. To make the changes apply to all configurations, also select from the Configuration dropdown “All configurations” entry and from Platform dropdown “All platforms” entry. Note that multi-select will work only if all projects are of the same type.

 

Once the “PlatformToolset” is set to v90, the compiler, linker, headers, and libraries from VS2008 will be used. Below is a snapshot of the build log when building against the v90 toolset from the VS2010 IDE or command line prompt. You can see that VS2008 compiler is used instead of VS2010 compiler.

 

c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\CL.exe /c /ZI /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _WINDOWS /D _DEBUG /D _UNICODE /D UNICODE /D _AFXDLL /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Yc"StdAfx.h" /Fp"Debug\MFC.pch" /Fo"Debug\\" /Fd"Debug\vc90.pdb" /Gd /TP /analyze- /errorReport:prompt stdafx.cpp

 

Note that changing the value of the Platform Toolset property will cause a full rebuild of the project.

 

How does it work?

In VS2010, there are V90 and V100 folders under %ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\Platforms\<Platforms>\PlatformToolsets or %ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\Platforms\<Platforms>\PlatformToolsets on a x64 machine. Inside each folder, you can find a ToolSet specific props and targets file. Here is a representation of the layout of the file/directory structure on the disk:

 

 

 

Microsoft.Cpp.<Platform>.<PlatformToolset>.props file sets the properties required to build against a certain toolset. For example, in Microsoft.Cpp.Win32.v90.Props, ExecutablePath (PATH), IncludePath (INCLUDE), ReferencePath (LIBPATH), LibraryPath (LIB), SourcePath, ExcludedPath are set based on the VS2008 installation and Windows SDK installation that shipped with VS2008 (6.0A)

 

<VCInstallDir>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Setup\VC@ProductDir)</VCInstallDir>

<VCInstallDir

    Condition="'$(VCInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\9.0\Setup\VC@ProductDir)</VCInstallDir>

<VCInstallDir

    Condition="'$(VCInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VCExpress\9.0\Setup\VC@ProductDir)</VCInstallDir>

<VCInstallDir

    Condition="'$(VCInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VCExpress\9.0\Setup\VC@ProductDir)</VCInstallDir>

<WindowsSdkDir

    Condition="'$(UseEnv)' != 'true'">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows@CurrentInstallFolder)</WindowsSdkDir>

<WindowsSdkDir

    Condition="'$(WindowsSdkDir)'==''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\MicrosoftSDKs\Windows@CurrentInstallFolder)</WindowsSdkDir>

<ExecutablePath

    Condition="'$(ExecutablePath)' == ''">$(VCInstallDir)bin;$(WindowsSDKDir)bin;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);$(VSInstallDir);$(SystemRoot)\SysWow64;$(FxCopDir);$(PATH);</ExecutablePath>

<IncludePath

    Condition="'$(IncludePath)' == ''">$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)include;</IncludePath>

<ReferencePath

    Condition="'$(ReferencePath)' == ''">$(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib</ReferencePath>

<LibraryPath

    Condition="'$(LibraryPath)' == ''">$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)lib</LibraryPath>

<SourcePath

    Condition="'$(SourcePath)' == ''">$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\mfcm;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src;</SourcePath>

<ExcludePath

    Condition="'$(ExcludePath)' == ''">$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)include;$(FrameworkDir)$(FrameworkVersion);$(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib;</ExcludePath>

 

MSBuild can spawn the build environment based on the information you set in the props file for a certain platform toolset. In the command line build scenarios, this means that the PATH, LIBPATH, LIB, INCLUDE that the build system is using can come from the PlatformToolsets itself, which can eliminate the need to use other batch files, such as vcvars32.bat, to set up the build environment. This is a functionality that the old VCBuild system does not have. Below is a code snippet from Microsoft.cpp.Targets that enables the functionality. When UseEnv is not set to true, PATH, LIB, INCLUDE, LIBPATH will be set from the corresponding property values in the platform toolset.  When UseEnv is set to true, as in old build system, the values from environment variables for PATH, INCLUDE, LIB, LIBPATH will be used, instead.

 

<Target Name="SetBuildDefaultEnvironmentVariables"

          Condition="'$(UseEnv)' != 'true'">

    <SetEnv Condition="'$(_IsNativeEnvironment)' != 'true'"

            Name   ="PATH"

            Value  ="$(ExecutablePath)"

            Prefix ="false">

      <Output TaskParameter="OutputEnvironmentVariable" PropertyName="Path"/>

    </SetEnv>

    <SetEnv Condition="'$(_IsNativeEnvironment)' == 'true'"

            Name ="PATH"

            Value ="$(NativeExecutablePath)"

            Prefix ="false">

      <Output TaskParameter="OutputEnvironmentVariable" PropertyName="Path"/>

    </SetEnv>

    <SetEnv Name   ="LIB"

            Value  ="$(LibraryPath)"

            Prefix ="false">

      <Output TaskParameter="OutputEnvironmentVariable" PropertyName="LIB"/>

    </SetEnv>

    <SetEnv Name   ="LIBPATH"

            Value  ="$(ReferencePath)"

            Prefix ="false">

      <Output TaskParameter="OutputEnvironmentVariable" PropertyName="LIBPATH"/>

    </SetEnv>

    <SetEnv Name   ="INCLUDE"

            Value  ="$(IncludePath)"

            Prefix ="false" >

      <Output TaskParameter="OutputEnvironmentVariable" PropertyName="INCLUDE"/>

    </SetEnv>

  </Target>

 

 

For managed applications, since the VS2008 compiler can only target CLR 2.0, targeting v90 platform toolset it allows only one of these versions of .NET Framework: v2.0, v3.0 or v3.5. By default, v90 platform toolset target v3.5 framework.

 

How to extend it?

With this model, if you have a specific SDK that you would like to target, you can create your custom platform toolset. Below is an example of a Microsoft.cpp.Win32.v80.props file for targeting VS2005. Note that for VS2005, WindowsSDKDir is not available, we have PlatformSDK instead.  After creating the v80 directory for each platform along with the corresponding props and targets files, you will have support to build against v80 instantly.

 

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Import Project="$(VCTargetsPath)\Platforms\Win32\ PlatformToolsets\v80\ImportBefore\*.props" Condition="Exists('$(VCTargetsPath)\Platforms\Win32\PlatformToolsets\v80\ImportBefore')" />

  <PropertyGroup>

    <PlatformToolsetVersion>80</PlatformToolsetVersion>

    <VCInstallDir>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Setup\VC@ProductDir)</VCInstallDir>

    <VCInstallDir

        Condition="'$(VCInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\8.0\Setup\VC@ProductDir) </VCInstallDir>

    <VSInstallDir>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Setup\VS@ProductDir)</VSInstallDir>

    <VSInstallDir

        Condition="'$(VSInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\8.0\Setup\VS@ProductDir)</VSInstallDir>

    <FrameworkDir

        Condition="'$(UseEnv)' != 'true'">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework@InstallRoot)</FrameworkDir>

    <FrameworkDir

        Condition="'$(FrameworkDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework@InstallRoot)</FrameworkDir>

    <FrameworkSdkDir

        Condition="'$(UseEnv)' != 'true'">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\.NETFrameWork\v2.0@InstallationFolder)</FrameworkSdkDir>

    <FrameworkSdkDir

        Condition="'$(FrameworkSdkDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\.NETFrameWork\v2.0@InstallationFolder)</FrameworkSdkDir>

    <FrameworkSdkDir

        Condition="'$(FrameworkSdkDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\.NETFrameWork\v2.0@InstallationFolder)</FrameworkSdkDir>

    <FrameworkVersion

        Condition="'$(UseEnv)' != 'true'">v2.0.50727</FrameworkVersion>

    <Framework35Version

        Condition="'$(UseEnv)' != 'true'">v3.5</Framework35Version>

    <ExecutablePath

        Condition="'$(ExecutablePath)' == ''">$(VCInstallDir)bin;$(VCInstallDir)PlatformSDK\bin;$(VCInstallDir)PlatformSDK\common\bin;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);$(VSInstallDir);$(SystemRoot)\SysWow64;$(FxCopDir);$(PATH);</ExecutablePath>

    <IncludePath

        Condition="'$(IncludePath)' == ''">$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include;$(VCInstallDir)PlatformSDK\common\include;$(FrameworkSDKDir)include;</IncludePath>

    <ReferencePath

        Condition="'$(ReferencePath)' == ''">$(FrameworkDir)$(FrameworkVersion);$(VCInstallDir)atlmfc\lib;</ReferencePath>

    <LibraryPath

        Condition="'$(LibraryPath)' == ''">$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)atlmfc\lib\i386;$(VCInstallDir)PlatformSDK\lib;$(VCInstallDir)PlatformSDK\common\lib;$(FrameworkSDKDir)lib;$(VSInstallDir);$(VSInstallDir)lib;</LibraryPath>

    <SourcePath

        Condition="'$(SourcePath)' == ''">$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src;</SourcePath>

    <ExcludePath

        Condition="'$(ExcludePath)' == ''">$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include;$(VCInstallDir)PlatformSDK\common\include;$(FrameworkSDKDir)include;$(FrameworkDir)$(FrameworkVersion);$(VCInstallDir)atlmfc\lib;</ExcludePath>

    <NativeExecutablePath

        Condition="'$(NativeExecutablePath)' == ''">$(ExecutablePath)</NativeExecutablePath>

  </PropertyGroup>

  <Import Project="$(VCTargetsPath)\Platforms\Win32\ PlatformToolsets\v80\ImportAfter\*..props" Condition="Exists('$(VCTargetsPath)\Platforms\Win32\ PlatformToolsets\v80\ImportAfter')" />

</Project>

 

 

Below is a snapshot of the IDE build setting and the build log:

 

 

The Platform Toolset feature also provides the extensibility points. In the Microsoft.Cpp.Win32.<PlatformToolset version>.props file, there are the following sections:

 

<Import Project="$(VCTargetsPath)\Platforms\Win32\PlatformToolsets\v100\ImportBefore\*.props"

        Condition="Exists('$(VCTargetsPath)\Platforms\Win32\PlatformToolsets\v100\ImportBefore')" />

 

<Import Project="$(VCTargetsPath)\Platforms\Win32\PlatformToolsets\v100\ImportAfter\*.props"

        Condition="Exists('$(VCTargetsPath)\Platforms\Win32\PlatformToolsets\v100\ImportAfter')" />

 

With this design, you can create your own special props file that is specific to a certain Platform Toolset. For example, you might create a custom props file called DirectX.props (it could be called anything) which you then drop into the following folder: win32\PlatformToolsets\v100\ImportAfter. The Properties defined in DirectX.props (below) would be imported after the properties in Microsoft.Cpp.Win32.<PlatformToolset version>.props are imported.

 

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemDefinitionGroup>

    <ClCompile>

<AdditionalIncludeDirectories>$(DirectXSDK)\Latest\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

    </ClCompile>

    <Link>

<AdditionalLibraryDirectories>$(DirectXSDK)\Latest\Lib\x86%(AdditionalLibraryDirectories);</AdditionalLibraryDirectories>

    </Link>

  </ItemDefinitionGroup>

</Project>

 

Upon compile, you will get /I d:\DirectX\Latest\include being passed to CL.exe and /LIBPATH:d:\DirectX\Latest\Lib\x86 being passed to the linker when compiling a project targeting the Win32 platform and the v100 platform toolset. Any other project configurations will not be impacted.

 

Windows SDK will adopt this Platform Toolset concept for future releases so that re-targeting of different WinSDK versions becomes an easier task. We also encourage our customers to use this feature and let us know if you have any feedback.

 

Li Shao

Project and Build Team

 

  • Hi Owen, could you share with me the PlatformToolset you have for VS2003 and a sample project? I will investigate the problem and let you know. You can email me at lishao AT microsoft.com.

  • I sent you an email on December 21 with my files, but it bounced:

    [microsft.com (1): Connection refused]

    Do you have an alternate address?

  • Funny, I didn't notice anything wrong with the email address until I read my previous comment. I'll try again, this time with "microsoft" spelled correctly. :/

  • Hi Owen, I got your email correctly this time and will look into this after new year once I come back from vacation.

    Happy Holidays!

  • Hi Owen, I was able to repro the issue you mentioned when targeting VC7, which is the version of the Visual Studio that you were using as you clarified in our email exchange. The error is caused by the fact that VC7 compiler does not support Unicode while VS2010 MSBuild compiler task uses Unicode response file. I will see if we can make any changes in this release for the response file encoding.

    Starting from VS2003, CL compiler supports Unicode.

  • So if we're able to target the Platform/Windows SDK's instead of having the full VS installations what's the equivalent SDK versions for each of the following:

    VC6.0sp6 - Platform SDK Feb 2003?

    VS2002sp1 (for sake of completeness)

    VS2003sp1 - Windows Server 2003 R2 PSDK?

    VS2005sp1 - Windows Server 2008 SDK?

    VS2008sp1 - Windows 7 SDK?

    Assuming there is equivalent SDKs for each of the above is there really any reason for keeping the older VS versions installed?

    Or is it only recommended that we target previous VS installations?

  • Each WinSDK comes with a version of Windows Headers/Libs as well as a version of CRT Headers/Libs. It is not likely a WinSDK release willl have the same set of windows headers and libs as well as the same set of CRT headers and libs.

    If the version of Windows headers/libs is not a concern to you when you deploy your application, yes, you can choose a WinSDK version with the same set of CRT Headers/Libs as your VS installation.

    Li Shao

  • I'm not in a "large C++ shop" as you describe it but we've been around for a long time and have a lot of small native C++ projects to support in Production.  We often find ourselves in the position of not having enough time to do everything we need to do for our active projects - let alone upgrade every single project to the latest version of VS every time a new version ships.  (Migrating projects to new toolsets doesn't pay the bills generally.)  Additionally there's simply some projects that can never be updated to the latest version (Attributed ATL Server projects for one).

    To support everything we have, I currently would need 4 versions of VS + a few Windows SDKs + 2 SQL versions (Dev Ed) + 2 versions of Team Explorer (for TFS) + documentation for all of that (largely redundant).  Once VS2010 goes RTM then it will be added to the mix - along with Team Explorer 2010 and SQL2008R2.

    That's a huge (ever growing) footprint and a real pain to install [correctly].

    So it is in that context I asked about the WinSDK versions and targetting - I'm trying to figure out whether we can really use this functionality to reduce the footprint/burden by only requiring VS2010 + 2 SQL versions + appropriate Platform/Windows SDK versions in addition to the benefits of a single IDE application and unified project/solution files.

    Is it realistic?  Or am I trying to push the feature to far?

  • I understand your situation. For current release, we officially support targeting VS2008 from VS2010. You can create the Platform Toolset to target VS2005. To target older versions, such as VS2002, however, one issue you may run into is the response file encoding. We only support response file with Unicode encoding for current release. There may also be issues with compiler/linker tasks or tasks for other tools due to differences in the switches between VS2010 tools and tools from earlier versions of Visual Studio, if you still use the tasks that we created based on VS2010 tools.

    In terms of using WinSDK to replace Visual Studio, if the applications are for internal use only, it is feasible to do so.

    We can consider to expand the support such as non-Unicode response files in future releases.

Page 2 of 2 (24 items) 12