How to set PlatformToolset property from MSbuild?

2020-05-09 17:02发布

We are experiencing AppVeyor x64 build failures, and trying a suggestion to set PlatformToolset to DefaultPlatformToolset. The property value is undocumented, so we want to thoroughly test it.

We wired DefaultPlatformToolset into our AppVeyor configuration file:

test_script:

- cmd: >-
    msbuild /t:Build /p:PlatformToolset=DefaultPlatformToolset cryptlib.vcxproj
    msbuild /t:Build /p:PlatformToolset=DefaultPlatformToolset cryptest.vcxproj
    msbuild /t:CopyCryptestToRoot /p:PlatformToolset=DefaultPlatformToolset cryptest.vcxproj
    cryptest.exe v
    cryptest.exe tv all

The value DefaultPlatformToolset is causing AppVeyor failures. Here's from the log where we believe everything is correct: Build 1.0.129:

msbuild /t:Build /p:PlatformToolset=DefaultPlatformToolset cryptlib.vcxproj
Microsoft (R) Build Engine version 14.0.25420.1
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 8/2/2017 5:14:24 AM.
The target "_ConvertPdbFiles" listed in a BeforeTargets attribute at "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets (45,37)" does not exist in the project, and will be ignored.
The target "_CollectPdbFiles" listed in an AfterTargets attribute at "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets (45,70)" does not exist in the project, and will be ignored.
The target "_CollectMdbFiles" listed in a BeforeTargets attribute at "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets (52,38)" does not exist in the project, and will be ignored.
The target "_CopyMdbFiles" listed in an AfterTargets attribute at "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets (52,71)" does not exist in the project, and will be ignored.
Project "C:\projects\cryptopp\cryptlib.vcxproj" on node 1 (Build target(s)).
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.Cpp.Platform.targets(57,5): error MSB8020: The build tools for DefaultPlatformToolset (Platform Toolset = 'DefaultPlatformToolset') cannot be found. To build using the DefaultPlatformToolset build tools, please install DefaultPlatformToolset build tools.  Alternatively, you may upgrade to the current Visual Studio tools by selecting the Project menu or right-click the solution, and then selecting "Retarget solution". [C:\projects\cryptopp\cryptlib.vcxproj]
Done Building Project "C:\projects\cryptopp\cryptlib.vcxproj" (Build target(s)) -- FAILED.
Build FAILED.

How do we set PlatformToolset on the command line when using MSbuild to build the project?


None of these work. They produce similar errors as above.

> msbuild /t:Build /p:PlatformToolset=DefaultPlatformToolset cryptlib.vcxproj

> msbuild /t:Build /p:PlatformToolset=$(DefaultPlatformToolset) cryptlib.vcxproj

> msbuild /t:Build /p:PlatformToolset="$(DefaultPlatformToolset)" cryptlib.vcxproj

> msbuild /t:Build /p:PlatformToolset='$(DefaultPlatformToolset)' cryptlib.vcxproj

Here's another error when trying to do that silly MS XML quoting:

>msbuild /t:Build /p:PlatformToolset="$(DefaultPlatformToolset)" cryptlib.vcxproj
Microsoft (R) Build Engine version 4.6.1087.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Copyright (C) Microsoft Corporation. All rights reserved.

MSBUILD : error MSB1011: Specify which project or solution file to use because t
his folder contains more than one project or solution file.
'#34' is not recognized as an internal or external command,
operable program or batch file.
'#34' is not recognized as an internal or external command,
operable program or batch file.

And then there's this one:

> msbuild /t:Build /p:PlatformToolset=""$(DefaultPlatformToolset)"" cryptlib.vcxproj
Microsoft (R) Build Engine version 4.6.1087.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Copyright (C) Microsoft Corporation. All rights reserved.

Build started 8/2/2017 2:19:15 AM.
Project "c:\Users\Test\cryptlib.vcxproj" on node 1 (Build target(s)).
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.Cpp.Platform.p
rops(15,24): error MSB4115: The "exists" function only accepts a scalar value,
but its argument "$(VCTargetsPath)\Platforms\$(Platform)\PlatformToolsets\$(Pla
tformToolset)\Microsoft.Cpp.$(Platform).$(PlatformToolset).props" evaluates to
"C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\\Platforms\x64\Platform
Toolsets\"$(DefaultPlatformToolset)"\Microsoft.Cpp.x64."$(DefaultPl
atformToolset)".props" which is not a scalar value.

Regarding the error message's suggestion ... or right-click the solution, and then selecting "Retarget solution", we can't. This is remote, command line only. We are trying to figure out how to run VCUpgrade from the command line at How to run VCUpgrade before Appveyor build?

1条回答
走好不送
2楼-- · 2020-05-09 17:46

DefaultPlatformToolset is an MSBuild property which gets set in one of the platform .props files imported by the main project. At one point in a C++ project file there should be the line

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

which for instance for VS2017 ends up importing <msbuild installation>\Microsoft.Cpp\v4.0\V140\Microsoft.Cpp.Default.props in turn, which sets the property using

<DefaultPlatformToolset>v140</DefaultPlatformToolset>

Since this is an msbuild property it is not known by Appveyor nor by cmd and as such you cannot reference it directly from within appveyor.yml. And even if it were known, you cannot refer to it using $(DefaultPlatformToolset) which is msbuild property expansion syntax whereas cmd's is of the form %ABC%. The errors you get are all because the PlatformToolset property gets set to e.g. the string 'DefaultPlatformToolset' whereas it needs to be set to the name of an actual available platform like v110 or v140 or v120_xp or ...

If you want to use DefaultPlatformToolset the easiest way is entering it in the project file. It can still be overriden from the commandline if needed. The alternative would be running msbuild and have it print it's value, then pass that using /p:PlatformToolset=. But that's not so nice for local development builds in Visual Studio.

For changing the project file, find the section(s) where the PlatformToolset is defined and change it to $(DefaultPlatformToolset) - this should come after the Microsoft.Cpp.Default.props import else the property might not yet be defined. To guard against the case where you're building for a platform which for some reason does not define DefaultPlatformToolset (something custom, or possibly old VS versions) set it to a sensible default first, before the Microsoft.Cpp.Default.props. Example:

<PropertyGroup Label="Globals">
  <PlatformToolset>v100</PlatformToolset>
</PropertyGroup>
....
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
....
<PropertyGroup>
  <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
</PropertyGroup>

Here's another way to do it:

<!-- Use DefaultPlatformToolset after Microsoft.Cpp.Default.props -->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<!-- Set DefaultPlatformToolset to v100 (VS2010) if not defined -->
<PropertyGroup Label="EmptyDefaultPlatformToolset">
    <DefaultPlatformToolset Condition=" '$(DefaultPlatformToolset)' == '' ">v100</DefaultPlatformToolset>
  </PropertyGroup>
<PropertyGroup Label="PlatformToolset">
    <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
</PropertyGroup>

It is also very important to ensure the Global property group elements are set before Microsoft.Cpp.Default.props is imported. If they are not set, then MSbuild assumes an Application is being built, and it ignores the ConfigurationType that comes later:

<PropertyGroup Label="Globals">
    <ProjectGuid>{016d3861-ccd6-4a50-83b4-fe4e93bea333}</ProjectGuid>
    <RootNamespace>mylib</RootNamespace>
    <ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
...
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
...
<PropertyGroup>
    <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
</PropertyGroup>
查看更多
登录 后发表回答