_From @davidmatson on March 21, 2017 2:47_
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net452</TargetFramework>
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<AssemblyName>Testx64</AssemblyName>
</PropertyGroup>
</Project>
Expected:
64-bit assembly
Actual:
AnyCPU assembly
Current workaround is to add:
<PlatformTarget>x64</PlatformTarget>
_Copied from original issue: dotnet/roslyn-project-system#1804_
I think this is by design. I believe the plural RuntimeIdentifiers property only affects what runtime-specific packages NuGet restores (allowing you to do a rid-specific publish for example later). If you want to build for x64, then I think you would set the singular RuntimeIdentifier property instead.
@nguerrera or @eerhardt may be able to provide more insight.
Having
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
be AnyCPU and
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
be x64 is a little subtle.
If there's only one entry in RuntimeIdentifiers, should it be used to set RuntimeIdentifier as well?
Also, perhaps a separate bug, but if the solution configuration has a selected Platform for the project named x64, RuntimeIdentifiers is sufficient (edit: actually, neither is needed). Selecting that project Platform in solution configuration currently requires the following two lines to be present in the csproj:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
Though in this case the output path is:
bin\x64\Debug\net452\My.exe (with RuntimeIdentifiers)
bin\x64\Debug\net452\win-x64\My.exe (with RuntimeIdentifier)
Particularly in the second case, it's odd to have the extra x64 added to the path (and odd that the path doesn't say x64 in the same place regardless of how it figured out it should be x64).
Particularly in the second case, it's odd to have the extra x64 added to the path (and odd that the path doesn't say x64 in the same place regardless of how it figured out it should be x64).
I see how it can feel be odd in some cases, but in general $(Platform) and $(RuntimeIdentifier) changes must both cause the output path to change. Both are intended to be set per-build (commonly via Platform drop-down in VS or -r argument in dotnet CLI). They are technically independent and many:many.
If in your case $(RuntimeIdentifier) is unique per $(Platform), then you can set $(AppendRuntimeIdentifierToOutputPath) to false to remove the win-x64 from the second path. We don't have enough context to know when this is safe or when it will cause different build invocations to clobber each other.
The fact that it can be many:many makes sense. Should the system have some intelligence about how the two do/ought to map? For example, if the runtime identifier is win-x64, could that be understood as requiring a platform of x64? If the system understood that relationship, it could be used to remove the extra path segment (the first "x64" from the platform target) in this case, right?
Also, this understanding might be needed/important for correctness reasons in other cases. For example, I suspect if platform is x64 but runtimeidentifier is win-x86, something is wrong, and the build should error out with a clear message - is that correct?
For example, if the runtime identifier is win-x64, could that be understood as requiring a platform of x64
It already does set $(PlatformTarget) for you to x64 in this case if you haven't set it yourself.
Similarly, if you build on Windows and set $(PlatformTarget), it will pick the win7-x64 $(RuntimeIdentifier) for you. When we pick for you, we do not add path segments. This is safe because the build invocation with something unset is different from when it's set and the goal is different build gestures that change these parameters go to different directories.
There's a good subset of common cases where you can stick to either the traditional Platform/PlatformTarget or RuntimeIdentifier/RuntimeIdentifiers, but not mix them, and you won't get any project file content or path duplication.
If platform is x64 but runtimeidentifier is win-x86, something is wrong, and the build should error out with a clear message - is that correct?
The variables that can never mismatch are $(PlatformTarget) (used to stamp exe) and $(RuntimeIdentifier) (used to resolve native nuget assets). With that said, we do not issue a warning or error in this case today.
$(Platform) is a little different. It is just an extra dimension to the configuration. It does not necessarily affect other things. By default Platform=x64 implies PlatformTarget=x64, but you can change in fact change them independently. It is not uncommon to just use empty default $(Platform)=AnyCPU but still emit platform-specific binaries. It has also always been possible (for classic csproj too) to have Platform=x86 (set by configuration manager), but PlatformTarget=x64 (set by build property page).
If the system understood that relationship, it could be used to remove the extra path segment in this case, right?
It's not that easy. We certainly can't remove an explicit RuntimeIdentifier=win7-x64 because there can be a separate build invocation with win10-x64 and that needs to go to a distinct directory. Removing the x64 part is more promising as it's the only part that is actually redundant, but it would make it overly complicated to predict the output path of any given project and build invocation. Platform being much like Configuration (as explained above) means that it is part of the output path in the same way (with the long-standing convention that AnyCPU gets an empty segment). While bizarre, it is possible to have conditions based on $(Platform) such that $(Platform)=x64/$(PlatformTarget)=x64 builds something different than $(Platform)=AnyCPU, $(PlatformTarget)=x64.
In the fully general case, this can get very complex.
I see two follow-ups here:
Thanks, Nick. The documentation and diagnostics sounds great.
Regarding:
If platform is x64 but runtimeidentifier is win-x86, something is wrong, and the build should error out with a clear message - is that correct?
Yeah, I had meant specifically PlatformIdentifier rather than Platform.
I'm used to Platform/PlatformTarget from the pre-CPS days. It looks like the new way to do this is to use RuntimeIdentifier instead and not include either Platform or PlatformTarget in the project file (once the solution configuration manager is fixed to infer data via RuntimeIdentifier instead of from Platform/ PlatformTarget).
I think the biggest remaining ask here is that if a csproj uses the the normal "right way" to mark itself as x64, it doesn't result in x64 showing up twice in the output path. Do you think the recommendation here would be not to define either Platform or PlatformTarget in the significant majority of csprojs?
Most helpful comment
It already does set $(PlatformTarget) for you to x64 in this case if you haven't set it yourself.
Similarly, if you build on Windows and set $(PlatformTarget), it will pick the win7-x64 $(RuntimeIdentifier) for you. When we pick for you, we do not add path segments. This is safe because the build invocation with something unset is different from when it's set and the goal is different build gestures that change these parameters go to different directories.
There's a good subset of common cases where you can stick to either the traditional Platform/PlatformTarget or RuntimeIdentifier/RuntimeIdentifiers, but not mix them, and you won't get any project file content or path duplication.
The variables that can never mismatch are $(PlatformTarget) (used to stamp exe) and $(RuntimeIdentifier) (used to resolve native nuget assets). With that said, we do not issue a warning or error in this case today.
$(Platform) is a little different. It is just an extra dimension to the configuration. It does not necessarily affect other things. By default Platform=x64 implies PlatformTarget=x64, but you can change in fact change them independently. It is not uncommon to just use empty default $(Platform)=AnyCPU but still emit platform-specific binaries. It has also always been possible (for classic csproj too) to have Platform=x86 (set by configuration manager), but PlatformTarget=x64 (set by build property page).
It's not that easy. We certainly can't remove an explicit RuntimeIdentifier=win7-x64 because there can be a separate build invocation with win10-x64 and that needs to go to a distinct directory. Removing the x64 part is more promising as it's the only part that is actually redundant, but it would make it overly complicated to predict the output path of any given project and build invocation. Platform being much like Configuration (as explained above) means that it is part of the output path in the same way (with the long-standing convention that AnyCPU gets an empty segment). While bizarre, it is possible to have conditions based on $(Platform) such that $(Platform)=x64/$(PlatformTarget)=x64 builds something different than $(Platform)=AnyCPU, $(PlatformTarget)=x64.
In the fully general case, this can get very complex.
I see two follow-ups here: