Skip to content

Commit

Permalink
Design time build fixes (#449)
Browse files Browse the repository at this point in the history
* Update Draco.ProjectSystem.csproj

* Restore added

* Minor fixes

* Update Draco.LanguageServer.csproj
  • Loading branch information
LPeter1997 authored Aug 28, 2024
1 parent 1133427 commit e42ac24
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 58 deletions.
7 changes: 6 additions & 1 deletion src/Draco.LanguageServer/Draco.LanguageServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@

<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="NuGet.Protocol" Version="6.8.0" />

<PackageReference Include="NuGet.Frameworks" Version="6.11.0" ExcludeAssets="runtime" />
<PackageReference Include="NuGet.Protocol" Version="6.11.0" ExlcudeAssets="runtime" />
<PackageReference Include="NuGet.Packaging" Version="6.11.0" ExcludeAssets="runtime" />
<PackageReference Include="NuGet.Common" Version="6.11.0" ExcludeAssets="runtime" />
<PackageReference Include="NuGet.Versioning" Version="6.11.0" ExcludeAssets="runtime" />
</ItemGroup>

</Project>
9 changes: 6 additions & 3 deletions src/Draco.LanguageServer/DracoLanguageServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,17 @@ private async Task CreateCompilation()
}

var project = projects[0];
var designTimeBuild = project.BuildDesignTime();
var buildResult = project.BuildDesignTime();

if (!designTimeBuild.Succeeded)
if (!buildResult.Success)
{
await this.client.LogMessageAsync(MessageType.Error, designTimeBuild.BuildLog);
await this.client.LogMessageAsync(MessageType.Error, buildResult.Log);
await this.client.ShowMessageAsync(MessageType.Error, "Design-time build failed! See logs for details.");
return;
}

var designTimeBuild = buildResult.Value;

var syntaxTrees = Directory
.GetFiles(rootPath, "*.draco", SearchOption.AllDirectories)
.Select(x => SyntaxTree.Parse(SourceText.FromFile(x)))
Expand Down
49 changes: 49 additions & 0 deletions src/Draco.ProjectSystem/BuildResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Diagnostics.CodeAnalysis;

namespace Draco.ProjectSystem;

/// <summary>
/// Factory for creating build results.
/// </summary>
internal static class BuildResult
{
/// <summary>
/// Creates a successful build result.
/// </summary>
/// <typeparam name="T">The result type of a successful build.</typeparam>
/// <param name="value">The result of the build.</param>
/// <param name="log">The log of the build.</param>
/// <returns>A successful build result.</returns>
public static BuildResult<T> Success<T>(T value, string? log = null) => new(true, value, log);

/// <summary>
/// Creates a failed build result.
/// </summary>
/// <typeparam name="T">The result type of a successful build.</typeparam>
/// <param name="log">The log of the build.</param>
/// <returns>A failed build result.</returns>
public static BuildResult<T> Failure<T>(string? log = null) => new(false, default, log);
}

/// <summary>
/// The result of a build.
/// </summary>
/// <typeparam name="T">The result type of a successful build.</typeparam>
public readonly struct BuildResult<T>(bool success, T? value, string? log = null)
{
/// <summary>
/// True, if the build succeeded.
/// </summary>
[MemberNotNullWhen(true, nameof(Value))]
public bool Success { get; } = success;

/// <summary>
/// The result of the build.
/// </summary>
public T? Value { get; } = value;

/// <summary>
/// The log of the build.
/// </summary>
public string Log { get; } = log ?? string.Empty;
}
16 changes: 1 addition & 15 deletions src/Draco.ProjectSystem/DesignTimeBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ public readonly struct DesignTimeBuild
/// </summary>string
public Project Project { get; }

/// <summary>
/// True, if the build succeeded.
/// </summary>
public bool Succeeded { get; }

/// <summary>
/// The build log.
/// </summary>
public string BuildLog { get; }

/// <summary>
/// The reference paths of the project.
/// </summary>
Expand All @@ -40,13 +30,9 @@ public readonly struct DesignTimeBuild

internal DesignTimeBuild(
Project project,
MSBuildProjectInstance projectInstance,
bool success,
string buildLog)
MSBuildProjectInstance projectInstance)
{
this.Project = project;
this.ProjectInstance = projectInstance;
this.Succeeded = success;
this.BuildLog = buildLog;
}
}
10 changes: 5 additions & 5 deletions src/Draco.ProjectSystem/Draco.ProjectSystem.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<ItemGroup>
<PackageReference Include="Microsoft.Build.Locator" Version="1.7.8" />
<PackageReference Include="Microsoft.Build" Version="17.10.4" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Framework" Version="17.10.4" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="17.10.4" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.10.4" ExcludeAssets="runtime" />
<PackageReference Include="NuGet.Frameworks" Version="6.10.1" ExcludeAssets="runtime" />
<PackageReference Include="NuGet.Packaging" Version="6.10.1" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Framework" Version="17.11.4" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="17.11.4" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.11.4" ExcludeAssets="runtime" />
<PackageReference Include="NuGet.Frameworks" Version="6.11.0" ExcludeAssets="runtime" />
<PackageReference Include="NuGet.Packaging" Version="6.11.0" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
98 changes: 64 additions & 34 deletions src/Draco.ProjectSystem/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using MSBuildProject = Microsoft.Build.Evaluation.Project;
using MSBuildProjectInstance = Microsoft.Build.Execution.ProjectInstance;

