Skip to content

Commit 436bc57

Browse files
authored
Merge pull request #8885 from drewnoakes/log-projects-not-producing-ref-assembly
Log details of referenced projects not producing a reference assembly
2 parents 55987b3 + 653a2f1 commit 436bc57

21 files changed

+107
-87
lines changed

docs/build-acceleration.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,15 @@ Looking through the build output with the following points in mind:
9898

9999
> Build acceleration data is unavailable for project with target 'C:\Solution\Project\bin\Debug\Project.dll'.
100100
101-
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.
101+
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.
102102

103103
- ⛔ If you see:
104104

105-
> 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'.
105+
> 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>'.
106106
107107
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).
108108

109-
- 🗒️ TODO Add validation and output message when reference assemblies are not enabled (https://github.com/dotnet/project-system/issues/8798)
109+
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.
110110

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

src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UpToDate/BuildUpToDateCheck.FileSystemOperationAggregator.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,26 @@ internal sealed class FileSystemOperationAggregator
5858
public bool? IsAccelerationEnabled { get; internal set; }
5959

6060
/// <summary>
61-
/// Gets whether all referenced projects produce reference assemblies or not.
61+
/// Gets paths to the target of any projects that do not produce a reference assembly, or
62+
/// <see langword="null"/> if none exist.
6263
/// </summary>
6364
/// <remarks>
6465
/// Build acceleration works best when all referenced projects produce reference assemblies.
65-
/// This flag allows us to prompt the user when they have enabled build acceleration, but
66+
/// This collecting allows us to prompt the user when they have enabled build acceleration, but
6667
/// they are referencing projects that do not produce reference assemblies.
6768
/// </remarks>
68-
public bool AllReferencesProduceReferenceAssemblies { get; internal set; } = true;
69+
public IReadOnlyList<string>? TargetsWithoutReferenceAssemblies => _targetsWithoutReferenceAssemblies;
70+
71+
private List<string>? _targetsWithoutReferenceAssemblies;
72+
73+
internal void AddTargetsWithoutReferenceAssemblies(IReadOnlyList<string> targetsWithoutReferenceAssemblies)
74+
{
75+
if (targetsWithoutReferenceAssemblies is { Count: > 0 })
76+
{
77+
_targetsWithoutReferenceAssemblies ??= new();
78+
_targetsWithoutReferenceAssemblies.AddRange(targetsWithoutReferenceAssemblies);
79+
}
80+
}
6981

7082
public BuildAccelerationResult AccelerationResult
7183
{
@@ -197,7 +209,7 @@ private sealed class ConfiguredFileSystemOperationAggregator
197209
private readonly FileSystemOperationAggregator _parent;
198210
private readonly bool? _isBuildAccelerationEnabled;
199211

200-
public ConfiguredFileSystemOperationAggregator(FileSystemOperationAggregator parent, bool? isBuildAccelerationEnabled, bool referencesProduceReferenceAssemblies)
212+
public ConfiguredFileSystemOperationAggregator(FileSystemOperationAggregator parent, bool? isBuildAccelerationEnabled, IReadOnlyList<string>? targetsWithoutReferenceAssemblies)
201213
{
202214
_parent = parent;
203215
_isBuildAccelerationEnabled = isBuildAccelerationEnabled;
@@ -213,9 +225,9 @@ public ConfiguredFileSystemOperationAggregator(FileSystemOperationAggregator par
213225
_parent.IsAccelerationEnabled = false;
214226
}
215227

216-
if (referencesProduceReferenceAssemblies is false)
228+
if (targetsWithoutReferenceAssemblies is not null)
217229
{
218-
_parent.AllReferencesProduceReferenceAssemblies = false;
230+
_parent.AddTargetsWithoutReferenceAssemblies(targetsWithoutReferenceAssemblies);
219231
}
220232
}
221233

src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UpToDate/BuildUpToDateCheck.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,12 +1054,11 @@ private async Task<bool> IsUpToDateInternalAsync(
10541054

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

1060-
bool? isBuildAccelerationEnabled = IsBuildAccelerationEnabled(isCopyItemsComplete, implicitState);
1059+
bool? isBuildAccelerationEnabled = IsBuildAccelerationEnabled(copyInfo.IsComplete, implicitState);
10611060

1062-
var configuredFileSystemOperations = new ConfiguredFileSystemOperationAggregator(fileSystemOperations, isBuildAccelerationEnabled, allReferencesProduceReferenceAssemblies);
1061+
var configuredFileSystemOperations = new ConfiguredFileSystemOperationAggregator(fileSystemOperations, isBuildAccelerationEnabled, copyInfo.TargetsWithoutReferenceAssemblies);
10631062

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

@@ -1069,7 +1068,7 @@ private async Task<bool> IsUpToDateInternalAsync(
10691068
!CheckInputsAndOutputs(logger, lastSuccessfulBuildStartTimeUtc, timestampCache, implicitState, ignoreKinds, token) ||
10701069
!CheckBuiltFromInputFiles(logger, timestampCache, implicitState, token) ||
10711070
!CheckMarkers(logger, timestampCache, implicitState, isBuildAccelerationEnabled, fileSystemOperations) ||
1072-
!CheckCopyToOutputDirectoryItems(logger, implicitState, copyItemsByProject, configuredFileSystemOperations, isBuildAccelerationEnabled, token))
1071+
!CheckCopyToOutputDirectoryItems(logger, implicitState, copyInfo.ItemsByProject, configuredFileSystemOperations, isBuildAccelerationEnabled, token))
10731072
{
10741073
return (false, checkedConfigurations);
10751074
}
@@ -1114,12 +1113,12 @@ private async Task<bool> IsUpToDateInternalAsync(
11141113
logger.Minimal(nameof(Resources.FUTD_AccelerationCandidate));
11151114
}
11161115

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

11251124
logger.Verbose(nameof(Resources.FUTD_Completed), sw.Elapsed.TotalMilliseconds);

src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UpToDate/CopyItemAggregator.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public void SetProjectData(ProjectCopyData projectCopyData)
2020
}
2121
}
2222

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

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

