Msbuild: MSBuild on .NET Core does not allow tasks with same assembly name, but different assembly versions

Created on 1 Aug 2018  路  12Comments  路  Source: dotnet/msbuild

With desktop MSBuild / VS, we've gotten around issues with node reuse and varying versions of the SDK by increasing the assembly version of the SDK tasks on every build. (Aside: we've regressed that a bunch of times with infrastructure changes.)

However, this does not work on .NET Core. I suspect we haven't noticed this until now because node reuse has only made its way to .NET Core recently.

Steps to reproduce

Run ReproCore.cmd from https://github.com/nguerrera/repros/commit/fc1e1df38786edff4258c6c2ed09ec6f4c89c4a3

WARNING: First step of repro kills msbuild.exe processes, so don't do this with real builds in progress in parallel.

Command line

git clone https://github.com/nguerrera/repros
cd repros
git checkout fc1e1df38786edff4258c6c2ed09ec6f4c89c4a3
cd NodeReuseAndAssemblyVer
ReproCore.cmd

Expected behavior

Final build command in .cmd succeeds as it does on Desktop (try ReproDesktop.cmd instead).

Actual behavior

D:\Src\repros\NodeReuseAndAssemblyVer\repro.proj(16,9): error MSB4062: The "MyTask" task could not be loaded from the assembly D:\Src\repros\NodeReuseAndAssemblyVer\bin\2.0.0\netstandard2.0\MyTask.dll.
Assembly with same name is already loaded Confirm that the <UsingTask> declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements
Microsoft.Build.Framework.ITask.

Environment data

Microsoft (R) Build Engine version 15.8.160-preview+gaf9d27ba72 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

15.8.160.40367

.NET Core SDK (reflecting any global.json):
 Version:   2.1.400-preview-009171
 Commit:    6f5d38734b

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.1.400-preview-009171\

cc @davkean @rainersigwald @dsplaisted

.NET Core Engine bug

Most helpful comment

I'm currently using something similar to https://github.com/AArnott/Nerdbank.MSBuildExtension/blob/master/src/Nerdbank.MSBuildExtension/netstandard1.5/ContextIsolatedTask.cs
to work around conflicting references.

What popped up on SO was that loading assemblies that are also used by the SDK (newtonsoft.json) are impossible to load (without a custom AssemblyLoadContext to execute your logic).

TL;DR having a load context per task assembly in general would be great! (https://github.com/Microsoft/msbuild/issues/1754)

All 12 comments

Hmm, this repro might not be good because the assembly isn't strong name signed. I suspect desktop is silently reloading old version. Let me update it to strong name sign the assembly...

Repro fixed to strong name task. Still gives the unexpected behavior on core.

This may be a limitation of putting all tasks in default assembly load context.

That's plausible. This throws:

image

System.IO.FileLoadException: Assembly with same name is already loaded
   at System.Runtime.Loader.AssemblyLoadContext.LoadFromPath(IntPtr ptrNativeAssemblyLoadContext, String ilPath, String niPath, ObjectHandleOnStack retAssembly)
   at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String assemblyPath)
   at Microsoft.Build.Shared.CoreClrAssemblyLoader.LoadAndCache(String fullPath) in /_/src/Shared/CoreCLRAssemblyLoader.cs:line 146
   at Microsoft.Build.Shared.CoreClrAssemblyLoader.LoadFromPath(String fullPath) in /_/src/Shared/CoreCLRAssemblyLoader.cs:line 72
   at Microsoft.Build.Shared.TypeLoader.LoadAssembly(AssemblyLoadInfo assemblyLoadInfo) in /_/src/Shared/TypeLoader.cs:line 181
   at Microsoft.Build.Shared.TypeLoader.AssemblyInfoToLoadedTypes.ScanAssemblyForPublicTypes() in /_/src/Shared/TypeLoader.cs:line 370
   at Microsoft.Build.Shared.TypeLoader.AssemblyInfoToLoadedTypes.<>c__DisplayClass8_0.<GetLoadedTypeByTypeName>b__0(String key) in /_/src/Shared/TypeLoader.cs:line 341
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Build.Shared.TypeLoader.AssemblyInfoToLoadedTypes.GetLoadedTypeByTypeName(String typeName) in /_/src/Shared/TypeLoader.cs:line 314
   at Microsoft.Build.Shared.TypeLoader.GetLoadedType(ConcurrentDictionary`2 cache, String typeName, AssemblyLoadInfo assembly) in /_/src/Shared/TypeLoader.cs:line 242
   at Microsoft.Build.Shared.TypeLoader.Load(String typeName, AssemblyLoadInfo assembly) in /_/src/Shared/TypeLoader.cs:line 208
   at Microsoft.Build.BackEnd.AssemblyTaskFactory.InitializeFactory(AssemblyLoadInfo loadInfo, String taskName, IDictionary`2 taskParameters, String taskElementContents, IDictionary`2 taskFactoryIdentityParameters, Boolean taskHostFactoryExplicitlyRequested, TargetLoggingContext targetLoggingContext, ElementLocation elementLocation, String taskProjectFile) in /_/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs:line 278

And indeed, from https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/assemblyloadcontext.md#custom-loadcontext

Multiple assemblies with the same simple name cannot be loaded into a single load context (Default or Custom). Also, .Net Core ignores strong name token for assembly binding process.

So I guess we should have a load context per . . . assembly path?

So I guess we should have a load context per . . . assembly path?

Yes. More precisely, per UsingTask AssemblyFile path. Dependencies of the task would come into the same load context as the task.

This is very much related to the challenges that @natemcmaster outlined here: https://natemcmaster.com/blog/2018/07/25/netcore-plugins/

Many things he has mentioned have bit msbuild loading tasks and roslyn loading analyzers in turn.

cc @jeffschwMSFT

I guess this comes down to more motivation for https://github.com/Microsoft/msbuild/issues/1754

I'm currently using something similar to https://github.com/AArnott/Nerdbank.MSBuildExtension/blob/master/src/Nerdbank.MSBuildExtension/netstandard1.5/ContextIsolatedTask.cs
to work around conflicting references.

What popped up on SO was that loading assemblies that are also used by the SDK (newtonsoft.json) are impossible to load (without a custom AssemblyLoadContext to execute your logic).

TL;DR having a load context per task assembly in general would be great! (https://github.com/Microsoft/msbuild/issues/1754)

What's the status of this bug? Customers are encountering problems with the build tasks in Grpc.Tools:

https://github.com/grpc/grpc/issues/20501

I believe #4916 would address this and planned for 16.5 / sdk 3.1.200, right, @rainersigwald ?

Correct; I'll close this as a duplicate.

Duplicate of #1754

Was this page helpful?
0 / 5 - 0 ratings