Skip to content
Merged
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
6 changes: 3 additions & 3 deletions docs/build-acceleration.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,15 @@ Looking through the build output with the following points in mind:

> Build acceleration data is unavailable for project with target 'C:\Solution\Project\bin\Debug\Project.dll'.

Then any project that references the indicated project (directy or transitively) cannot be accelerated. This can happen if the mentioned project uses the legacy `.csproj` format, or for any other project system within Visual Studio that doesn't support build acceleration. Currently only .NET SDK-style projects (loaded with the project system from this GitHub repository) provide the needed data.
Then any project that references the indicated project (directly or transitively) cannot be accelerated. This can happen if the mentioned project uses the legacy `.csproj` format, or for any other project system within Visual Studio that doesn't support build acceleration. Currently only .NET SDK-style projects (loaded with the project system from this GitHub repository) provide the needed data.

- ⛔ If you see:

> This project has enabled build acceleration, but at least one referenced project does not produce reference assemblies. Ensure all referenced projects, both direct and indirect, have the 'ProduceReferenceAssembly' MSBuild property set to 'true'.
> This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': '<path1>', '<path2>'.

Then build acceleration will not know whether it is safe to copy a modified output DLL from a referenced project or not. We rely on the use of reference assemblies to convey this information. To address this, ensure all referenced projects have the `ProduceReferenceAssembly` property set to `true`. You may like to add this to your `Directory.Build.props` file alongside the `AccelerateBuildsInVisualStudio` property. Note that projects targeting `net5.0` or later produce reference assemblies by default. Projects that target .NET Standard may require this to be specified manually (see https://github.com/dotnet/project-system/issues/8865).

- 🗒️ TODO Add validation and output message when reference assemblies are not enabled (https://github.com/dotnet/project-system/issues/8798)
This message lists the referenced projects that are not producing a reference assembly. The `TargetPath` of those projects is used, as this can help disambiguate between target frameworks in multi-targeting projects.

- ✅ You should see a section listing items to copy:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,26 @@ internal sealed class FileSystemOperationAggregator
public bool? IsAccelerationEnabled { get; internal set; }

/// <summary>
/// Gets whether all referenced projects produce reference assemblies or not.
/// Gets paths to the target of any projects that do not produce a reference assembly, or
/// <see langword="null"/> if none exist.
/// </summary>
/// <remarks>
/// Build acceleration works best when all referenced projects produce reference assemblies.
/// This flag allows us to prompt the user when they have enabled build acceleration, but
/// This collecting allows us to prompt the user when they have enabled build acceleration, but
/// they are referencing projects that do not produce reference assemblies.
/// </remarks>
public bool AllReferencesProduceReferenceAssemblies { get; internal set; } = true;
public IReadOnlyList<string>? TargetsWithoutReferenceAssemblies => _targetsWithoutReferenceAssemblies;

private List<string>? _targetsWithoutReferenceAssemblies;

internal void AddTargetsWithoutReferenceAssemblies(IReadOnlyList<string> targetsWithoutReferenceAssemblies)
{
if (targetsWithoutReferenceAssemblies is { Count: > 0 })
{
_targetsWithoutReferenceAssemblies ??= new();
_targetsWithoutReferenceAssemblies.AddRange(targetsWithoutReferenceAssemblies);
}
}

public BuildAccelerationResult AccelerationResult
{
Expand Down Expand Up @@ -197,7 +209,7 @@ private sealed class ConfiguredFileSystemOperationAggregator
private readonly FileSystemOperationAggregator _parent;
private readonly bool? _isBuildAccelerationEnabled;

public ConfiguredFileSystemOperationAggregator(FileSystemOperationAggregator parent, bool? isBuildAccelerationEnabled, bool referencesProduceReferenceAssemblies)
public ConfiguredFileSystemOperationAggregator(FileSystemOperationAggregator parent, bool? isBuildAccelerationEnabled, IReadOnlyList<string>? targetsWithoutReferenceAssemblies)
{
_parent = parent;
_isBuildAccelerationEnabled = isBuildAccelerationEnabled;
Expand All @@ -213,9 +225,9 @@ public ConfiguredFileSystemOperationAggregator(FileSystemOperationAggregator par
_parent.IsAccelerationEnabled = false;
}

if (referencesProduceReferenceAssemblies is false)
if (targetsWithoutReferenceAssemblies is not null)
{
_parent.AllReferencesProduceReferenceAssemblies = false;
_parent.AddTargetsWithoutReferenceAssemblies(targetsWithoutReferenceAssemblies);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1054,12 +1054,11 @@ private async Task<bool> IsUpToDateInternalAsync(

// We may have an incomplete set of copy items.
// We check timestamps of whatever items we can find, but only perform acceleration when the full set is available.
(IEnumerable<(string Path, ImmutableArray<CopyItem> CopyItems)> copyItemsByProject, bool isCopyItemsComplete, bool allReferencesProduceReferenceAssemblies)
= _copyItemAggregator.TryGatherCopyItemsForProject(implicitState.ProjectTargetPath, logger);
CopyItemsResult copyInfo = _copyItemAggregator.TryGatherCopyItemsForProject(implicitState.ProjectTargetPath, logger);

bool? isBuildAccelerationEnabled = IsBuildAccelerationEnabled(isCopyItemsComplete, implicitState);
bool? isBuildAccelerationEnabled = IsBuildAccelerationEnabled(copyInfo.IsComplete, implicitState);

var configuredFileSystemOperations = new ConfiguredFileSystemOperationAggregator(fileSystemOperations, isBuildAccelerationEnabled, allReferencesProduceReferenceAssemblies);
var configuredFileSystemOperations = new ConfiguredFileSystemOperationAggregator(fileSystemOperations, isBuildAccelerationEnabled, copyInfo.TargetsWithoutReferenceAssemblies);

string outputFullPath = Path.Combine(implicitState.MSBuildProjectDirectory, implicitState.OutputRelativeOrFullPath);

Expand All @@ -1069,7 +1068,7 @@ private async Task<bool> IsUpToDateInternalAsync(
!CheckInputsAndOutputs(logger, lastSuccessfulBuildStartTimeUtc, timestampCache, implicitState, ignoreKinds, token) ||
!CheckBuiltFromInputFiles(logger, timestampCache, implicitState, token) ||
!CheckMarkers(logger, timestampCache, implicitState, isBuildAccelerationEnabled, fileSystemOperations) ||
!CheckCopyToOutputDirectoryItems(logger, implicitState, copyItemsByProject, configuredFileSystemOperations, isBuildAccelerationEnabled, token))
!CheckCopyToOutputDirectoryItems(logger, implicitState, copyInfo.ItemsByProject, configuredFileSystemOperations, isBuildAccelerationEnabled, token))
{
return (false, checkedConfigurations);
}
Expand Down Expand Up @@ -1114,12 +1113,12 @@ private async Task<bool> IsUpToDateInternalAsync(
logger.Minimal(nameof(Resources.FUTD_AccelerationCandidate));
}

if (fileSystemOperations.IsAccelerationEnabled is true && fileSystemOperations.AllReferencesProduceReferenceAssemblies is false)
if (fileSystemOperations.IsAccelerationEnabled is true && fileSystemOperations.TargetsWithoutReferenceAssemblies is { Count: > 0 })
{
// This project is configured to use build acceleration, but some of its references do not
// produce reference assemblies. Log a message to let the user know that they may be able
// to improve their build performance by enabling the production of reference assemblies.
logger.Minimal(nameof(Resources.FUTD_NotAllReferencesProduceReferenceAssemblies));
logger.Minimal(nameof(Resources.FUTD_NotAllReferencesProduceReferenceAssemblies_1), string.Join(", ", fileSystemOperations.TargetsWithoutReferenceAssemblies.Select(s => $"'{s}'")));
}

logger.Verbose(nameof(Resources.FUTD_Completed), sw.Elapsed.TotalMilliseconds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void SetProjectData(ProjectCopyData projectCopyData)
}
}

public (IEnumerable<(string Path, ImmutableArray<CopyItem> CopyItems)> ItemsByProject, bool IsComplete, bool AllReferencesProduceReferenceAssemblies) TryGatherCopyItemsForProject(string targetPath, BuildUpToDateCheck.Log logger)
public CopyItemsResult TryGatherCopyItemsForProject(string targetPath, BuildUpToDateCheck.Log logger)
{
// Keep track of all projects we've visited to avoid infinite recursion or duplicated results.
HashSet<string> explored = new(StringComparers.Paths);
Expand All @@ -38,9 +38,9 @@ public void SetProjectData(ProjectCopyData projectCopyData)
// results is strictly an improvement over ignoring what results we do have.
bool isComplete = true;

// Whether all referenced projects have ProduceReferenceAssembly set to true. The originating project
// is not included in this check (targetPath).
bool allReferencesProduceReferenceAssemblies = true;
// Lazily populated list of referenced projects not having ProduceReferenceAssembly set to true.
// The originating project is not included in this check (targetPath).
List<string>? referencesNotProducingReferenceAssembly = null;

List<ProjectCopyData>? contributingProjects = null;

Expand All @@ -67,7 +67,8 @@ public void SetProjectData(ProjectCopyData projectCopyData)
if (!data.ProduceReferenceAssembly && project != targetPath)
{
// One of the referenced projects does not produce a reference assembly.
allReferencesProduceReferenceAssemblies = false;
referencesNotProducingReferenceAssembly ??= new();
referencesNotProducingReferenceAssembly.Add(data.TargetPath);
}

foreach (string referencedProjectTargetPath in data.ReferencedProjectTargetPaths)
Expand All @@ -83,7 +84,7 @@ public void SetProjectData(ProjectCopyData projectCopyData)
}
}

return (GenerateCopyItems(), isComplete, allReferencesProduceReferenceAssemblies);
return new(GenerateCopyItems(), isComplete, referencesNotProducingReferenceAssembly);

IEnumerable<(string Path, ImmutableArray<CopyItem> CopyItems)> GenerateCopyItems()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,25 @@ internal interface ICopyItemAggregator
/// </remarks>
/// <param name="targetPath">The target path of the project to query from.</param>
/// <param name="logger">An object for writing log messages.</param>
/// <returns>
/// A tuple comprising:
/// <list type="number">
/// <item><c>Items</c> a sequence of items by project, that are reachable from the current project.</item>
/// <item><c>IsComplete</c> indicating whether we have items from all reachable projects.</item>
/// <item><c>AllReferencesProduceReferenceAssemblies</c> indicating whether all referenced projects produce reference assemblies.</item>
/// </list>
/// </returns>
(IEnumerable<(string Path, ImmutableArray<CopyItem> CopyItems)> ItemsByProject, bool IsComplete, bool AllReferencesProduceReferenceAssemblies) TryGatherCopyItemsForProject(string targetPath, BuildUpToDateCheck.Log logger);
/// <returns>A structure containing the results of the operation.</returns>
CopyItemsResult TryGatherCopyItemsForProject(string targetPath, BuildUpToDateCheck.Log logger);
}

/// <summary>
/// Results of gathering the items that must be copied as part of a project's build
/// by <see cref="ICopyItemAggregator.TryGatherCopyItemsForProject(string, BuildUpToDateCheck.Log)"/>.
/// </summary>
/// <param name="ItemsByProject">A sequence of items by project, that are reachable from the current project</param>
/// <param name="IsComplete">Indicates whether we have items from all reachable projects.</param>
/// <param name="TargetsWithoutReferenceAssemblies">
/// A list of target paths for projects that do not produce reference assemblies, or <see langword="null"/> if
/// all reachable projects do in fact produce reference assemblies.
/// </param>
internal record struct CopyItemsResult(
IEnumerable<(string Path, ImmutableArray<CopyItem> CopyItems)> ItemsByProject,
bool IsComplete,
IReadOnlyList<string>? TargetsWithoutReferenceAssemblies);

/// <summary>
/// Models the set of copy items a project produces, along with some details about the project.
/// </summary>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,8 @@ This project was loaded using the wrong project type, likely as a result of rena
<value>This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration.</value>
<comment>Do not translate 'AccelerateBuildsInVisualStudio' or 'true'.</comment>
</data>
<data name="FUTD_NotAllReferencesProduceReferenceAssemblies" xml:space="preserve">
<value>This project has enabled build acceleration, but at least one referenced project does not produce reference assemblies. Ensure all referenced projects, both direct and indirect, have the 'ProduceReferenceAssembly' MSBuild property set to 'true'. See https://aka.ms/vs-build-acceleration.</value>
<data name="FUTD_NotAllReferencesProduceReferenceAssemblies_1" xml:space="preserve">
<value>This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': {0}. See https://aka.ms/vs-build-acceleration for more information.</value>
<comment>Do not translate 'ProduceReferenceAssembly' or 'true'.</comment>
</data>
<data name="FUTD_AccelerationDisabledCopyItemsIncomplete" xml:space="preserve">
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading