Skip to content

Remove requirement for specifying a RID in desktop exe projects #847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 16, 2017

Conversation

nguerrera
Copy link
Contributor

@nguerrera nguerrera commented Feb 9, 2017

Customer scenario

  • Cannot build AnyCPU desktop app (common case) using SDK/new project system without applying a cumbersome and hard-to-understand workaround.
  • Cannot build desktop app with native nuget dependencies without making a RID choice.

The approach taken is scoped to address these two common cases:

  1. Building AnyCPU desktop application
  2. Building win7+ desktop applications with native dependencies

Both of the above will happen automatically and F5 successfully without any mention of RuntimeIdentifier or PlatformTarget in the project. Other scenarios will still require adjusting the RID and PlatformTarget to resolve the native dependencies correctly and stamp the resulting binary correctly.

Follow-ups to improve the experience outside these cases:

Bugs this fixes:
Fix #396 and its myriad of duplicates/consequences.

Workarounds, if any
Specify dummy runtime identifier for AnyCPU app, and explicitly let PlatformTarget to AnyCPU.
Always pick a RID whenever taking native nuget dependencies from a desktop app.

Risk
Low. We address two common cases with new defaults. Specifying PlatformTarget and RuntimeIdentifier explicitly shuts off these defaults and gives the user the same control they had before. We simply make it so that this control needn't be used for the most common cases.

Performance impact
Minimal. A few extra checks are needed in msbuild evaluation.

Is this a regression from a previous update?
No.

Root cause analysis
N/A since we are amending a deliberate design decision in response to significant customer and partner feedback.

How was the bug found
Customer and partner reported.

@srivatsn @dsplaisted @eerhardt @dotnet/project-system

Copy link
Member

@dsplaisted dsplaisted left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. Congrats on figuring out a reasonable way of improving the user experience here.


#if USE_NATIVE_CODE
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
static extern int uv_loop_size();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like the test might eventually break if the Libuv and Kestrel packages get updated to support AnyCPU .NET Framework deployments by deploying both x86 and x64 versions of the native library and Kestrel dynamically chooses which one to load.

I'm not suggesting you change this now, just pointing it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would only happen if my libuv package ref was upgraded, which is unexpected and I'd be happy to have tests fail in that case.


2. Building an x86 or x64 NETFramework application on and for
Windows with native NuGet dependencies that do not require
greater thanwin7.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space: thanwin7

@@ -60,9 +114,19 @@ Copyright (c) .NET Foundation. All rights reserved.
<OutputPath>$(OutputPath)$(RuntimeIdentifier)\</OutputPath>
</PropertyGroup>

<Target Name="_CheckRuntimeIdentifier" BeforeTargets="_CheckForInvalidConfigurationAndPlatform">
<NetSdkError Condition="'$(RuntimeIdentifier)' == '' and '$(TargetFrameworkIdentifier)' == '.NETFramework' and '$(OutputType)' == 'Exe'"
ResourceName="RuntimeIdentifierMustBeSetForNETFramework" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove this resource (RuntimeIdentifierMustBeSetForNETFramework)? Or is that even something we can do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

Switch our default .NETFramework CPU architecture choice back to AnyCPU before
compiling the exe if no copy-local native dependencies were resolved from NuGet
-->
<Target Name="AdjustDefaultPlatformTargetForNetFrameworkExeWithNoNativeCopyLocalItems"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How likely is it that there could be something else that consumes the PlatformTarget property between when we set it to x86 or x64 based on the defaulted RuntimeIdentifier and when we set it back to AnyCPU because we didn't resolve any native assets?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm reviewing the uses of PlatformTarget in msbuild /pp output. There's quite a bit more than I'd hoped.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved it to run soon after the assets are resolved.


