Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
52 changes: 35 additions & 17 deletions src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal class DependencyContextBuilder
private string _referenceAssembliesPath;
private Dictionary<PackageIdentity, string> _filteredPackages;
private bool _includeMainProjectInDepsFile = true;
private bool _trimLibrariesWithoutAssets = true;
private readonly Dictionary<string, DependencyLibrary> _dependencyLibraries;
private readonly Dictionary<string, List<LibraryDependency>> _libraryDependencies;
private readonly List<string> _mainProjectDependencies;
Expand Down Expand Up @@ -212,6 +213,12 @@ public DependencyContextBuilder WithMainProjectInDepsFile(bool includeMainProjec
return this;
}

public DependencyContextBuilder WithTrimLibrariesWithoutAssets(bool trimLibrariesWithoutAssets)
{
_trimLibrariesWithoutAssets = trimLibrariesWithoutAssets;
return this;
}

public DependencyContextBuilder WithRuntimePackAssets(IEnumerable<RuntimePackAssetInfo> runtimePackAssets)
{
_runtimePackAssets = new Dictionary<string, List<RuntimePackAssetInfo>>();
Expand Down Expand Up @@ -356,31 +363,42 @@ public DependencyContext Build(string[] userRuntimeAssemblies = null)
}
}

var unprocessedLibraries = runtimeLibraries.ToHashSet();
while (unprocessedLibraries.Any())
if (_trimLibrariesWithoutAssets)
{
var lib = unprocessedLibraries.First();
unprocessedLibraries.Remove(lib);

if (lib.Library.RuntimeAssemblyGroups.Count == 0 && lib.Library.NativeLibraryGroups.Count == 0 && lib.Library.ResourceAssemblies.Count == 0)
var unprocessedLibraries = runtimeLibraries.ToHashSet();
while (unprocessedLibraries.Any())
{
if (lib.Library.Dependencies.All(d => !libraries.TryGetValue(d.Name, out var dependency) || dependency.Dependents.Count > 1))
var lib = unprocessedLibraries.First();
unprocessedLibraries.Remove(lib);

if (lib.Library.Name.Equals("xunit.core", StringComparison.OrdinalIgnoreCase))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check "xunit" as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I've fixed this.

{
runtimeLibraries.Remove(lib);
libraries.Remove(lib.Library.Name);
foreach (var dependency in lib.Library.Dependencies)
// Special case xunit.core, it should not be removed because the xUnit v2 runner looks for this library in the deps.json to
// identify test projects.
// See https://github.com/dotnet/sdk/issues/49248
continue;
}

if (lib.Library.RuntimeAssemblyGroups.Count == 0 && lib.Library.NativeLibraryGroups.Count == 0 && lib.Library.ResourceAssemblies.Count == 0)
{
if (lib.Library.Dependencies.All(d => !libraries.TryGetValue(d.Name, out var dependency) || dependency.Dependents.Count > 1))
{
if (libraries.TryGetValue(dependency.Name, out ModifiableRuntimeLibrary value))
runtimeLibraries.Remove(lib);
libraries.Remove(lib.Library.Name);
foreach (var dependency in lib.Library.Dependencies)
{
value.Dependents.Remove(lib.Library.Name);
if (libraries.TryGetValue(dependency.Name, out ModifiableRuntimeLibrary value))
{
value.Dependents.Remove(lib.Library.Name);
}
}
}

foreach (var dependent in lib.Dependents)
{
if (libraries.TryGetValue(dependent, out var dep))
foreach (var dependent in lib.Dependents)
{
unprocessedLibraries.Add(dep);
if (libraries.TryGetValue(dependent, out var dep))
{
unprocessedLibraries.Add(dep);
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class GenerateDepsFile : TaskBase
[Required]
public bool IncludeMainProject { get; set; }

public bool TrimDepsJsonLibrariesWithoutAssets { get; set; }

// @(ReferencePath) that will be passed to
public ITaskItem[] ReferencePaths { get; set; } = Array.Empty<ITaskItem>();

Expand Down Expand Up @@ -230,6 +232,7 @@ bool ShouldIncludeRuntimeAsset(ITaskItem item)

builder = builder
.WithMainProjectInDepsFile(IncludeMainProject)
.WithTrimLibrariesWithoutAssets(TrimDepsJsonLibrariesWithoutAssets)
.WithReferenceAssemblies(referenceAssemblyInfos)
.WithDirectReferences(directReferences)
.WithDependencyReferences(dependencyReferences)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Copyright (c) .NET Foundation. All rights reserved.
AssetsFilePath="$(ProjectAssetsFile)"
DepsFilePath="$(_DesignerDepsFilePath)"
IncludeMainProject="false"
TrimDepsJsonLibrariesWithoutAssets="$(TrimDepsJsonLibrariesWithoutAssets)"
IncludeRuntimeFileVersions="$(IncludeFileVersionsInDependencyFile)"
IsSelfContained="false"
PlatformLibraryName="$(MicrosoftNETPlatformLibrary)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,7 @@ Copyright (c) .NET Foundation. All rights reserved.
ReferenceAssemblies="@(_ReferenceAssemblies)"
RuntimePackAssets="@(RuntimePackAsset)"
IncludeMainProject="$(IncludeMainProjectInDepsFile)"
TrimDepsJsonLibrariesWithoutAssets="$(TrimDepsJsonLibrariesWithoutAssets)"
RuntimeIdentifier="$(RuntimeIdentifier)"
PlatformLibraryName="$(MicrosoftNETPlatformLibrary)"
RuntimeFrameworks="@(RuntimeFramework)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<ProjectRuntimeConfigFilePath Condition="'$(ProjectRuntimeConfigFilePath)' == ''">$(TargetDir)$(ProjectRuntimeConfigFileName)</ProjectRuntimeConfigFilePath>
<ProjectRuntimeConfigDevFilePath Condition="'$(ProjectRuntimeConfigDevFilePath)' == '' and $(GenerateRuntimeConfigDevFile) == 'true'">$(TargetDir)$(AssemblyName).runtimeconfig.dev.json</ProjectRuntimeConfigDevFilePath>
<IncludeMainProjectInDepsFile Condition=" '$(IncludeMainProjectInDepsFile)' == '' ">true</IncludeMainProjectInDepsFile>
<TrimDepsJsonLibrariesWithoutAssets Condition=" '$(TrimDepsJsonLibrariesWithoutAssets)' == '' ">true</TrimDepsJsonLibrariesWithoutAssets>
</PropertyGroup>

<Import Project="Microsoft.NET.Sdk.Shared.targets" />
Expand Down Expand Up @@ -316,6 +317,7 @@ Copyright (c) .NET Foundation. All rights reserved.
ReferenceAssemblies="@(_ReferenceAssemblies)"
RuntimePackAssets="@(RuntimePackAsset)"
IncludeMainProject="$(IncludeMainProjectInDepsFile)"
TrimDepsJsonLibrariesWithoutAssets="$(TrimDepsJsonLibrariesWithoutAssets)"
RuntimeIdentifier="$(RuntimeIdentifier)"
PlatformLibraryName="$(MicrosoftNETPlatformLibrary)"
RuntimeFrameworks="@(RuntimeFramework)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,24 +136,60 @@ public void PackageReferences_with_private_assets_do_not_appear_in_deps_file(str
}
}

[Fact]
public void PackageWithoutAssets_ShouldNotShowUpInDepsJson()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void PackageWithoutAssets_ShouldNotShowUpInDepsJson(bool trimLibrariesWithoutAssets)
{
var testProject = new TestProject()
{
TargetFrameworks = ToolsetInfo.CurrentTargetFramework
};
testProject.PackageReferences.Add(new TestPackageReference("Nerdbank.GitVersioning", "3.6.146"));

if (!trimLibrariesWithoutAssets)
{
testProject.AdditionalProperties["TrimDepsJsonLibrariesWithoutAssets"] = "False";
Copy link

Copilot AI Jun 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using consistent lowercase boolean string representations (e.g., 'false' instead of 'False') to match the casing used in the MSBuild targets.

Suggested change
testProject.AdditionalProperties["TrimDepsJsonLibrariesWithoutAssets"] = "False";
testProject.AdditionalProperties["TrimDepsJsonLibrariesWithoutAssets"] = "false";

Copilot uses AI. Check for mistakes.
}

var testAsset = _testAssetsManager.CreateTestProject(testProject);

var buildCommand = new BuildCommand(testAsset);
buildCommand.Execute().Should().Pass();

using (var depsJsonFileStream = File.OpenRead(Path.Combine(buildCommand.GetOutputDirectory(ToolsetInfo.CurrentTargetFramework).FullName, $"{testProject.Name}.deps.json")))
{
var dependencyContext = new DependencyContextJsonReader().Read(depsJsonFileStream);
if (trimLibrariesWithoutAssets)
{
dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("Nerdbank.GitVersioning")).Should().BeFalse();
}
else
{
dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("Nerdbank.GitVersioning")).Should().BeTrue();
}
}
}

[Fact]
public void XUnitCoreIsNotTrimmed()
{
// Regression test for https://github.com/dotnet/sdk/issues/49248

var testProject = new TestProject();
testProject.PackageReferences.Add(new TestPackageReference("xunit.core", "2.9.3"));
testProject.PackageReferences.Add(new TestPackageReference("xunit.extensibility.core", "2.9.3"));
testProject.PackageReferences.Add(new TestPackageReference("xunit.extensibility.execution", "2.9.3"));

var testAsset = _testAssetsManager.CreateTestProject(testProject);

var buildCommand = new BuildCommand(testAsset);
buildCommand.Execute().Should().Pass();

using (var depsJsonFileStream = File.OpenRead(Path.Combine(buildCommand.GetOutputDirectory(ToolsetInfo.CurrentTargetFramework).FullName, "PackageWithoutAssets_ShouldNotShowUpInDepsJson.deps.json")))
using (var depsJsonFileStream = File.OpenRead(Path.Combine(buildCommand.GetOutputDirectory(ToolsetInfo.CurrentTargetFramework).FullName, $"{testProject.Name}.deps.json")))
{
var dependencyContext = new DependencyContextJsonReader().Read(depsJsonFileStream);
dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("Nerdbank.GitVersioning")).Should().BeFalse();
dependencyContext.RuntimeLibraries.Any(l => l.Name.Equals("xunit.core")).Should().BeTrue();
}
}

Expand Down
Loading