Skip to content

Commit e07a38c

Browse files
authored
Include only PackageReferences included at runtime in deps.json (#46218)
2 parents cb8ae89 + 540e612 commit e07a38c

File tree

6 files changed

+441
-32
lines changed

6 files changed

+441
-32
lines changed

src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenADependencyContextBuilder.cs

Lines changed: 192 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,6 @@ public static IEnumerable<object[]> ProjectData
144144
new object[] { "dotnet.new", "1.0.0", null, "dotnet.new.resources", null, dotnetNewSatelliteAssemblies, null, null, null },
145145
new object[] { "simple.dependencies", "1.0.0", null, "simple.dependencies", null, null, null, null, resolvedNuGetFiles },
146146
};
147-
148-
149-
150147
}
151148
}
152149

@@ -190,11 +187,16 @@ public void ItDoesntCreateReferenceAssembliesWhenNoCompilationOptions()
190187
.RuntimeLibraries
191188
.SelectMany(l => l.Dependencies)
192189
.Should()
193-
.NotBeEmpty()
194-
.And
195-
.NotContain(d => d.Name == "System.NotConflicting")
196-
.And
197-
.NotContain(d => d.Name == "System.Collections.NonGeneric.Reference");
190+
.BeEmpty();
191+
}
192+
193+
[Fact]
194+
public void ItDoesntCreateKeepUnneededRuntimeReferences()
195+
{
196+
DependencyContext dependencyContext = BuildDependencyContextWithReferenceAssemblies(useCompilationOptions: false);
197+
198+
dependencyContext.RuntimeLibraries.Count.Should().Be(1);
199+
dependencyContext.RuntimeLibraries[0].Name.Should().Be("simple.dependencies"); // This is the entrypoint
198200
}
199201

200202
[Fact]
@@ -213,6 +215,188 @@ public void ItHandlesReferenceAndPackageReferenceNameCollisions()
213215
.Contain(c => c.Name == "System.Collections.NonGeneric.Reference.Reference" && c.Type == "referenceassembly");
214216
}
215217

