Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/Build.Abstractions/BuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ public BuildContext(ICakeContext context)
/// </summary>
public MinVerVersion? Version { get; set; }

/// <summary>
/// Gets or sets whether the solution was compiled in this run.
/// When true, later test, pack, and publish steps can reuse outputs
/// without rebuilding project references.
/// </summary>
public bool SolutionBuilt { get; set; }

static (DirectoryPath, FilePath) GetRootPath(ICakeContext context, DirectoryPath path)
{
// Try and find the .git directory.
Expand Down
128 changes: 65 additions & 63 deletions apps/Build.Abstractions/BuildHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ namespace Build
using Cake.Common.IO;
using Cake.Common.Tools.DotNet;
using Cake.Common.Tools.DotNet.Build;
using Cake.Common.Tools.DotNet.MSBuild;
using Cake.Common.Tools.DotNet.Publish;
using Cake.Common.Tools.DotNet.Test;
using Cake.Common.Tools.MSBuild;
using Cake.Common.Tools.NuGet;
using Cake.Common.Tools.NuGet.Restore;
using Cake.Common.Tools.VSTest;
using Cake.Common.Xml;
using Cake.Core;
using Cake.Core.IO;
Expand All @@ -38,35 +37,44 @@ public static class BuildHelpers
};

/// <summary>
/// Builds the target project.
/// Builds or publishes the target project.
/// When <paramref name="publish"/> is set and the project has already been
/// built (for example by a solution-level compile), publish reuses those
/// outputs instead of rebuilding project references.
/// </summary>
/// <param name="context">The build context.</param>
/// <param name="project">The project to build.</param>
/// <param name="publish">States whether to publish the output.</param>
/// <param name="outputPath">The output path for publishing.</param>
/// <param name="noRestore">Skip restore when outputs are already restored.</param>
/// <param name="noDependencies">Skip building project references.</param>
public static void BuildProject(
this BuildContext context,
BuildProject project,
bool publish = false,
DirectoryPath? outputPath = default)
DirectoryPath? outputPath = default,
bool noRestore = false,
bool noDependencies = false)
{
context = context ?? throw new ArgumentNullException(nameof(context));
project = project ?? throw new ArgumentNullException(nameof(project));

bool reuseOutputs = project.HasBuilt || noDependencies;

if (project.BuildEngine == BuildEngine.MSBuild)
{
// MA - Force a NuGet restore
context.NuGetRestore(project.ProjectFilePath, new NuGetRestoreSettings());
if (!noRestore)
{
context.NuGetRestore(project.ProjectFilePath, new NuGetRestoreSettings());
}

var settings = new MSBuildSettings()
{
Configuration = context.Configuration,
MaxCpuCount = 0,
ArgumentCustomization = args =>
{
if (context.Version is not null)
{
args.Append($"/p:SemVer={context.Version}");
}
SolutionHelpers.ApplyCommonProperties(args, context, noDependencies || reuseOutputs);

if (publish && outputPath is not null)
{
Expand All @@ -80,11 +88,6 @@ public static void BuildProject(
}
}

if (context.SolutionPath is not null)
{
args.Append($"/p:SolutionDir={context.SolutionPath.FullPath}");
}

return args;
}
};
Expand All @@ -96,60 +99,59 @@ public static void BuildProject(

context.MSBuild(project.ProjectFilePath.FullPath, settings);
}
else
else if (publish && outputPath is not null)
{
foreach (string targetFramework in project.TargetFrameworks)
{
if (publish && outputPath is not null)
{
var settings = new DotNetPublishSettings()
{
Configuration = context.Configuration,
ArgumentCustomization = args =>
{
if (context.Version is not null)
{
args.Append($"/p:SemVer={context.Version}");
}

if (context.SolutionPath is not null)
{
args.Append($"/p:SolutionDir={context.SolutionPath.FullPath}");
}

return args;
},
Framework = targetFramework
};

context.DotNetPublish(project.ProjectFilePath.FullPath, settings);
}
else
var settings = new DotNetPublishSettings()
{
var settings = new DotNetBuildSettings()
{
Configuration = context.Configuration,
ArgumentCustomization = args =>
{
if (context.Version is not null)
{
args.Append($"/p:SemVer={context.Version}");
}

if (context.SolutionPath is not null)
{
args.Append($"/p:SolutionDir={context.SolutionPath.FullPath}");
}

return args;
},
Framework = targetFramework
};

context.DotNetBuild(project.ProjectFilePath.FullPath, settings);
}
Configuration = context.Configuration,
Framework = targetFramework,
OutputDirectory = outputPath,
NoBuild = reuseOutputs,
NoRestore = noRestore || reuseOutputs,
NoDependencies = noDependencies || reuseOutputs,
ArgumentCustomization = args => SolutionHelpers.ApplyCommonProperties(args, context, noDependencies || reuseOutputs)
};

context.DotNetPublish(project.ProjectFilePath.FullPath, settings);
}
}
else
{
var settings = new DotNetBuildSettings()
{
Configuration = context.Configuration,
NoRestore = noRestore,
NoDependencies = noDependencies,
MSBuildSettings = new DotNetMSBuildSettings
{
MaxCpuCount = 0
},
ArgumentCustomization = args => SolutionHelpers.ApplyCommonProperties(args, context, noDependencies)
};

context.DotNetBuild(project.ProjectFilePath.FullPath, settings);
}
}

