dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.21k stars 1.35k forks source link

.NET Core MSBuild cannot load tasks built against MSBuild 4.0 #2111

Closed ghost closed 7 years ago

ghost commented 7 years ago

It seems that the dependencies of a task DLL built with VS2015 cannot be loaded when using those tasks in dotnet msbuild. Here's a reproducer using simple dummy files.

A primitive custom task, SimpleTaskThatExtends:

using Microsoft.Build.Utilities;

namespace SimpleTaskLibrary
{
    public class SimpleTaskThatExtends : Task
    {
        public override bool Execute()
        {
            Log.LogMessage("Simple task that extends Task");
            return true;
        }
    }
}

A primitive targets file using the above task:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <UsingTask TaskName="SimpleTaskThatExtends" AssemblyFile=".\bin\debug\SimpleTaskThatExtends.dll" />

  <Target Name="dummy">
    <SimpleTaskThatExtends />
  </Target>

</Project>

msbuild works:

msbuild /t:dummy Simple.targets

I get:

Microsoft (R) Build Engine version 15.1.548.43366
Copyright (C) Microsoft Corporation. All rights reserved.

Build started 5/19/2017 10:42:08 AM.
Project "C:\Users\me\Documents\Visual Studio 2015\Projects\SimpleTaskLibrary\SimpleTaskLibrary\Simple.targets
" on node 1 (dummy target(s)).
dummy:
  Simple task that extends Task
Done Building Project "C:\Users\me\Documents\Visual Studio 2015\Projects\SimpleTaskLibrary\SimpleTaskLibrary\
Simple.targets" (dummy target(s)).

Build succeeded.
    0 Warning(s)
    0 Error(s)

The same command but run through dotnet doesn't work:

dotnet msbuild /t:dummy Simple.targets

I get:

Microsoft (R) Build Engine version 15.1.1012.6693
Copyright (C) Microsoft Corporation. All rights reserved.

C:\Users\me\Documents\Visual Studio 2015\Projects\SimpleTaskLibrary\SimpleTaskLibrary\Simple.targets(9,5): error MSB4062: The "SimpleTaskThatExtends" task could not be loaded from the assembly C:\Users\me\Documents\Visual Studio 2015\Projects\SimpleTaskLibrary\SimpleTaskLibrary\.\bin\debug\SimpleTaskThatExtends.dll. Could not load file or assembly 'Microsoft.Build.Utilities.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified. 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.

If instead of extending Task in Microsoft.Build.Utilities I implement ITask in Microsoft.Build.Framework, then it works because it doesn't need to load Microsoft.Build.Utilities.v4.0.

I tried copying Microsoft.Build.Utilities.Core.* (.dll and .xml) in the same directory as the task dll. I tried both the net46 and netstandard1.3 versions. But the output is exactly the same.

I've read #658 and I was under the impression that the problem there and in #858 was fixed in 15.1.1012, which is the version I have (as you can see in the last output).

The bigger picture is that I have a task library that I would like to use in the builds of both VS2015 and .NET Core projects. I would rather not have two versions of the tasks. Is there something fundamentally wrong with that?

ghost commented 7 years ago

The short answer is that this is only the tip of the iceberg.

I could get past this error by removing the Microsoft.Build.Utilities.v4.0 reference, and adding instead the Microsoft.Build.Utilities NuGet dependency.

That solved the issue in the simplified reproducer I posted here, but in my real application I got further strange errors like:

error MSB4018: System.IO.FileNotFoundException: Could not load file or assembly 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. The system cannot find the file specified.

This and the original errors are just a symptom of a bigger problem: it's not safe to execute via dotnet applications that were built in <=VS2015 targeted for .NET Framework instead of .NET Core. When executed by dotnet, the runtime has a subset of .NET Framework features, and when the application tries to use something not in the runtime, it will of course crash. In my example my code is some Task implementations compiled with VS2015, executed as part of dotnet msbuild.

The solution seems to be to convert my .NET Framework application to .NET Core.

dasMulli commented 7 years ago

Your best option probably is multitargeting your tasks to a version of .net standard / .net core and .net framework like this and then use a different assembly based on $(MSBuildRuntimeType) (UsingTask example.

ManfredLange commented 7 years ago

@dasMulli Looks as if both of the links provided in your latest comment are now broken. Do you have updated links for your suggestion?

dasMulli commented 7 years ago

too bad.. updated them to permalinks

praeclarum commented 6 years ago

Hello, is there a fix for this? There are a lot of nuget DLLs that don't work int dotnet because of this.

jamesmontemagno commented 6 years ago

I was able to dig through: https://github.com/aspnet/EntityFrameworkCore/issues/8336

And add this:

<PackageReference Include="Xamarin.Forms" Version="2.4.0.38779">
      <ExcludeAssets>build</ExcludeAssets>
    </PackageReference>

I could build originally via msbuild, but not dotnet build due to this, which caused issues in VSTS for building. After adding this, it got farther, but since I have xaml pages in there it seems to fail to generate anything :(

jrahma commented 6 years ago

i keep getting:

/Users/jassim/.nuget/packages/xamarin.forms/2.4.0.280/build/netstandard1.0/Xamarin.Forms.targets(3,3): Error MSB4062: The "Xamarin.Forms.Build.Tasks.FixedCreateCSharpManifestResourceName" task could not be loaded from the assembly /Users/jassim/.nuget/packages/xamarin.forms/2.4.0.280/build/netstandard1.0/Xamarin.Forms.Build.Tasks.dll. Confirm that the 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. (MSB4062) (ZayedAlKhair)

taori commented 6 years ago

Same problem while loading C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Xamarin\iOS\Xamarin.iOS.Tasks.dll

Legends commented 4 years ago

@dasMulli

Your best option probably is multitargeting your tasks to a version of .net standard / .net core and .net framework ....

Just one little question: Why use multi-targeting here? Isn't using netstandard20enough? It should run on all platforms using netstandard20only?!

dasMulli commented 4 years ago

Why use multi-targeting here? Isn't using netstandard20 enough? It should run on all platforms using netstandard20 only?!

At least at the time of writing (2017) i believe VS didn't carry all necessary forwarding assemblies to load and run netstandard2.0 dlls. I believe this has now been mostly done, maybe @rainersigwald can comment on that.

(Basically when you reference a .NET Standard assembly / project, the build system will add some additional dlls for older versions of .NET Framework. Applications not built with this logic needed to add these assemblies manually in order to load .NET Standard assemblies).

Also do note that some Tasks like(d) to do some form of isolation using AppDomain on .NET Framework or AssemblyLoadContext on .NET Core so they needed to target both frameworks in order to use these specific APIs (e.g. to load a different version of Newtonsoft.Json than is included in Visual Studio).

rainersigwald commented 4 years ago

.NET 4.7.2+ (which is now required by MSBuild) does handle netstandard2.0 assemblies pretty well. If your task has no dependencies, you can usually get away with just a .NET Standard implementation. When referencing other libraries, especially but not limited to native code, things can get hairy, and multitargeting is often still required.