Open vpenades opened 3 years ago
This is not a particularly nice workaround because it makes the library pretty pointless, as you have to tell the "locator" library exactly where to look, but you can do this from a .NET Core app (on Windows), and it will be able to load both .NET Core and .NET Framework projects:
MSBuildLocator.RegisterMSBuildPath(@"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin");
Seems that DiscoveryType.DeveloperConsole
and DiscoveryType.VisualStudioSetup
only work on the .NET Framework version of this package, and DiscoveryType.DotNetSdk
only works on the .NET Core version of this package, so this returns completely different results depending on the target framework of the project that is referencing the package:
foreach (var instance in MSBuildLocator.QueryVisualStudioInstances(
new VisualStudioInstanceQueryOptions
{
DiscoveryTypes = DiscoveryType.DeveloperConsole | DiscoveryType.DotNetSdk | DiscoveryType.VisualStudioSetup
}))
{
Console.WriteLine($"{instance.DiscoveryType} {instance.MSBuildPath}");
}
But if you take the paths from either of them and just pass them to MSBuildLocator.RegisterMSBuildPath
, they work in the other, so it's a bit annoying that it doesn't just find all the same paths regardless of target framework. You can specify DiscoveryType
but it's mostly pointless, because it ignores it when finding them, and then only uses it to throw away results afterwards.
The problem with that approach is that our project runs on different environments, and it's not guaranteed the path to pass to RegisterMSBuildPath is the same on all machines (specially those with different versions of VS)
Yes, it's not ideal, and I'm not making any excuses for this library. I'm just a consumer of this library, and I'm sharing my workaround.
Exactly how you get the path to the directory containing msbuild will depend on your environments. For my usage, I can just pass it in as an argument. You might be able to search your path environment variables, or use where.exe msbuild
to find it, or save it to an environment variable on the machine, or you might be able to do something like this:
var msbuildDirectories = new[]
{
@"C:\Program Files (x86)\Microsoft Visual Studio",
@"C:\Program Files\Microsoft Visual Studio"
}
.Where(Directory.Exists)
.SelectMany(path => Directory.EnumerateFiles(path, "msbuild.exe", SearchOption.AllDirectories))
.Select(Path.GetDirectoryName);
Two overloads exist:
MSBuildLocator.RegisterMSBuildPath(string)
MSBuildLocator.RegisterMSBuildPath(string[])
So you could either .ToArray()
this or .First()
it or .Last()
it etc.
I've been trying to load a csproj using new
Microsoft.Build.Evaluation.Project("project.csproj", null, null");
and it throws this exception:The specified SDK "Microsoft.NET.Sdk.WindowsDesktop" could not be found. Source: Microsoft.Build Error: MSB4236 MSBuild.CouldNotResolveSdk
The loaded csproj looks like this:
And the host application registers the MSBuild location with:
I've noticed different behavior depending on whether the host application is compiled against Net472 or against NetCoreApp31:
On Net472
Microsoft.NET.Sdk.WindowsDesktop
projects succeeds to load.MSBuildPath
of Locator Instance points to: "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin"On NetCoreApp3.1
Microsoft.NET.Sdk.WindowsDesktop
projects throw an exception on load.MSBuildPath
of Locator instance points to: "C:\Program Files\dotnet\sdk\2.2.300\" On Net4xx:So I believe this is the reason because it works on Net4 and not on NetCore.
Is there a way to solve this issue and make the "Microsoft.NET.Sdk.WindowsDesktop" projects to load while the host app is NetCoreApp?