Expand Down Expand Up @@ -43,12 +44,7 @@ public string? TargetFramework
/// <summary>
/// Project for reading out the target framework(s).
/// </summary>
internal MSBuildProject TfmProject => this.tfmProject ??= new(
projectFile: this.ProjectFile.FullName,
projectCollection: this.Workspace.ProjectCollection,
globalProperties: GetGlobalProperties(),
toolsVersion: null,
loadSettings: ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.IgnoreInvalidImports);
internal MSBuildProject TfmProject => this.tfmProject ??= this.CreateTfmProject();
private MSBuildProject? tfmProject;

/// <summary>
Expand All @@ -61,28 +57,7 @@ public string? TargetFramework
/// <summary>
/// Project for design-time builds.
/// </summary>
internal MSBuildProject BuildProject
{
get
{
if (this.buildProject is null)
{
var globalProps = GetGlobalProperties();
var targetFramework = this.TargetFramework;
if (targetFramework is not null)
{
globalProps.Add("TargetFramework", targetFramework);
}
this.buildProject = new(
projectFile: this.ProjectFile.FullName,
projectCollection: this.Workspace.ProjectCollection,
globalProperties: globalProps,
toolsVersion: null,
loadSettings: ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.IgnoreInvalidImports);
}
return this.buildProject;
}
}
internal MSBuildProject BuildProject => this.buildProject ??= this.CreateBuildProject();
private MSBuildProject? buildProject;

internal Project(FileInfo projectFile, Workspace workspace)
Expand All @@ -91,20 +66,72 @@ internal Project(FileInfo projectFile, Workspace workspace)
this.Workspace = workspace;
}

/// <summary>
/// Performs a restore of the project.
/// </summary>
/// <returns>The result of the restore.</returns>
public BuildResult<Unit> Restore()
{
var log = new StringWriter();
var logger = new StringWriterLogger(log);

var buildTargets = GetRestoreBuildTargets();
var projectInstance = this.BuildProject.CreateProjectInstance();
var success = projectInstance.Build(buildTargets, [logger]);

return success
? BuildResult.Success(Unit.Default, log.ToString())
: BuildResult.Failure<Unit>(log.ToString());
}

/// <summary>
/// Performs a design-time build of the project.
/// </summary>
/// <returns>The result of the build.</returns>
public DesignTimeBuild BuildDesignTime()
public BuildResult<DesignTimeBuild> BuildDesignTime()
{
var stringLog = new StringWriter();
var stringLogger = new StringWriterLogger(stringLog);
// First restore the project
var restoreResult = this.Restore();
if (!restoreResult.Success) return BuildResult.Failure<DesignTimeBuild>(restoreResult.Log);

// NOTE: I have no idea why, but this is needed
// I suppose it's because a restore introduces files since the last run, something that the API doesn't handle?
// Mark the project as dirty
this.BuildProject.MarkDirty();

var log = new StringWriter();
var logger = new StringWriterLogger(log);

var projectInstance = this.BuildProject.CreateProjectInstance();
var buildTargets = GetDesignTimeBuildTargets();
var success = projectInstance.Build(buildTargets, [stringLogger]);
var projectInstance = this.BuildProject.CreateProjectInstance();
var success = projectInstance.Build(buildTargets, [logger]);

return new(this, projectInstance, success, stringLog.ToString());
return success
? BuildResult.Success(new DesignTimeBuild(this, projectInstance), log.ToString())
: BuildResult.Failure<DesignTimeBuild>(log.ToString());
}

private MSBuildProject CreateTfmProject() => new(
projectFile: this.ProjectFile.FullName,
projectCollection: this.Workspace.ProjectCollection,
globalProperties: GetGlobalProperties(),
toolsVersion: null,
loadSettings: ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.IgnoreInvalidImports);

private MSBuildProject CreateBuildProject()
{
var globalProps = GetGlobalProperties();
var targetFramework = this.TargetFramework;
if (targetFramework is not null)
{
globalProps.Add("TargetFramework", targetFramework);
}
return new MSBuildProject(
projectFile: this.ProjectFile.FullName,
projectCollection: this.Workspace.ProjectCollection,
globalProperties: globalProps,
toolsVersion: null,
loadSettings: ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.IgnoreInvalidImports);
}

private static IDictionary<string, string> GetGlobalProperties() => new Dictionary<string, string>
Expand All @@ -122,6 +149,9 @@ public DesignTimeBuild BuildDesignTime()
["DotnetProjInfo"] = "true",
};

private static string[] GetRestoreBuildTargets() => [
"Restore"];

private static string[] GetDesignTimeBuildTargets() => [
"ResolveAssemblyReferencesDesignTime",
"ResolveProjectReferencesDesignTime",
Expand Down
12 changes: 12 additions & 0 deletions src/Draco.ProjectSystem/Unit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Draco.ProjectSystem;

/// <summary>
/// A unit type representing the absence of a value.
/// </summary>
public readonly record struct Unit
{
/// <summary>
/// A default instance of the unit type.
/// </summary>
public static readonly Unit Default = default;
}

0 comments on commit e42ac24

Please sign in to comment.