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
216 stars 83 forks source link

FEATURE_VISUALSTUDIOSETUP for .NET Core too #105

Closed jairbubbles closed 3 years ago

jairbubbles commented 3 years ago
ghost commented 3 years ago

CLA assistant check
Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.

:x: jairbubbles sign now
You have signed the CLA already but the status is still pending? Let us recheck it.

rainersigwald commented 3 years ago

This looks interesting, but I'm not sure it's the right direction. What motivated you to put this together?

My concern is that .NET Core MSBuild cannot run all the tasks and targets used by Visual Studio, and a .NET Core/.NET 5.0 application shouldn't load the full-.NET-Framework copy of MSBuild from a Visual Studio installation--it has functionality that isn't supported on .NET Core.

jairbubbles commented 3 years ago

Hi @rainersigwald!

We use the lib to inspect .csproj and gather properties (such as OutputPath) and/or items (such as the PackageReferences). In the past we used to do that by parsing the .xml but with the new toolset we now need MsBuild to evaluate the projects for us.

The main usage is to add external steps during CI (for instance to tag assemblies). We could do that directly in MsBuild targets but we prefer to rely on an external tool (for different reasons). One other scenario is a simple tool that display a graph of the dependencies of any .csproj.

We're migrating all our tools to .NET 5.0 and we still want those tools to be able to inspect .NET Framework projects. We can imagine that at some point we won't have anymore .NET Framework .csproj to inspect but it will take some time to achieve.

rainersigwald commented 3 years ago

We're migrating all our tools to .NET 5.0 and we still want those tools to be able to inspect .NET Framework projects.

Unfortunately that's not currently possible. If you want to be able to load any project that Visual Studio can open/MSBuild.exe can build, your application must target .NET 4.7.2 (or 4.8). MSBuild itself is in the same boat, because VS itself is a .NET Framework application.

jairbubbles commented 3 years ago

Makes sense, invoking MsBuild has always been a challenge so I'm not really surprised it's not working with .NET Core 😅

Could we imagine adding a new command to MsBuild.exe that would exports the project after evaluation?

I know about the .binlog but they are used when compiling and we want the evaluation to be as fast as possible. We don't need to execute targets.

rainersigwald commented 3 years ago

@jairbubbles what do you mean by "exports the project after evaluation"? Something like the current preprocessor, or do you want something like dotnet/msbuild#3911?

I'm going to close this PR, but we can keep talking.

jairbubbles commented 3 years ago

Hi @rainersigwald,

I don't know what you mean by "current processor" as for dotnet/msbuild#3911 it does look similar to what I have in mind even I though more of a "evaluate everyhting and dump the result" so I can process it afterwards.

To better explain my needs, I'll show you bits of our library (which sadly is not public).

A basic example would look like this:

 var project = new CsProject("<full_path>.csproj"); // Load a .csproj

 // Get info from all the possible outputs (handle multitargetting for instance)
 var outputs = project.GetAllOutputs("Release|AnyCpu");

// Get all referenced NuGet packages
var packageReferences = project.GetPackageReferences();

//...

It handles .vcxproj and .csproj, the common interface looks like this:

public interface IProject
{
        string ToolsetVersion { get; }

        OutputType OutputType { get; }

        IEnumerable<string> Configurations { get; }

        IEnumerable<string> Platforms { get; }

        ProjectOutput GetOutput(string configuration = "");

        ProjectOutput GetOutput(string configuration, string platform);

        IEnumerable<ProjectOutput> GetAllOutputs();

        IEnumerable<ProjectReference> GetProjectReferences();

        string GetProperty(string propertyName);

        string GetProperty(string propertyName, string configuration, string platform);

        IEnumerable<string> GetSourceFiles(string configuration = "", string platform = "");

        IEnumerable<FileItem> GetAllFileItems(string configuration = "", string platform = "");

        string FilePath { get; }
}

So basically it's a wrapper over MsBuild to provide a simple API for scripts writers.

jairbubbles commented 3 years ago

Ok I found the command line arguments (-preprocess). It should indeed allow us to do what we want:

Thx for the tip @rainersigwald!

jairbubbles commented 3 years ago

I tested quickly and sadly it didn't went as well as expected 😥

jairbubbles commented 3 years ago

Hi @KirillOsenkov , by any chance I was wondering if you think we could use binlog to output the info about a .csproj, and reads them from the binlog as if we were loading the project with MsBuild directly.

msbuild.exe /bl /t:clean <my_project.csproj> => open my_project.binlog in our lib => introspect the .csproj (as shown in a prevous comment)

Also, do you know an even more mimimalistic target than "Clean" that would preprocess properly the projects and set properties such as TargetPath, OutputPath and so on used by the "Build" target?

jairbubbles commented 3 years ago

Humm looks like I forgot about multitargetting when testing the preprocess commandline arguments.

By providing the framework -p:TargetFramework=net472 I'm able to get all the info I need. I'll need to test more but it's promising!

KirillOsenkov commented 3 years ago

@jairbubbles yes, you can read the .binlog programmatically via an API and extract property and item values etc.

See here: https://msbuildlog.com/#api

Properties and Items will be on the ProjectStartedBuildEventArgs.