Skip to content

Commit

Permalink
Adds experimental code to read MSBuild project files and evaluate its…
Browse files Browse the repository at this point in the history
… properties
  • Loading branch information
matzefriedrich committed Jun 5, 2024
1 parent 1d4384a commit d7c6185
Showing 1 changed file with 106 additions and 89 deletions.
195 changes: 106 additions & 89 deletions src/dotnet.nugit/Services/Workspace/ProjectWorkspaceManager.cs
Original file line number Diff line number Diff line change
@@ -1,96 +1,113 @@
namespace dotnet.nugit.Services.Workspace
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using dotnet.nugit.Abstractions;
using Microsoft.Build.Evaluation.Context;
using Microsoft.Build.Execution;
using Microsoft.Build.FileSystem;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.Extensions.Logging;

namespace dotnet.nugit.Services.Workspace;

internal sealed class Fs : MSBuildFileSystemBase
{
}

public class ProjectWorkspaceManager : IProjectWorkspaceManager
{
private readonly ILogger<ProjectWorkspaceManager> logger;
private readonly ILoggerFactory loggerFactory;
private readonly IEnumerable<IMsBuildToolPathLocator> msBuildLocators;
private readonly ProjectWorkspaceLogger msBuildLogger;

public ProjectWorkspaceManager(
IEnumerable<IMsBuildToolPathLocator> msBuildLocators,
ILoggerFactory loggerFactory,
ILogger<ProjectWorkspaceManager> logger)
{
this.msBuildLocators = msBuildLocators ?? throw new ArgumentNullException(nameof(msBuildLocators));
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));

var workspaceLogger = loggerFactory.CreateLogger<ProjectWorkspaceLogger>();
msBuildLogger = new ProjectWorkspaceLogger(workspaceLogger);
}

public string? LocateMsBuildToolsPath()
{
foreach (var toolPathLocator in msBuildLocators)
if (toolPathLocator.TryLocateMsBuildToolsPath(out var path))
return path;

return null;
}

public async Task<IProjectAccessor> LoadProjectAsync(string projectFile, string configurationName,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(projectFile))
throw new ArgumentException(Resources.Resources.ArgumentException_Value_cannot_be_null_or_whitespace,
nameof(projectFile));

var msBuildToolsPath = LocateMsBuildToolsPath();
if (string.IsNullOrWhiteSpace(msBuildToolsPath))
throw new InvalidOperationException("Cannot detect the .NET SDK");

// Environment.SetEnvironmentVariable("MsBuildToolsPath", msBuildToolsPath);

var workspace = MSBuildWorkspace.Create();
var project = await workspace.OpenProjectAsync(projectFile, msBuildLogger, new Progress<ProjectLoadProgress>(),
cancellationToken);
var compilation = await project.GetCompilationAsync(cancellationToken);
if (compilation == null) return ProjectAccessor.Empty;

/* using var stream = new MemoryStream();
compilation.Emit(stream, cancellationToken: cancellationToken); */

/* var m = new MSBuildProjectLoader(workspace);
var map = ProjectMap.Create();
ImmutableArray<ProjectInfo> info = await m.LoadProjectInfoAsync(projectFile, map, cancellationToken: cancellationToken); */

await using Stream input = File.OpenRead(projectFile);
using var reader = XmlReader.Create(input);
var p = new Microsoft.Build.Evaluation.Project(reader);
const ProjectInstanceSettings settings = ProjectInstanceSettings.Immutable;
MSBuildFileSystemBase fs = new Fs();
var projectInstance = p.CreateProjectInstance(settings, EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated, fs));

return new ProjectAccessor(project, projectInstance, configurationName);
}
}

