Enable TaskHostFactory for .NET (Core) MSBuild#6994
Conversation
This enables taskhost tests on .NET Core. Includes a couple of low- complexity test fixes.
e654a32 to
dd98059
Compare
| internal const string clr2 = "CLR2"; | ||
| internal const string clr4 = "CLR4"; | ||
| internal const string currentRuntime = "CurrentRuntime"; | ||
| internal const string net = "NET"; |
There was a problem hiding this comment.
@baronfel I'd like your input on this new name please. We need to add an entry to the list that indicates ".NET Core or .NET 5.0+" and is distinguishable from the longstanding CLR2 and CLR4. We should probably get buyoff from somebody in .NET Runtime PM about it.
There was a problem hiding this comment.
Reaching out to some folks for discussion 👍
There was a problem hiding this comment.
Where are these values exposed? Presumably you want input on the literal itself, not the name of the constant field, right?
There was a problem hiding this comment.
Correct. They are exposed to MSBuild task authors in the UsingTask element or "phantom parameters" on task invocations within targets (docs on existing CLR2/CLR4 options).
The existing options are "CLR2" which means "run on .NET Framework 3.5" and "CLR4" which means "run on .NET Framework 4.7.2+". We need one for "run on .NET 6.0+". If we'd done this before .NET 5.0, I would have selected CORE which I still kinda like, but since it's not .NET Core any more that's probably not the best choice.
There was a problem hiding this comment.
I lean towards "NET" now, personally. There's a long internal email thread where I think we've settled on this nomenclature.
benvillalobos
left a comment
There was a problem hiding this comment.
Looks mostly fine. My main concern is variable/property naming in the long term. I feel like NET as a value is not explicitly clear that we're in a "net core" case and it could be confused for net48 or net framework
|
|
||
| reservedProperties.Add(ProjectPropertyInstance.Create(ReservedPropertyNames.msbuildRuntimeType, | ||
| #if RUNTIME_TYPE_NETCORE | ||
| "Core", |
There was a problem hiding this comment.
Nit: The new handshake option is NET which refers to a net core build. Here the runtime is being set to Core if it's on net core. For consistency should they have the same name? When I first saw the handshake option I felt like it should be named NETCORE or something.
| if ((runtimeA.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase) && runtimeB.Equals(MSBuildRuntimeValues.clr4, StringComparison.OrdinalIgnoreCase)) || | ||
| (runtimeA.Equals(MSBuildRuntimeValues.clr4, StringComparison.OrdinalIgnoreCase) && runtimeB.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase))) | ||
| if ((runtimeA.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase) && runtimeB.Equals(GetCurrentMSBuildRuntime(), StringComparison.OrdinalIgnoreCase)) || | ||
| (runtimeA.Equals(GetCurrentMSBuildRuntime(), StringComparison.OrdinalIgnoreCase) && runtimeB.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase))) |
There was a problem hiding this comment.
grumbling out loud: funny how we ended up in a scenario where MSBuildRuntimeValues.currentRuntime and GetCurrentMSBuildRuntime() are two different things. Which lead to the variable name on line 221 string actualCurrentRuntime 😅
| { | ||
| clrVersion = 4; | ||
| } | ||
| else if (runtimeVersion.Equals(XMakeAttributes.MSBuildRuntimeValues.clr2, StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
nit: checking clr2 first is more intuitive. Or is this ordering intentional?
There was a problem hiding this comment.
Just a straight port of the existing logic but I agree that putting it in order makes sense.
There was a problem hiding this comment.
We should do the ordering based on which is expected to occur most often, so I'd even bump MSBuildRuntimeValue.net to the top.
There was a problem hiding this comment.
- That's an optimization that PGO should be great at and with small effect, so I don't think it's worth a readability hit without proof (and this isn't a super hot loop or anything).
- Why do you think
netwould happen most often?
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| using Microsoft.Build.Shared; | ||
|
|
There was a problem hiding this comment.
This is actually my preferred style:
- Block for
System./ BCL usings - Block for local-repo usings
- Block for third-party usings
| #if NET40_OR_GREATER | ||
| return MSBuildRuntimeValues.clr4; | ||
| #else | ||
| return MSBuildRuntimeValues.net; |
There was a problem hiding this comment.
Thinking this should be netcore or core to be extra clear.
Forgind
left a comment
There was a problem hiding this comment.
Looks pretty good. Mostly little things
| } | ||
|
|
||
| #if FEATURE_TASKHOST && !NO_MSBUILDTASKHOST | ||
| #if !NO_MSBUILDTASKHOST |
There was a problem hiding this comment.
Is the comment below still accurate? I'm wondering when we wouldn't have task hosts available.
There was a problem hiding this comment.
We don't have the 3.5 task host on .NET Core or Mono builds.
| #if RUNTIME_TYPE_NETCORE | ||
| "Core", | ||
| #elif MONO | ||
| NativeMethodsShared.IsMono ? "Mono" : "Full"); |
There was a problem hiding this comment.
Why would it be MONO but not IsMono?
There was a problem hiding this comment.
MSBuild compiled for mono but running on .NET Framework was a possible thing for a while. I don't think it's super relevant any more but this is copypasta from existing code and I'd rather have consistent behavior.
| { | ||
| clrVersion = 4; | ||
| } | ||
| else if (runtimeVersion.Equals(XMakeAttributes.MSBuildRuntimeValues.clr2, StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
We should do the ordering based on which is expected to occur most often, so I'd even bump MSBuildRuntimeValue.net to the top.
| internal static class NamedPipeUtil | ||
| { | ||
| internal static string GetPipeNameOrPath(string pipeName) | ||
| internal static string GetPipeNameOrPath(int? processId = null) |
There was a problem hiding this comment.
It looks like we're using this more. I'm curious if it will show up as extra MSBuild# string allocations.
|
|
||
| [Fact] | ||
| public void TestMergeRuntimeValues() | ||
| [Theory] |
There was a problem hiding this comment.
Nit:
This test and the previous one are similar in content but different in style. Switch to same style?
There was a problem hiding this comment.
I don't think the juice is worth the squeeze on this.
| <UsingTask TaskName="Microsoft.Build.Tasks.RegisterAssembly" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Condition="('$(MSBuildAssemblyVersion)' != '') and '$(DisableOutOfProcTaskHost)' != ''" /> | ||
| <UsingTask TaskName="Microsoft.Build.Tasks.RegisterAssembly" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Runtime="CLR4" Condition="('$(MSBuildAssemblyVersion)' != '') and '$(DisableOutOfProcTaskHost)' == ''" /> | ||
| <UsingTask TaskName="Microsoft.Build.Tasks.RegisterAssembly" AssemblyName="Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Runtime="CLR2" Condition="('$(MSBuildAssemblyVersion)' != '') and '$(DisableOutOfProcTaskHost)' == ''" /> | ||
| <UsingTask TaskName="Microsoft.Build.Tasks.RegisterAssembly" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Runtime="CLR4" Condition="('$(MSBuildAssemblyVersion)' != '') and '$(DisableOutOfProcTaskHost)' == '' and '$(MSBuildRuntimeType)' != 'Core'" /> |
There was a problem hiding this comment.
Do we need a core version of RegisterAssembly? Not sure if it's been ported. Same for UnregisterAssembly.
There was a problem hiding this comment.
COM build operations in general aren't functional on Core today. This doesn't change that but does potentially make it slightly clearer.
|
Oh, and I'm curious if there are any other issues (disabled tests) we can close with this PR. Have you checked/just know there aren't? |
rainersigwald
left a comment
There was a problem hiding this comment.
Accidentally didn't send the comment I made promptly 🤦🏻
| { | ||
| clrVersion = 4; | ||
| } | ||
| else if (runtimeVersion.Equals(XMakeAttributes.MSBuildRuntimeValues.clr2, StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
Just a straight port of the existing logic but I agree that putting it in order makes sense.
| internal const string clr2 = "CLR2"; | ||
| internal const string clr4 = "CLR4"; | ||
| internal const string currentRuntime = "CurrentRuntime"; | ||
| internal const string net = "NET"; |
There was a problem hiding this comment.
Correct. They are exposed to MSBuild task authors in the UsingTask element or "phantom parameters" on task invocations within targets (docs on existing CLR2/CLR4 options).
The existing options are "CLR2" which means "run on .NET Framework 3.5" and "CLR4" which means "run on .NET Framework 4.7.2+". We need one for "run on .NET 6.0+". If we'd done this before .NET 5.0, I would have selected CORE which I still kinda like, but since it's not .NET Core any more that's probably not the best choice.
| string architecture = null; | ||
|
|
||
| if (!mergedParameters.TryGetValue(XMakeAttributes.runtime, out runtime)) | ||
| if (!mergedParameters.TryGetValue(XMakeAttributes.runtime, out _)) |
There was a problem hiding this comment.
nit:
| if (!mergedParameters.TryGetValue(XMakeAttributes.runtime, out _)) | |
| if (!mergedParameters.ContainsKey(XMakeAttributes.runtime)) |
| } | ||
|
|
||
| if (!mergedParameters.TryGetValue(XMakeAttributes.architecture, out architecture)) | ||
| if (!mergedParameters.TryGetValue(XMakeAttributes.architecture, out _)) |
There was a problem hiding this comment.
nit:
| if (!mergedParameters.TryGetValue(XMakeAttributes.architecture, out _)) | |
| if (!mergedParameters.ContainsKey(XMakeAttributes.architecture)) |
| /// <summary> | ||
| /// Using the .NET Core/.NET 5.0+ runtime | ||
| /// </summary> | ||
| NET = 64, |
There was a problem hiding this comment.
It looks like this comment should be updated:
I checked lightly and didn't see anything. |
|
I see this hit the main branch. I'm wondering what SDK version(s) this was/will be released in? And/or how to find that information myself. Thanks! |
|
I don't know if this is a good way to do it at all, but you can look at the release/version branches in the dotnet/sdk repo then go to eng/Versions.Details.xml and find Microsoft.Build. That gives you a commit hash for the last MSBuild in that version, and you can go to our commits list to figure out if that's before or after this. Like for release/6.0.2xx, https://github.com/dotnet/sdk/blob/release/6.0.2xx/eng/Version.Details.xml#L71 says a02f736, which is a02f736, which came out in February, so it should also include this commit. From there, you can do binary search. |
Fixes #5158
Context
A task author can force any task to run out of process by specifying that it must run with
TaskFactory="TaskHostFactory". But we never made this work on .NET Core; it fails with an error likeThat's because the "default" task host runtime was hardcoded to be
CLR4(even when on .NET 6.0+).Changes Made
Created
GetCurrentMSBuildRuntime()and used it; plumbed the new value around and checked for it in a few places. EliminatedFEATURE_TASKHOSTsince it's on everywhere now. Unified the named-pipe-name computation code (before it was using a different pipe name for taskhost pipes on UNIX).Testing
Re-enabled tests for this behavior that have been disabled on .NET Core. Extended some to account for the
NETruntime.