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

Linux, MSBuild does not support the task factory "CodeTaskFactory" #616

Closed jyoungyun closed 8 years ago

jyoungyun commented 8 years ago

I got the below error message when I tried to build csproj file in coreclr project.

error MSB4801: The task factory "CodeTaskFactory" could not be loaded because this version of MSBuild does not support it. [/home/jyoung/git/dotnet/coreclr_buildtestsh/tests/src/GC/Regressions/v2.0-beta2/462651/462651.csproj]

The MSBuild.exe version is 14.1 and it is downloaded by init-tools.sh from nuget.

root@jyoung-Samsung-DeskTop-System:/home/jyoung/git/dotnet/coreclr_buildtestsh/Tools# ./corerun MSBuild.exe /version
Microsoft (R) Build Engine version 14.1.0.0
Copyright (C) Microsoft Corporation. All rights reserved.

14.1.0.0

CoreClr creates GenerateParamList in CLRTest.Execute.targets like below. But as I know, the latest version does not support Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll. Instead we can use Microsoft.Build.Tasks.Core.dll. So I changed it but it still failed and the error msg was the same.

<UsingTask                                                                    
    TaskName="GenerateParamList"                                                
    TaskFactory="CodeTaskFactory"                                               
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">          

What can I do to fix it ?

(I tired to build msbuild too, because it also uses UsingTask element. But it built successfully and there is no distinction btw init-tools.sh in coreclr and init-tools.sh in msbuild. I think this issue might be related to CoreCLR envrionment. But I couldn't find it...)

wjk commented 8 years ago

@jyoungyun Unfortunately, MSBuild on .NET Core does not support CodeTaskFactory. This is because CodeTaskFactory is built using the deprecated System.CodeDom.Compiler APIs, that are only available in the full .NET Framework, which is Windows-only. (CodeTaskFactory can in theory be rewritten on top of the open-source Roslyn project, but that entails significant work and has not yet been attempted.)

jyoungyun commented 8 years ago

@wjk Thank you for your comments! I can't get any info about this error. But I can build MSBuild successfully on Linux and it also uses UsingTask with CodeTaskFactory. What is the difference btw MSBuild and CoreCLR? (MSBuild uses the element in source file by comparison with targets file in CoreCLR. Does it make difference?)

wjk commented 8 years ago

Most likely when you build MSBuild on Linux, it has been compiled against Mono instead of .NET Core. Unlike .NET Core, Mono supports System.CodeDom.Compiler.

jyoungyun commented 8 years ago

@wjk Got it. I need to find other way to replace CodeTaskFactory. Your comments are a lot of help to me. Thank you!

rainersigwald commented 8 years ago

@jyoungyun You can always compile your own task assembly as a DLL and load it from there, instead of compiling the assembly at build time with CodeTaskFactory. The full-framework instructions at https://msdn.microsoft.com/en-us/library/t9883dzc.aspx look good, except that the project should a) target the CoreCLR/.NET Standard 1.3 and b) reference the CoreCLR, 14.1 versions of MSBuild assemblies.

jyoungyun commented 8 years ago

@rainersigwald Thank you for your comments! I'm trying to create a task assembly as a DLL. And I'm going to use UsingTask element to load the task, it makes the same result with CodeTaskFactory I think.

iskiselev commented 7 years ago

@rainersigwald, will task that reference 14.1 versions of MSBuild assemblies work with Visual Studio 2013 or earlier? What is recommended way to create msbuild task, that will work in earlier VS version alongside with dotnet build?

dls314 commented 6 years ago

For those arriving from search, see also: https://github.com/jeffkl/RoslynCodeTaskFactory

rainersigwald commented 6 years ago

@iskiselev There's no good way to write a single task assembly that works on MSBuild 4 or 12 and on .NET Core MSBuild. I'd suggest shipping multiple assemblies, one compiled against an old MSBuild and one compiled against the 14.1 assemblies, and using two <UsingTask>s conditioned on $(MSBuildAssemblyVersion).

ro-jo commented 6 years ago

I know this topic is closed. I installed RoslynCodeTaskFactory and it works perfectly, but with one problem - there's a catch-22 isn't there?

It's unacceptable to me to commit changes that will break builds, am I doing something wrong, or should RoslynCodeTaskFactory be integrated into the default dotnet msbuild tools?

jeffkl commented 6 years ago

@ro-jo The <UsingTask /> should have a condition on it so that its not created unless packages have been restored. My sample has the condition on there but people miss it. Condition=" '$(RoslynCodeTaskFactory)' != '' "

<UsingTask  
    TaskName="DoNothing"  
    TaskFactory="CodeTaskFactory"  
    AssemblyFile="$(RoslynCodeTaskFactory)"
    Condition=" '$(RoslynCodeTaskFactory)' != '' ">

Users must restore packages before building to ensure a successful result. RoslynCodeTaskFactory is no different then any package in that way. If you reference Newtonsoft.Json but a user does not restore first, they will get compilation errors.

dotnet build does an implicit restore for you unless you specify --no-restore. So by adding a condition and users just running dotnet build, everything should work perfectly. On Windows, Visual Studio does a restore before building and MSBuild.exe has a /restore argument that runs a restore prior to building.

ro-jo commented 6 years ago

@jeffkl Firstly - thanks for providing CodeTaskFactory - it's the most useful of all MSBuild tasks. In fact it's probably the only task anyone needs.

I had the condition, everything looked ok but still NuGet restore wasn't pulling the package, and even after I installed it manually, $(RoslynCodeTaskFactory) was empty...

I was calling my 'UsingTask' with AfterTargets = 'PrepareForBuild', I thought it might be the reason, so changed it to AfterTargets='Restore' - still didn't work.

Then I noticed that for some reason 'InitialTargets' in the project file had been set to the target that was calling the CodeTaskFactory 'UsingTask'. I deleted it and everything works as expected. I just didn't notice the change. It wasn't put there by VS or anything, one of our team did it - sorry.

abatishchev commented 6 years ago

Any chance CodeTaskFactory will work one day on Core without 3rd party packages?

eduherminio commented 6 years ago

https://github.com/Microsoft/msbuild/pull/3175

abatishchev commented 6 years ago

Cool! Do you know what version of Core is going to include that?

eduherminio commented 6 years ago

I guess it will be included in next VS minor release (v15.8), can you confirm it @jeffkl?

jeffkl commented 6 years ago

Cool! Do you know what version of Core is going to include that?

RoslynCodeTaskFactory will be available in daily builds of .NET Core CLI after https://github.com/dotnet/cli/pull/9279 goes in.

I guess it will be included in next VS minor release (v15.8), can you confirm it @jeffkl?

Yes, the RoslynCodeTaskFactory will first be available in Visual Studio 2017 15.8 Preview 2. I think that it will be out in the next week or so. My guess is that Visual Studio 2017 15.8 RTM will ship in the summer.

ro-jo commented 6 years ago

@jeffkl CodeTaskFactory already works in visual studio with core projects (from .Net runtime). It's needed in builds run from the .Net core command line. Am I missing something?

jeffkl commented 6 years ago

Yeah its a little confusing. Visual Studio uses MSBuild.exe which is compiled against .NET Framework 4.6. The .NET Core SDK is a collection of build targets and tasks that can run on .NET Framework or .NET Core and emit assemblies that can target either.

So you only truly run .NET Core MSBuild when you run dotnet build. The .NET Core MSBuild can't use the CodeTaskFactory because it uses CodeDom which does not support compiling assemblies cross-platform (it throws a runtime exception that the platform is not supported on Linux and Mac).

The RoslynCodeTaskFactory generates code, references netstandard.dll, and uses the managed compilers that are next to MSBuild. This means it can run the same on .NET Framework and .NET Core.

We did want to just make CodeTaskFactory work on .NET Core but it would have been a very intrusive change which would have a high likelihood of a regression. The last thing we want is for people to upgrade to the latest MSBuild and their build is broken ☹️. So we chose to introduce a new task factory that you have to opt-in to. That way we don't break anyone.

abatishchev commented 6 years ago

Why don't rename the new RoslynCodeTaskFactory to just CodeTaskFactory so nobody would notice?

And another question, please. When the new task will be available on build servers? That's what interests me, rather than when it's shipped with VS. as nothing is worse than when a build works fine locally and not at all on the build server (Windows container on VSTS in my case).

jeffkl commented 6 years ago

Why don't rename the new RoslynCodeTaskFactory to just CodeTaskFactory so nobody would notice?

If there's a bug or difference in how RoslynCodeTaskFactory works, you could take a 10 year old code base, upgrade to the latest MSBuild, and no longer be able to build. That is what I'm trying to avoid. If you need to use inline tasks on .NET Core, you've never been able to before. So to start using a new thing, you'll need to use a different task factory. And if you don't care, you don't change the task factory name and we have no chance of breaking you.

When the new task will be available on build servers?

I am not certain how often build servers are updated. I know that AppVeyor has a Visual Studio Preview version which is updated regularly. RoslynCodeTaskFactory will be publicly available in 15.8 Preview 2. Each build system will need to update to that version before you'll be able to use it. And of course, if you really can't wait, you can use my NuGet package immediately.