public sealed class ProjectAccessor : IProjectAccessor
{
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Abstractions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.Extensions.Logging;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using Resources;

public class ProjectWorkspaceManager : IProjectWorkspaceManager
public static readonly IProjectAccessor Empty = new ProjectAccessor();

private readonly ProjectInstance? _projectPropertiesEvaluationInstance;
private readonly Project? project;
private readonly string? configurationName;

private ProjectAccessor()
{
}

public ProjectAccessor(
Project codeAnalysisProject,
ProjectInstance projectPropertiesEvaluationInstance,
string configurationName)
{
private readonly ILogger<ProjectWorkspaceManager> logger;
private readonly ILoggerFactory loggerFactory;
private readonly IEnumerable<IMsBuildToolPathLocator> msBuildLocators;
private readonly ProjectWorkspaceLogger msBuildLogger;

public ProjectWorkspaceManager(
IEnumerable<IMsBuildToolPathLocator> msBuildLocators,
ILoggerFactory loggerFactory,
ILogger<ProjectWorkspaceManager> logger)
{
this.msBuildLocators = msBuildLocators ?? throw new ArgumentNullException(nameof(msBuildLocators));
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));

ILogger<ProjectWorkspaceLogger> workspaceLogger = loggerFactory.CreateLogger<ProjectWorkspaceLogger>();
this.msBuildLogger = new ProjectWorkspaceLogger(workspaceLogger);
}

public string? LocateMsBuildToolsPath()
{
foreach (IMsBuildToolPathLocator toolPathLocator in this.msBuildLocators)
{
if (toolPathLocator.TryLocateMsBuildToolsPath(out string? path)) return path;
}

return null;
}

public async Task<IProjectAccessor> LoadProjectAsync(string projectFile, string configurationName, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(projectFile)) throw new ArgumentException(Resources.ArgumentException_Value_cannot_be_null_or_whitespace, nameof(projectFile));

string? msBuildToolsPath = this.LocateMsBuildToolsPath();
if (string.IsNullOrWhiteSpace(msBuildToolsPath))
throw new InvalidOperationException("Cannot detect the .NET SDK");

// Environment.SetEnvironmentVariable("MsBuildToolsPath", msBuildToolsPath);

var workspace = MSBuildWorkspace.Create();
Project project = await workspace.OpenProjectAsync(projectFile, this.msBuildLogger, new Progress<ProjectLoadProgress>(), cancellationToken);
Compilation? compilation = await project.GetCompilationAsync(cancellationToken);
if (compilation == null) return ProjectAccessor.Empty;

/* using var stream = new MemoryStream();
compilation.Emit(stream, cancellationToken: cancellationToken); */

/* var m = new MSBuildProjectLoader(workspace);
var map = ProjectMap.Create();
ImmutableArray<ProjectInfo> info = await m.LoadProjectInfoAsync(projectFile, map, cancellationToken: cancellationToken); */

return new ProjectAccessor(project, configurationName);
}
if (string.IsNullOrWhiteSpace(configurationName)) throw new ArgumentException(Resources.Resources.ArgumentException_Value_cannot_be_null_or_whitespace, nameof(configurationName));
this.project = codeAnalysisProject ?? throw new ArgumentNullException(nameof(codeAnalysisProject));
this._projectPropertiesEvaluationInstance = projectPropertiesEvaluationInstance ?? throw new ArgumentNullException(nameof(projectPropertiesEvaluationInstance));
this.configurationName = configurationName;
}

public sealed class ProjectAccessor : IProjectAccessor
public void DeriveNuspec()
{
public static readonly IProjectAccessor Empty = new ProjectAccessor();
private readonly string? configurationName;

private readonly Project? project;

private ProjectAccessor()
{
}

public ProjectAccessor(Project project, string configurationName)
{
if (string.IsNullOrWhiteSpace(configurationName)) throw new ArgumentException(Resources.ArgumentException_Value_cannot_be_null_or_whitespace, nameof(configurationName));
this.project = project ?? throw new ArgumentNullException(nameof(project));
this.configurationName = configurationName;
}

public void DeriveNuspec()
{

}
}
}

0 comments on commit d7c6185

Please sign in to comment.