Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvalidProjectFileException when opening a Project using XmlReader #7035

Closed
El-Gor-do opened this issue Nov 11, 2021 · 7 comments
Closed

InvalidProjectFileException when opening a Project using XmlReader #7035

El-Gor-do opened this issue Nov 11, 2021 · 7 comments
Labels
author-responded Author responded, needs response from dev team. bug needs-attention Needs a member of the team to respond. needs-more-info Issues that need more info to continue investigation. triaged

Comments

@El-Gor-do
Copy link

Issue Description

I use the Microsoft.Build and Microsoft.Build.Utilities.Core Nuget packages to open and inspect csproj files. Using v16.11.0 of these 2 packages, I can open csproj files successfully but v17.0.0 throws InvalidProjectFileException.

Steps to Reproduce

Console application project that opens an in-memory csproj.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Build" Version="16.11.0" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.11.0" />
  </ItemGroup>
</Project>

Console application's Program.cs

using System;
using System.IO;
using System.Xml;
using Microsoft.Build.Definition;
using Microsoft.Build.Evaluation;

namespace MsBuildTesting
{
    static class Program
    {
        static void Main()
        {
            // set environment variables based on
            // https://github.com/microsoft/MSBuildLocator/blob/88225e6e92744b40f7cf42637ccce090f63ea9db/src/MSBuildLocator/MSBuildLocator.cs#L310

            string sdkVersion = "6.0.100";
            Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", @$"C:\Program Files\dotnet\sdk\{sdkVersion}\MSBuild.dll");
            Environment.SetEnvironmentVariable("MSBuildSDKsPath", @$"C:\Program Files\dotnet\sdk\{sdkVersion}\Sdks");
            Environment.SetEnvironmentVariable("MSBuildExtensionsPath", @$"C:\Program Files\dotnet\sdk\{sdkVersion}\");

            try
            {
                OpenProject();
                Console.WriteLine("Project opened successfully");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private static void OpenProject()
        {
            string csproj = @"
<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
</Project>
";

            Project proj;
            using (MemoryStream ms = new MemoryStream())
            {
                // create csproj
                using (StreamWriter sw = new StreamWriter(ms, leaveOpen: true))
                {
                    sw.Write(csproj);
                }
                ms.Position = 0;

                // open csproj
                using (XmlReader xr = XmlReader.Create(ms))
                {
                    ProjectOptions options = new ProjectOptions();

                    // throws Microsoft.Build.Exceptions.InvalidProjectFileException when using Microsoft.Build 17.0.0 but not when using Microsoft.Build 16.11.0
                    proj = Project.FromXmlReader(xr, options);
                }
            }
        }
    }
}

Expected Behavior

The console application should print

Project opened successfully

Actual Behavior

If the console application references Microsoft.Build 16.11.0 and Microsoft.Build.Utilities.Core 16.11.0 then the console output correctly shows "Project opened successfully".

If the console application references Microsoft.Build 17.0.0 and Microsoft.Build.Utilities.Core 17.0.0 then an InvalidProjectFileException is thrown when calling Project.FromXmlReader(xr, options).

Analysis

If environment variable MSBUILD_EXE_PATH is not set the both 16.11.0 and 17.0.0 throw

Microsoft.Build.Exceptions.InvalidProjectFileException: The SDK 'Microsoft.NET.Sdk' specified could not be found.

If MSBUILD_EXE_PATH is set then 16.11.0 opens successfully, 17.0.0 throws

Microsoft.Build.Exceptions.InvalidProjectFileException: SDK Resolver Failure: "The SDK resolver "NuGetSdkResolver" failed while attempting to resolve the SDK "Microsoft.NET.Sdk". Exception: "System.ArgumentNullException: Value cannot be null. (Parameter 'path')

Using 17.0.0, if only MSBuildExtensionsPath is set then

Microsoft.Build.Exceptions.InvalidProjectFileException: The SDK 'Microsoft.NET.Sdk' specified could not be found.

My console application project is D:\dev\MsBuildTesting\MsBuildTesting.csproj. Using 17.0.0, if only MSBuildSDKsPath is set then

Microsoft.Build.Exceptions.InvalidProjectFileException: The imported project "D:\dev\MsBuildTesting\MsBuildTesting\bin\Debug\net6.0\Current\Microsoft.Common.props" was not found. Confirm that the expression in the Import declaration "D:\dev\MsBuildTesting\MsBuildTesting\bin\Debug\net6.0\Current\Microsoft.Common.props" is correct, and that the file exists on disk.  C:\Program Files\dotnet\sdk\6.0.100\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.props

Using 17.0.0, if both MSBuildSDKsPath and MSBuildExtensionsPath are set then

Microsoft.Build.Exceptions.InvalidProjectFileException: The SDK 'Microsoft.NET.SDK.WorkloadAutoImportPropsLocator' specified could not be found.  C:\Program Files\dotnet\sdk\6.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.ImportWorkloads.props

Versions & Configurations

I have both Visual Studio 2019 16.11.6 and Visual Studio 2022 17.0.0 installed.

I am running on Windows 10 Enterprise. Output from ver

Microsoft Windows [Version 10.0.19042.1348]

Output from dotnet --list-sdks

3.1.415 [C:\Program Files\dotnet\sdk]
5.0.203 [C:\Program Files\dotnet\sdk]
5.0.209 [C:\Program Files\dotnet\sdk]
5.0.300 [C:\Program Files\dotnet\sdk]
5.0.303 [C:\Program Files\dotnet\sdk]
5.0.400 [C:\Program Files\dotnet\sdk]
5.0.403 [C:\Program Files\dotnet\sdk]
6.0.100 [C:\Program Files\dotnet\sdk]

Output from msbuild -version

Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

17.0.0.52104
@El-Gor-do El-Gor-do added bug needs-triage Have yet to determine what bucket this goes in. labels Nov 11, 2021
@benvillalobos
Copy link
Member

Team Triage: Does this still reproduce if you use msbuildlocator?

@benvillalobos benvillalobos added the needs-more-info Issues that need more info to continue investigation. label Nov 11, 2021
@ghost ghost removed the needs-triage Have yet to determine what bucket this goes in. label Nov 11, 2021
@El-Gor-do
Copy link
Author

Using Microsoft.Build.Locator 1.4.1, the console app csproj has changed to

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Build" Version="16.11.0" ExcludeAssets="runtime" />
    <PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.11.0" ExcludeAssets="runtime" />
  </ItemGroup>
</Project>

Program.cs

using System;
using System.IO;
using System.Xml;
using Microsoft.Build.Definition;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Locator;

namespace MsBuildTesting
{
    static class Program
    {
        static void Main()
        {
            MSBuildLocator.RegisterDefaults();
            try
            {
                OpenProject();
                Console.WriteLine("Project opened successfully");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private static void OpenProject()
        {
            string csproj = @"
<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
</Project>
";

            Project proj;
            using (MemoryStream ms = new MemoryStream())
            {
                // create csproj
                using (StreamWriter sw = new StreamWriter(ms, leaveOpen: true))
                {
                    sw.Write(csproj);
                }
                ms.Position = 0;

                // open csproj
                using (XmlReader xr = XmlReader.Create(ms))
                {
                    ProjectOptions options = new ProjectOptions();

                    // throws Microsoft.Build.Exceptions.InvalidProjectFileException when using Microsoft.Build 16.11.0 and 17.0.0
                    proj = Project.FromXmlReader(xr, options);
                }
            }
        }
    }
}

Now whether the console app's Microsoft.Build package references are v16.11.0 or 17.0.0, Project.FromXmlReader(xr, options) throws

Microsoft.Build.Exceptions.InvalidProjectFileException: SDK Resolver Failure: "The SDK resolver "NuGetSdkResolver" failed while attempting to resolve the SDK "Microsoft.NET.Sdk". Exception: "System.ArgumentNullException: Value cannot be null. (Parameter 'path')

@ghost ghost added needs-attention Needs a member of the team to respond. author-responded Author responded, needs response from dev team. labels Nov 11, 2021
@jeffkl
Copy link
Contributor

jeffkl commented Nov 11, 2021

Thanks for reporting this. I've identified the issue. The NuGet SDK resolver throws an exception when an in-memory project is evaluated. This used to be logged as a warning and evaluation would continue. But now this exception fails evaluation because this change: #6763

The only workaround at the moment is to save the project to disk somewhere so the ArgumentNullException is not thrown and evaluation can succeed. In the meantime, we're looking into a fix.

@El-Gor-do
Copy link
Author

Project.FromXmlReader(...) is still borked when the csproj file is on either a mock disk or real disk.

console app csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Build" Version="17.0.0" ExcludeAssets="runtime" />
    <PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.0.0" ExcludeAssets="runtime" />
    <PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="13.2.47" />
  </ItemGroup>
</Project>

Program.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;
using System.Xml;
using Microsoft.Build.Definition;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Locator;

namespace MsBuildTesting
{
    static class Program
    {
        static void Main()
        {
            MSBuildLocator.RegisterDefaults();
            try
            {
                OpenProject();
                Console.WriteLine("Project opened successfully");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private static void OpenProject()
        {
            string csproj = @"
<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
</Project>
";

            IFileSystem fs = new MockFileSystem();  // use either MockFileSystem() or FileSystem()
            IFileInfo fi = fs.FileInfo.FromFileName(fs.Path.GetTempFileName());
            using (Stream s = fi.Open(FileMode.Create, FileAccess.ReadWrite))
            {
                // create csproj
                using (StreamWriter sw = new StreamWriter(s, leaveOpen: true))
                {
                    sw.Write(csproj);
                }
                s.Position = 0;

                // open csproj
                using (XmlReader xr = XmlReader.Create(s))
                {
                    ProjectOptions options = new ProjectOptions();

                    // throws Microsoft.Build.Exceptions.InvalidProjectFileException when using Microsoft.Build 16.11.0 and 17.0.0
                    Project proj = Project.FromXmlReader(xr, options);
                }
            }
        }
    }
}

This throws the same exception as before, even if I replace the MockFileSystem with FileSystem

Microsoft.Build.Exceptions.InvalidProjectFileException: SDK Resolver Failure: "The SDK resolver "NuGetSdkResolver" failed while attempting to resolve the SDK "Microsoft.NET.Sdk". Exception: "System.ArgumentNullException: Value cannot be null. (Parameter 'path')

@benvillalobos
Copy link
Member

Closing in favor of NuGet/Home#11376

@jeffkl
Copy link
Contributor

jeffkl commented Nov 18, 2021

@benvillalobos I think we should still have an open issue to get a unit test created that would have caught this break. It would have been nice to know it was breaking before 17.0 shipped so that NuGet wasn't caught off guard.

@benvillalobos
Copy link
Member

@jeffkl Good idea. I created #7049

@AR-May AR-May added the triaged label Feb 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author-responded Author responded, needs response from dev team. bug needs-attention Needs a member of the team to respond. needs-more-info Issues that need more info to continue investigation. triaged
Projects
None yet
Development

No branches or pull requests

4 participants