4545
List<ProjectCopyData>? contributingProjects = null;
4646

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

7374
foreach (string referencedProjectTargetPath in data.ReferencedProjectTargetPaths)
@@ -83,7 +84,7 @@ public void SetProjectData(ProjectCopyData projectCopyData)
8384
}
8485
}
8586

86-
return (GenerateCopyItems(), isComplete, allReferencesProduceReferenceAssemblies);
87+
return new(GenerateCopyItems(), isComplete, referencesNotProducingReferenceAssembly);
8788

8889
IEnumerable<(string Path, ImmutableArray<CopyItem> CopyItems)> GenerateCopyItems()
8990
{

src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UpToDate/ICopyItemAggregator.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,25 @@ internal interface ICopyItemAggregator
2525
/// </remarks>
2626
/// <param name="targetPath">The target path of the project to query from.</param>
2727
/// <param name="logger">An object for writing log messages.</param>
28-
/// <returns>
29-
/// A tuple comprising:
30-
/// <list type="number">
31-
/// <item><c>Items</c> a sequence of items by project, that are reachable from the current project.</item>
32-
/// <item><c>IsComplete</c> indicating whether we have items from all reachable projects.</item>
33-
/// <item><c>AllReferencesProduceReferenceAssemblies</c> indicating whether all referenced projects produce reference assemblies.</item>
34-
/// </list>
35-
/// </returns>
36-
(IEnumerable<(string Path, ImmutableArray<CopyItem> CopyItems)> ItemsByProject, bool IsComplete, bool AllReferencesProduceReferenceAssemblies) TryGatherCopyItemsForProject(string targetPath, BuildUpToDateCheck.Log logger);
28+
/// <returns>A structure containing the results of the operation.</returns>
29+
CopyItemsResult TryGatherCopyItemsForProject(string targetPath, BuildUpToDateCheck.Log logger);
3730
}
3831

32+
/// <summary>
33+
/// Results of gathering the items that must be copied as part of a project's build
34+
/// by <see cref="ICopyItemAggregator.TryGatherCopyItemsForProject(string, BuildUpToDateCheck.Log)"/>.
35+
/// </summary>
36+
/// <param name="ItemsByProject">A sequence of items by project, that are reachable from the current project</param>
37+
/// <param name="IsComplete">Indicates whether we have items from all reachable projects.</param>
38+
/// <param name="TargetsWithoutReferenceAssemblies">
39+
/// A list of target paths for projects that do not produce reference assemblies, or <see langword="null"/> if
40+
/// all reachable projects do in fact produce reference assemblies.
41+
/// </param>
42+
internal record struct CopyItemsResult(
43+
IEnumerable<(string Path, ImmutableArray<CopyItem> CopyItems)> ItemsByProject,
44+
bool IsComplete,
45+
IReadOnlyList<string>? TargetsWithoutReferenceAssemblies);
46+
3947
/// <summary>
4048
/// Models the set of copy items a project produces, along with some details about the project.
4149
/// </summary>

src/Microsoft.VisualStudio.ProjectSystem.Managed/Resources.Designer.cs

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.VisualStudio.ProjectSystem.Managed/Resources.resx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ This project was loaded using the wrong project type, likely as a result of rena
449449
<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>
450450
<comment>Do not translate 'AccelerateBuildsInVisualStudio' or 'true'.</comment>
451451
</data>
452-
<data name="FUTD_NotAllReferencesProduceReferenceAssemblies" xml:space="preserve">
453-
<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>
452+
<data name="FUTD_NotAllReferencesProduceReferenceAssemblies_1" xml:space="preserve">
453+
<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>
454454
<comment>Do not translate 'ProduceReferenceAssembly' or 'true'.</comment>
455455
</data>
456456
<data name="FUTD_AccelerationDisabledCopyItemsIncomplete" xml:space="preserve">

src/Microsoft.VisualStudio.ProjectSystem.Managed/xlf/Resources.cs.xlf

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.VisualStudio.ProjectSystem.Managed/xlf/Resources.de.xlf

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)