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 App fails to load project (.NET 4.6.1 works as expected) #3434

Open paulvanbrenk opened 6 years ago

paulvanbrenk commented 6 years ago

I created a sample .NET Console app with the following code.

using System;
using Microsoft.Build.Evaluation;

namespace ConsoleApp25
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var buildEngine = new ProjectCollection();
            var project = buildEngine.LoadProject("<full path>\\UnitTestProject.csproj");
       }
    }
}

My project references the following 2 Nuget packages:

This gives the following exception when compiled to target .NET Core 2.0;

Microsoft.Build.Exceptions.InvalidProjectFileException
  HResult=0x80131500
  Message=The SDK 'Microsoft.NET.Sdk' specified could not be found.  d:\dd\GitHubTestAdapter\UnitTestProject\UnitTestProject.csproj
  Source=Microsoft.Build
  StackTrace:
   at Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject(String errorSubCategoryResourceName, IElementLocation elementLocation, String resourceName, Object[] args) in /_/src/Shared/ProjectErrorUtilities.cs:line 412
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpressionConditioned(String directoryOfImportingFile, ProjectImportElement importElement, List`1& projects, Boolean throwOnFileNotExistsError) in /_/src/Build/Evaluation/Evaluator.cs:line 2173
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImports(String directoryOfImportingFile, ProjectImportElement importElement) in /_/src/Build/Evaluation/Evaluator.cs:line 1944
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement) in /_/src/Build/Evaluation/Evaluator.cs:line 1823
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport) in /_/src/Build/Evaluation/Evaluator.cs:line 940
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate(ILoggingService loggingService, BuildEventContext buildEventContext) in /_/src/Build/Evaluation/Evaluator.cs:line 726
   at Microsoft.Build.Evaluation.Project.Reevaluate(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings) in /_/src/Build/Definition/Project.cs:line 2752
   at Microsoft.Build.Evaluation.Project.ReevaluateIfNecessary(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings) in /_/src/Build/Definition/Project.cs:line 2719
   at Microsoft.Build.Evaluation.Project.Initialize(IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext) in /_/src/Build/Definition/Project.cs:line 2822
   at Microsoft.Build.Evaluation.Project..ctor(String projectFile, IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext) in /_/src/Build/Definition/Project.cs:line 543
   at Microsoft.Build.Evaluation.ProjectCollection.LoadProject(String fileName, IDictionary`2 globalProperties, String toolsVersion) in /_/src/Build/Definition/ProjectCollection.cs:line 1118
   at ConsoleApp25.Program.Main(String[] args) in D:\tmp\source\repos\ConsoleApp25\ConsoleApp25\Program.cs:line 11

However it works as expected when targeting .NET 4.6.1

Any ideas?

ConsoleApp25.zip

rainersigwald commented 6 years ago

Can you see if the documentation at https://docs.microsoft.com/en-us/visualstudio/msbuild/updating-an-existing-application helps? Feedback welcome, but I think it addresses your use case.

paulvanbrenk commented 6 years ago

The documentation suggests it would, but I can't access the MSBuildLocator type in my .NET Core app. (The assemblies in the package only target .NET 4.6).

paulvanbrenk commented 6 years ago

When I run the project from the command line (using dotnet run), I get some more information:

Unhandled Exception: Microsoft.Build.Exceptions.InvalidProjectFileException: The imported project "C:\vs\dogfood\MSBuild\15.0\Bin\15.0\Microsoft.Common.props" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.  C:\Program Files\dotnet\sdk\2.1.400-preview-008963\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.props
   at Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject(String errorSubCategoryResourceName, IElementLocation elementLocation, String resourceName, Object[] args) in /_/src/Shared/ProjectErrorUtilities.cs:line 412
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpression(String directoryOfImportingFile, ProjectImportElement importElement, String unescapedExpression, Boolean throwOnFileNotExistsError, List`1& imports) in /_/src/Build/Evaluation/Evaluator.cs:line 2418
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpressionConditioned(String directoryOfImportingFile, ProjectImportElement importElement, List`1& projects, Boolean throwOnFileNotExistsError) in /_/src/Build/Evaluation/Evaluator.cs:line 2173
   at Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImports(String directoryOfImportingFile, ProjectImportElement importElement) in /_/src/Build/Evaluation/Evaluator.cs:line 1944
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement) in /_/src/Build/Evaluation/Evaluator.cs:line 1823
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport) in /_/src/Build/Evaluation/Evaluator.cs:line 997
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement) in /_/src/Build/Evaluation/Evaluator.cs:line 1825
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport) in /_/src/Build/Evaluation/Evaluator.cs:line 940
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate(ILoggingService loggingService, BuildEventContext buildEventContext) in /_/src/Build/Evaluation/Evaluator.cs:line 726
   at Microsoft.Build.Evaluation.Project.Reevaluate(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings) in /_/src/Build/Definition/Project.cs:line 2752
   at Microsoft.Build.Evaluation.Project.ReevaluateIfNecessary(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings) in /_/src/Build/Definition/Project.cs:line 2719
   at Microsoft.Build.Evaluation.Project.Initialize(IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext) in /_/src/Build/Definition/Project.cs:line 2822
   at Microsoft.Build.Evaluation.Project..ctor(String projectFile, IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext) in /_/src/Build/Definition/Project.cs:line 543
   at Microsoft.Build.Evaluation.ProjectCollection.LoadProject(String fileName, IDictionary`2 globalProperties, String toolsVersion) in /_/src/Build/Definition/ProjectCollection.cs:line 1118
   at ConsoleApp25.Program.DoWork() in d:\tmp\source\repos\ConsoleApp25\ConsoleApp25\Program.cs:line 44
   at ConsoleApp25.Program.Main(String[] args) in d:\tmp\source\repos\ConsoleApp25\ConsoleApp25\Program.cs:line 37

The only Microsoft.Common.props file I can find in my VS install directory is: C:\vs\dogfood\MSBuild\15.0\Microsoft.Common.props.

KirillOsenkov commented 6 years ago

I can confirm that it doesn't work when Paul's app that uses MSBuild is targeting netcoreapp20:

Unhandled Exception: Microsoft.Build.Exceptions.InvalidProjectFileException: The imported project "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\15.0\Microsoft.Common.props" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.  C:\Program Files\dotnet\sdk\2.1.300-preview1-008174\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.props

Looks like in both cases it's appending an unnecessary \15.0 to the path to Microsoft.Common.props.

Adding some SDK experts @dsplaisted @nguerrera. Still not sure if the bug is in the SDK or elsewhere, but hopefully someone can shed some light. The repro is easy (just had to change the hardcoded path to the test project on Paul's drive).

baruchiro commented 5 years ago

It is becoming more and more required, depending on the porting to the .net core. At the moment I don't see any libraries that provide the capabilities that this library provides, and there is no library that supports net core. Can anyone direct me to where the solution might be? I'm not yet familiar with the MSBuild code.

Alxandr commented 5 years ago

I just hit this issue. Are there any known workarounds?

rainersigwald commented 5 years ago

MSBuildLocator now supports .NET Core applications. Are you using it, @Alxandr?

Alxandr commented 5 years ago

I'm using nuke-build. So I'm not directly using anything. I might look into it though.

Alxandr commented 5 years ago

I tried doing the following:

using System;
using System.Collections.Generic;
using System.Text;

static class Program
{
    public static int Main()
    {
        Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
        return Build.Run();
    }
}

And I still got the same error.

Here's output from the immediate window before doing Build.Run() (I inserted > in front of the input lines):

> Microsoft.Build.Locator.MSBuildLocator.IsRegistered
true
> Environment.GetEnvironmentVariable("MSBUILD_EXE_PATH")
"C:\\Program Files\\dotnet\\sdk\\2.2.300\\MSBuild.dll"

Edit: And it's failing here: https://github.com/nuke-build/common/blob/35404e91f1a283cddf5e5659ef7011ba44d17873/source/Nuke.Common/ProjectModel/ProjectModelTasks.cs#L47

LegendaryB commented 3 years ago

Any news on this issue?

dsplaisted commented 3 years ago

@Forgind @BenVillalobos

As far as I know MSBuildLocator should work on .NET Core. I'm not sure what the issue with nuke-build was, or whether it's been resolved yet.

Forgind commented 3 years ago

It should, yes. @Alxandr, would you mind sharing the version of your repro that used MSBuildLocator?

Alxandr commented 3 years ago

I don't even remember which project I was using this on anymore, so I'm not going to to try to recreate it half a year later, sorry.

KirillOsenkov commented 3 years ago

I can still easily reproduce the problem:

Use this .csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Build" Version="16.8.0" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.8.0" />
  </ItemGroup>

</Project>

and this .cs:

using Microsoft.Build.Evaluation;

class Program
{
    private static void Main(string[] args)
    {
        var buildEngine = new ProjectCollection();
        var project = buildEngine.LoadProject("some.csproj");
    }
}

It works fine if you're targeting net472, but fails if you're targeting net5.0.

To workaround, you need to add a reference to Microsoft.Build.Locator. Change the .csproj to this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Build" Version="16.8.0" ExcludeAssets="runtime" />
    <PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.8.0" ExcludeAssets="runtime"/>
  </ItemGroup>

</Project>

and the .cs to this:

using Microsoft.Build.Evaluation;

class Program
{
    private static void Main(string[] args)
    {
        Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
        Test();
    }

    private static void Test()
    {
        var buildEngine = new ProjectCollection();
        var project = buildEngine.LoadProject("C:\\temp\\multi\\a\\a.csproj");
    }
}

Note how the first usage of MSBuild API has to be in a separate method, because otherwise JIT will try to resolve Microsoft.Build.dll before MSBuildLocator was registered.

I'm still curious why the desktop version works without MSBuildLocator, but .NET 5 version needs it.

Forgind commented 3 years ago

I'm still curious why the desktop version works without MSBuildLocator, but .NET 5 version needs it.

The part of this that confuses me is not that it doesn't work with .NET 5 but that it works with .NET Framework. It's meaningless to say you need __ part of the MSBuild API if you don't specify which version of MSBuild you want to use. That suggests to me that there's some default logic that tries to guess which MSBuild you are trying to use, in which case it would make sense that it would only be right if it knows what framework to use, and it presumably hasn't been updated since Core came out. I'm curious what would happen if you tried it on a computer with Core MSBuild but not Framework MSBuild.

rizi commented 2 years ago

For me everything works with .net 5.0, but as soon as I use .net 6.0 I have to use Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();

any updates on this issue (at least for .net 6.0)?

Forgind commented 2 years ago

What versions of MSBuild do you have? If you have net5.0 and net6.0 but nothing lower, that might validate my guess.

rizi commented 2 years ago

What versions of MSBuild do you have? If you have net5.0 and net6.0 but nothing lower, that might validate my guess.

I will check tomorrow, but I have VS 2019 and VS 2022 installed. .net core 3, .net 5 and .net 6.0 SDKs and runtimes.

I also have.net 4.7.2 installed. So I think I have the latest version of msbuild and at least one version before the latest version. Br

Forgind commented 2 years ago

Ah, ok. Not what I'd been hoping, then. Thanks for checking.