218+
// If an assembly is in withResources, it has to be a key in dependencies, even with an empty list.
219+
private static DependencyContext BuildDependencyContextFromDependenciesWithResources(Dictionary<string, List<string>> dependencies, List<string> withResources, List<string> references, bool dllReference)
220+
{
221+
string mainProjectName = "simpleApp";
222+
LockFile lockFile = TestLockFiles.GetLockFile(mainProjectName);
223+
224+
SingleProjectInfo mainProject = SingleProjectInfo.Create(
225+
"/usr/Path",
226+
mainProjectName,
227+
".dll",
228+
"1.0.0",
229+
[]);
230+
string mainProjectDirectory = Path.GetDirectoryName(mainProject.ProjectPath);
231+
232+
233+
ITaskItem[] referencePaths = dllReference ? references.Select(reference =>
234+
new MockTaskItem($"/usr/Path/{reference}.dll", new Dictionary<string, string> {
235+
{ "CopyLocal", "false" },
236+
{ "FusionName", $"{reference}, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null" },
237+
{ "Version", "" },
238+
})).ToArray() : [];
239+
240+
ProjectContext projectContext = lockFile.CreateProjectContext(
241+
FrameworkConstants.CommonFrameworks.Net10_0.GetShortFolderName(),
242+
runtime: null,
243+
platformLibraryName: Constants.DefaultPlatformLibrary,
244+
runtimeFrameworks: null,
245+
isSelfContained: false);
246+
247+
if (!dllReference)
248+
{
249+
projectContext.LockFile.ProjectFileDependencyGroups.Add(new ProjectFileDependencyGroup(string.Empty, references));
250+
}
251+
252+
Dictionary<string, SingleProjectInfo> referenceProjectInfos = new();
253+
254+
foreach (KeyValuePair<string, List<string>> kvp in dependencies)
255+
{
256+
projectContext.LockFileTarget.Libraries = projectContext.LockFileTarget.Libraries.Concat([
257+
new LockFileTargetLibrary()
258+
{
259+
Name = kvp.Key,
260+
Version = new NuGetVersion(4, 0, 0),
261+
Type = withResources.Contains(kvp.Key) ? "project" : "unrealType",
262+
Dependencies = kvp.Value.Select(n => new PackageDependency(n)).ToList()
263+
}]).ToList();
264+
265+
if (withResources.Contains(kvp.Key))
266+
{
267+
var fullPath = Path.GetFullPath(Path.Combine(mainProjectDirectory, kvp.Key));
268+
lockFile.Libraries = lockFile.Libraries.Concat([new LockFileLibrary()
269+
{
270+
Name = kvp.Key,
271+
Version = new NuGetVersion(4, 0, 0),
272+
Type = "project",
273+
MSBuildProject = fullPath
274+
}]).ToList();
275+
276+
referenceProjectInfos.Add(fullPath, SingleProjectInfo.Create(kvp.Key, kvp.Key, ".dll", "4.0.0",
277+
[new MockTaskItem($"{kvp.Key}.resource", new Dictionary<string, string>() {
278+
{ "Culture", "en-us" },
279+
{ "TargetPath", $"{kvp.Key}.resource" }
280+
})]));
281+
}
282+
}
283+
284+
CompilationOptions compilationOptions = CreateCompilationOptions();
285+
286+
return new DependencyContextBuilder(mainProject, includeRuntimeFileVersions: false, runtimeGraph: null, projectContext: projectContext, libraryLookup: new LockFileLookup(lockFile))
287+
.WithReferenceAssemblies(ReferenceInfo.CreateReferenceInfos(referencePaths))
288+
.WithCompilationOptions(compilationOptions)
289+
.WithReferenceProjectInfos(referenceProjectInfos)
290+
.Build();
291+
}
292+
293+
[Theory]
294+
[InlineData(true)]
295+
[InlineData(false)]
296+
public void DirectReferenceToPackageWithNoAssets(bool dllReference)
297+
{
298+
DependencyContext dependencyContext = BuildDependencyContextFromDependenciesWithResources([], [], ["System.A"], dllReference);
299+
Save(dependencyContext);
300+
dependencyContext.RuntimeLibraries.Count.Should().Be(1);
301+
}
302+
303+
[Theory]
304+
[InlineData(true)]
305+
[InlineData(false)]
306+
public void IndirectReferenceToPackageWithNoAssets(bool dllReference)
307+
{
308+
DependencyContext dependencyContext = BuildDependencyContextFromDependenciesWithResources(new Dictionary<string, List<string>>() {
309+
{ "System.A", ["System.B"] }
310+
}, ["System.A"], ["System.A"], dllReference);
311+
Save(dependencyContext);
312+
dependencyContext.RuntimeLibraries.Count.Should().Be(2);
313+
dependencyContext.RuntimeLibraries.Should().Contain(x => x.Name.Equals("System.A"));
314+
}
315+
316+
[Theory]
317+
[InlineData(true)]
318+
[InlineData(false)]
319+
public void PackageWithNoAssetsReferencesPackageWithNoAssets(bool dllReference)
320+
{
321+
DependencyContext dependencyContext = BuildDependencyContextFromDependenciesWithResources(new Dictionary<string, List<string>>() {
322+
{ "System.A", ["System.B"] },
323+
{ "System.B", [] }
324+
}, [], ["System.A"], dllReference);
325+
Save(dependencyContext);
326+
dependencyContext.RuntimeLibraries.Count.Should().Be(1);
327+
}
328+
329+
[Theory]
330+
[InlineData(true)]
331+
[InlineData(false)]
332+
public void PackageWithNoAssetsReferencesPackageWithAssets(bool dllReference)
333+
{
334+
DependencyContext dependencyContext = BuildDependencyContextFromDependenciesWithResources(new Dictionary<string, List<string>>() {
335+
{ "System.A", ["System.B"] },
336+
{ "System.B", [] }
337+
}, ["System.B"], ["System.A"], dllReference);
338+
Save(dependencyContext);
339+
dependencyContext.RuntimeLibraries.Count.Should().Be(3);
340+
dependencyContext.RuntimeLibraries.Should().Contain(x => x.Name.Equals("System.A"));
341+
dependencyContext.RuntimeLibraries.Should().Contain(x => x.Name.Equals("System.B"));
342+
}
343+
344+
[Theory]
345+
[InlineData(true)]
346+
[InlineData(false)]
347+
public void PackageWithNoAssetsReferencesPackageReferencesByOtherPackage(bool dllReference)
348+
{
349+
DependencyContext dependencyContext = BuildDependencyContextFromDependenciesWithResources(new Dictionary<string, List<string>>()
350+
{
351+
{ "System.A", ["System.B"] },
352+
{ "System.B", [] },
353+
}, ["System.B"], ["System.A", "System.B"], dllReference);
354+
Save(dependencyContext);
355+
dependencyContext.RuntimeLibraries.Count.Should().Be(2);
356+
dependencyContext.RuntimeLibraries.Should().Contain(x => x.Name.Equals("System.B"));
357+
}
358+
359+
[Theory]
360+
[InlineData(true)]
361+
[InlineData(false)]
362+
public void PackageWithNoAssetsReferencesPackageWithAssetsWithOtherReferencer(bool dllReference)
363+
{
364+
DependencyContext dependencyContext = BuildDependencyContextFromDependenciesWithResources(new Dictionary<string, List<string>>()
365+
{
366+
{ "System.A", ["System.B"] },
367+
{ "System.B", [] },
368+
{ "System.C", ["System.B"] }
369+
}, ["System.B", "System.C"], ["System.A", "System.C"], dllReference);
370+
Save(dependencyContext);
371+
dependencyContext.RuntimeLibraries.Count.Should().Be(3);
372+
dependencyContext.RuntimeLibraries.Should().Contain(x => x.Name.Equals("System.C"));
373+
dependencyContext.RuntimeLibraries.Should().Contain(x => x.Name.Equals("System.B"));
374+
}
375+
376+
[Theory]
377+
[InlineData(true)]
378+
[InlineData(false)]
379+
public void TwoPackagesWithNoAssetsReferencePackageWithAssets(bool dllReference)
380+
{
381+
DependencyContext dependencyContext = BuildDependencyContextFromDependenciesWithResources(new Dictionary<string, List<string>>()
382+
{
383+
{ "System.A", ["System.B"] },
384+
{ "System.C", ["System.B"] },
385+
{ "System.B", [] }
386+
}, ["System.B"], ["System.A", "System.C"], dllReference);
387+
Save(dependencyContext);
388+
dependencyContext.RuntimeLibraries.Count.Should().Be(3);
389+
dependencyContext.RuntimeLibraries.Should().Contain(x => x.Name.Equals("System.B"));
390+
if (dependencyContext.RuntimeLibraries.Any(x => x.Name.Equals("System.A")))
391+
{
392+
dependencyContext.RuntimeLibraries.Should().NotContain(x => x.Name.Equals("System.C"));
393+
}
394+
else
395+
{
396+
dependencyContext.RuntimeLibraries.Should().Contain(x => x.Name.Equals("System.C"));
397+
}
398+
}
399+
216400
private DependencyContext BuildDependencyContextWithReferenceAssemblies(bool useCompilationOptions)
217401
{
218402
string mainProjectName = "simple.dependencies";
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"version": 3,
3+
"targets": {
4+
"net10.0": {}
5+
},
6+
"libraries": {},
7+
"projectFileDependencyGroups": {
8+
"net10.0": []
9+
},
10+
"packageFolders": {
11+
"C:\\Users\\nmytelka\\.nuget\\packages\\": {},
12+
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
13+
},
14+
"project": {
15+
"version": "1.0.0",
16+
"restore": {
17+
"projectUniqueName": "C:\\git\\repro\\consoletest\\consoletest.csproj",
18+
"projectName": "consoletest",
19+
"projectPath": "C:\\git\\repro\\consoletest\\consoletest.csproj",
20+
"packagesPath": "C:\\Users\\username\\.nuget\\packages\\",
21+
"outputPath": "C:\\git\\repro\\consoletest\\obj\\",
22+
"projectStyle": "PackageReference",
23+
"fallbackFolders": [],
24+
"configFilePaths": [
25+
"C:\\Users\\username\\AppData\\Roaming\\NuGet\\NuGet.Config"
26+
],
27+
"originalTargetFrameworks": [
28+
"net10.0"
29+
],
30+
"sources": {
31+
"https://api.nuget.org/v3/index.json": {}
32+
},
33+
"frameworks": {
34+
"net10.0": {
35+
"targetAlias": "net10.0",
36+
"projectReferences": {}
37+
}
38+
},
39+
"warningProperties": {
40+
"warnAsError": [
41+
"NU1605"
42+
]
43+
},
44+
"restoreAuditProperties": {
45+
"enableAudit": "true",
46+
"auditLevel": "low",
47+
"auditMode": "all"
48+
},
49+
"SdkAnalysisLevel": "10.0.100"
50+
},
51+
"frameworks": {
52+
"net10.0": {
53+
"targetAlias": "net10.0",
54+
"imports": [
55+
"net472",
56+
"net481"
57+
],
58+
"frameworkReferences": {
59+
"Microsoft.NETCore.App": {
60+
"privateAssets": "all"
61+
}
62+
},
63+
"runtimeIdentifierGraphPath": "C:\\git\\sdk\\artifacts\\bin\\redist\\Debug\\dotnet\\sdk\\10.0.100-dev\\RuntimeIdentifierGraph.json"
64+
}
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)