static ProcessorArchitecture GetProcessorArchitecture()
{
return AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).ProcessorArchitecture;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest typeof(Program).Assembly.Location. I don't know that it matters at all, but Assembly.GetExecutingAssembly() is one of the methods that we originally removed from .NET Core because it doesn't play well with optimizations we might want to do (inlining across assembly boundaries).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming suggestion: The name GetProcessorArchitecture makes me think it will return the architecture of the physical CPU or the OS. Something like GetCurrentAssemblyProcessorArchitecture would be clearer to me, if more verbose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make the change to avoid any question, but Assembly.GetExecutingAssembly() doesn't suffer from these problems intrinsically. Assembly.GetCallingAssembly is the evil one. GetExecutingAssembly was guilty only by association and that we never bothered to make it intrinsic with identical code gen to typeof(CurrentType).Assembly.Location.

@dsplaisted
Copy link
Member

@mlorbetske @vijayrkn Once this is merged, I believe you should be able to remove the RuntimeIdentifier property from the web templates (for example here. You don't necessarily have to do it for RTM if it doesn't meet the bar though.

@piotrpMSFT @livarcocc Same thing for dotnet migrate - If you have logic that sets a RuntimeIdentifier property when targeting .NET Framework because we didn't have the same default in the .NET SDK as project.json did, you can probably remove that logic once this is merged.

@mlorbetske
Copy link
Contributor

@livarcocc
Copy link
Contributor

Thanks for the heads up @nguerrera. I have filled https://github.com/dotnet/cli/issues/5668 to track this on the CLI side.

@nguerrera
Copy link
Contributor Author

@MattGertz for approval

@nguerrera
Copy link
Contributor Author

@MattGertz @natidea @srivatsn Approved to merge or pending ship room?

@srivatsn
Copy link
Contributor

Approved at shiproom. However @rrelyea wanted to understand if the RID that we infer is similar to what's done in NuGet.targets.

@nguerrera
Copy link
Contributor Author

@rrelyea @emgarten Can you point me to what nuget.targets does?

@emgarten
Copy link
Member

@nguerrera this exists in the build task: https://github.com/NuGet/NuGet.BuildTasks/blob/dev/src/Microsoft.NuGet.Build.Tasks/Microsoft.NuGet.targets#L94-L96

NuGet.targets/restore does not infer RIDs anymore like it did in preview2. It only reads the RuntimeIdentifier and RuntimeIdentifiers properties.

@nguerrera
Copy link
Contributor Author

Hold on merging this.

@nguerrera
Copy link
Contributor Author

nguerrera commented Feb 14, 2017

Approved at shiproom. However @rrelyea wanted to understand if the RID that we infer is similar to what's done in NuGet.targets.

@nguerrera this exists in the build task: https://github.com/NuGet/NuGet.BuildTasks/blob/dev/src/Microsoft.NuGet.Build.Tasks/Microsoft.NuGet.targets#L94-L96

OK. The default RIDs (plural) there are not win7+ whereas we pick win7+ for the implicit RID here. This is a pragmatic choice to make popular packages like libuv work on F5. Ultimately, the design would be to pick as inclusive as possible for current OS, but that is tracked by #840

There is no bad interaction between those and this: restore will just use our RID plus all of the RIDs there and produce an assets file with a superset of what this change needs.

@nguerrera
Copy link
Contributor Author

(But still making adjustments based on other issues.)

Copy link
Contributor

@natidea natidea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waiting for test

@nguerrera
Copy link
Contributor Author

@natidea @srivatsn @dsplaisted I've got all the changes discussed made and test coverage added to handle it.

411f959 is unfortunate workaround for case where the net4x app is one target of many. In that case, nuget restore doesn't see the inferred RID of inner build. The fix is a hammer to include win7-x86 and win7-x64 RIDs (plural) in all multi-targeting builds on Windows.

var ns = project.Root.Name.Namespace;
var propertyGroup = project.Root.Elements(ns + "PropertyGroup").First();
propertyGroup.Add(new XElement(ns + "RuntimeIdentifier", rid));
propertyGroup.Add(new XElement(ns + "AppendRuntimeIdentifierToOutputPath", useAppendOption.ToString()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be testing the case where AppendRuntimeIdentifierToOutputPath is not specified in the project, as that will be the common case?

@@ -122,7 +122,7 @@ Copyright (c) .NET Foundation. All rights reserved.
compiling the exe if no copy-local native dependencies were resolved from NuGet
-->
<Target Name="AdjustDefaultPlatformTargetForNetFrameworkExeWithNoNativeCopyLocalItems"
DependsOnTargets="ResolvePackageDependenciesForBuild"
AfterTargets="ResolvePackageDependenciesForBuild"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this change? I haven't entirely grokked the difference between DependsOnTargets and AfterTargets, but I think this means that this target could run before ResolvePackageDependenciesForBuild if something else depended on this target.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am trying to make sure that this is done as soon as we know the right PlatformTarget. Somebody could indeed force it to be sooner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really concerned about someone trying to run this target themselves at some other time and I don't want to reset testing for this.

build, we may implicitly use one of these RIDs and so we must ensure that restore
includes them.
-->
<PropertyGroup Condition="'$(RuntimeIdentifiers)' == '' and '$(OS)' == 'Windows_NT'">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we check to see whether RuntimeIdentifiers contains a net* target framework? Even just checking if the string contains net would allow us to avoid this in most cases where we don't need it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will contain 'net' in most cases: netstandard netcoreapp. I don't want to add regex or other parsing complexity here.

@srivatsn
Copy link
Contributor

Is this ready to merge now?

@nguerrera
Copy link
Contributor Author

Validating Daniel's question about change to AfterTargets and saw that there's an issue with the final change for the multi-targetng case. It won't kick in in full msbuild/VS because of the other issue where Microsoft.Nuget.targets is imported. The satellite test that's covering that is disabled on full msbuild right now.

Working on it.

@nguerrera
Copy link
Contributor Author

Jenkins is down, but validated locally that d1c2fd4 closes desktop msbuild test gap, and 5046d92 turns it green. @dsplaisted, @srivatsn please review those changes.

@nguerrera
Copy link
Contributor Author

Do not merge.

The change to deal with multi-targeting case has interactions with #870 and #828 that need to be discussed.

@natidea
Copy link
Contributor

natidea commented Feb 15, 2017

@dotnet-bot test this please

@nguerrera nguerrera force-pushed the remove-rid-requirement branch from cc62950 to efbcaca Compare February 15, 2017 19:57
@nguerrera
Copy link
Contributor Author

@dsplaisted @natidea @srivatsn I've now fixed the mulitargeting issue in a much more general way that:

Please review efbcaca, which does this and is squashed delta since sign-offs. I've also asked @emgarten to review.

I am also going to add some unit tests to check GetAllRuntimeIdentifiers directly and not just in the end-to-end scenario this PR sets out to address.

</ItemGroup>

<MSBuild Projects="$(MSBuildProjectFile)"
Condition="'$(TargetFrameworks)' != ''"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition doesn't seem to be strictly necessary, as this file should only be imported when TargetFrameworks is defined, right?

buildCommand
.Execute()
.Should()
.Pass();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be checking that the output is built using the correct RID, as is the case with It_handles_native_depdencies_and_platform_target?

@nguerrera
Copy link
Contributor Author

nguerrera commented Feb 16, 2017

@emgarten approved here #871 (comment)

He noted offline that rids are technically case sensitive, so I'll switch to DistinctWithCase().

I am still adding GetAllRuntimeIdentifiers unit tests and I'll add the coverage @dsplaisted requested above. I have to run to an appointment so I'll merge this later tonight once the extra tests are in.

@nguerrera
Copy link
Contributor Author

Abandoning DistinctWithCase because MSBuild is already deduping case elsewhere. I think we have bigger problems if real RIDs ever differ only by case (e.g. path conflicts, endless confusion), so I think we can ignore it.

@nguerrera nguerrera force-pushed the remove-rid-requirement branch from 2effe62 to 497b67c Compare February 16, 2017 08:03
@nguerrera nguerrera merged commit 1042b68 into dotnet:master Feb 16, 2017
@nguerrera nguerrera deleted the remove-rid-requirement branch February 16, 2017 08:46
mmitche pushed a commit to mmitche/sdk that referenced this pull request Jun 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove requirement of setting RuntimeIdentifier in project file for .NET Framework Exe projects
9 participants