microsoft / MSBuildLocator

An API to locate MSBuild assemblies from an installed Visual Studio location. Use this to ensure that calling the MSBuild API will use the same toolset that a build from Visual Studio or msbuild.exe would.
Other
212 stars 83 forks source link

No instance found when project targets .NET 6 but only 7.X SDK is installed #195

Closed BalassaMarton closed 1 year ago

BalassaMarton commented 1 year ago

Steps to reproduce

  1. Create new console app targeting .NET 6
  2. Install latest Microsoft.Build.Locator package
  3. Replace contents of Program.cs with below code:
    
    using Microsoft.Build.Locator;

namespace MSBuildLocatorTest { internal class Program { static void Main(string[] args) { MSBuildLocator.RegisterDefaults(); } } }



### Expected behavior

Call to `RegisterDefaults` should succeed.

### Actual behavior

`InvalidOperationException` with message "No instances of MSBuild could be detected [...]"

### Environment

Visual Studio Community, version 17.4.4
.NET SDK installed according to `dotnet --list-sdks`: 7.0.102

### Workaround

Installing .NET 6 SDK solves the problem.
Forgind commented 1 year ago

An SDK of a particular version sometimes uses APIs not released until the corresponding runtime, so the 7.0.102 may use APIs present in the 7.0.102 runtime that aren't present in the 6.0 runtime. Since you're targeting .NET 6, it doesn't have access to those APIs, so using the MSBuild from that SDK may not work in surprising ways, so it isn't findable.

Similarly, using framework MSBuild (as from Visual Studio) would not work properly from a .NET-targeted app, so we did not "find" Visual Studio either.

When you installed .NET 6, that SDK was findable because it is guaranteed to only use APIs available in the runtime you're targeting. That's why it started working.

Hope that helps!

BalassaMarton commented 1 year ago

So how should we solve this? NuGet tools usually don't require a specific SDK to be installed. Should we redistribute the binaries from the Microsoft.Build.* packages, instead of using the locator? Does the license allow that?

Forgind commented 1 year ago

Can you elaborate on the problem you're trying to solve? If you don't know that the user will have a 6.0 or lower SDK, your primary alternative is to target a higher runtime, i.e., if you change the console app in your description to target 7.0.102, then it should have no problem finding the 7.0.102 SDK. Would either of those work for you?

BalassaMarton commented 1 year ago

The problem is that MSBuild.Locator fails if the machine doesn't have an SDK installed that matches the target runtime of the console app, and this is not documented. I ended up with adding the Microsoft.Build packages as regular dependencies, and added some code that sets some environment variables so that SDK imports still work.

Forgind commented 1 year ago

I kinda implied that it has to be an exact match, but that was misleading; sorry about that. If you have an SDK with a lower version than your runtime, MSBuildLocator can also find that. /cc: @ghogen for documentation

What environment variables did you set?

BalassaMarton commented 1 year ago

This is how I fixed it: https://github.com/morganstanley/dotnet-please/commit/09648b80126664d65d16fd43b66cb28f95096714#diff-4f8d24ed30c8de2ba2ba6939649e4ea978392d2293ae967bd8c23d3f1f77d8c3R454

Forgind commented 1 year ago

That looks similar to reverting to an earlier version of MSBuildLocator, but it seems like you aren't actually using MSBuild APIs; you just need to locate an MSBuild? If so, that's actually a scenario in which it should be safe to find a higher version of the SDK.

We don't currently have a way to opt into that, unfortunately. I've been considering whether we should add a way, but it hasn't materialized yet.