/// <summary>
/// Gets discovered projects in the conventional build order:
/// libraries, applications, tests, data, then content.
/// </summary>
/// <param name="context">The build context.</param>
/// <returns>The ordered project list.</returns>
public static IReadOnlyList<BuildProject> GetProjectsInBuildOrder(this BuildContext context)
{
context = context ?? throw new ArgumentNullException(nameof(context));

var projects = new List<BuildProject>();
projects.AddRange(context.Projects[BuildType.Library]);
projects.AddRange(context.Projects[BuildType.Application]);
projects.AddRange(context.Projects[BuildType.Test]);
projects.AddRange(context.Projects[BuildType.Data]);
projects.AddRange(context.Projects[BuildType.Content]);
return projects;
}

/// <summary>
Expand Down
18 changes: 10 additions & 8 deletions apps/Build.Abstractions/BuildServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace Build
{
using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.Extensions.DependencyInjection;

Expand All @@ -13,26 +15,26 @@ namespace Build
/// </summary>
public class BuildServices
{
readonly IServiceScopeFactory _serviceScopeFactory;
readonly IServiceProvider _services;

/// <summary>
/// Initialises a new instance of <see cref="BuildServices"/>
/// </summary>
/// <param name="hooks">The service scope factory.</param>
public BuildServices(IServiceScopeFactory serviceScopeFactory)
/// <param name="services">The root service provider.</param>
public BuildServices(IServiceProvider services)
{
_serviceScopeFactory = serviceScopeFactory;
_services = services;
}

/// <summary>
/// Gets the avilable hooks.
/// Gets the available hooks of the requested type.
/// Hooks are discovered from assemblies that reference this build
/// system and registered as <see cref="ITaskHook"/> implementations.
/// </summary>
/// <typeparam name="THook">The hook type.</typeparam>
/// <returns>The set of hooks.</returns>
public IEnumerable<THook> GetHooks<THook>()
where THook : ITaskHook
{
yield break;
}
=> _services.GetServices<ITaskHook>().OfType<THook>();
}
}
17 changes: 13 additions & 4 deletions apps/Build.Abstractions/Hooks/IBuildHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,23 @@ namespace Build

/// <summary>
/// Defines the required contract for implementing a build hook.
/// BeforeBuild runs for every project before the solution-level compile so
/// extensions can generate sources. AfterBuild runs after compile outputs
/// exist (including leftover projects not in the solution).
/// </summary>
public interface IBuildHook : ITaskHook
{
/// <summary>
/// Executes before the build starts.
/// Executes before the build starts for the given project.
/// Invoked before the solution-level compile.
/// </summary>
/// <param name="context">The build context.</param>
/// <param name="project">The build project.</param>
void BeforeBuild(BuildContext context, BuildProject project);

/// <summary>
/// Executes after the build completes.
/// Executes after the build completes for the given project.
/// Invoked after the solution-level compile and any leftover project builds.
/// </summary>
/// <param name="context">The build context.</param>
/// <param name="project">The build project.</param>
Expand All @@ -28,18 +33,22 @@ public interface IBuildHook : ITaskHook

/// <summary>
/// Defines the required contract for implementing an asynchronous build hook.
/// Hook phasing matches <see cref="IBuildHook"/>: all BeforeBuild callbacks
/// run before compile, all AfterBuild callbacks run after outputs exist.
/// </summary>
public interface IAsyncBuildHook : ITaskHook
{
/// <summary>
/// Executes before the build starts.
/// Executes before the build starts for the given project.
/// Invoked before the solution-level compile.
/// </summary>
/// <param name="context">The build context.</param>
/// <param name="project">The build project.</param>
Task BeforeBuildAsync(BuildContext context, BuildProject project);

/// <summary>
/// Executes after the build completes.
/// Executes after the build completes for the given project.
/// Invoked after the solution-level compile and any leftover project builds.
/// </summary>
/// <param name="context">The build context.</param>
/// <param name="project">The build project.</param>
Expand Down
3 changes: 2 additions & 1 deletion apps/Build.Abstractions/PackHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public static void PackProject(
},
Configuration = context.Configuration,
OutputDirectory = context.ArtefactsPath,
NoBuild = project.HasBuilt
NoBuild = project.HasBuilt,
NoRestore = project.HasBuilt
});
}
}
Expand Down
Loading