-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Include only PackageReferences included at runtime in deps.json #46218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
90898c7
a8ae853
fa1bd9d
6e201bf
082634c
dc20de0
298bb03
9542489
61ab712
4df737f
7cf7f45
5fba7d6
662d157
e73d3df
6a09c4c
181689f
f436c2d
ee824dd
87f906a
540e612
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -258,7 +258,7 @@ public DependencyContext Build(string[] userRuntimeAssemblies = null) | |
{ | ||
CalculateExcludedLibraries(); | ||
|
||
List<RuntimeLibrary> runtimeLibraries = new(); | ||
List<ModifiableRuntimeLibrary> runtimeLibraries = new(); | ||
|
||
if (_includeMainProjectInDepsFile) | ||
{ | ||
|
@@ -285,23 +285,83 @@ public DependencyContext Build(string[] userRuntimeAssemblies = null) | |
|
||
foreach (var directReference in directAndDependencyReferences) | ||
{ | ||
var runtimeLibrary = new RuntimeLibrary( | ||
var runtimeLibrary = new ModifiableRuntimeLibrary(new RuntimeLibrary( | ||
type: "reference", | ||
name: GetReferenceLibraryName(directReference), | ||
version: directReference.Version, | ||
hash: string.Empty, | ||
runtimeAssemblyGroups: new[] { new RuntimeAssetGroup(string.Empty, new[] { CreateRuntimeFile(directReference.FileName, directReference.FullPath) }) }, | ||
nativeLibraryGroups: new RuntimeAssetGroup[] { }, | ||
runtimeAssemblyGroups: [new RuntimeAssetGroup(string.Empty, [CreateRuntimeFile(directReference.FileName, directReference.FullPath)])], | ||
nativeLibraryGroups: [], | ||
resourceAssemblies: CreateResourceAssemblies(directReference.ResourceAssemblies), | ||
dependencies: Enumerable.Empty<Dependency>(), | ||
dependencies: [], | ||
path: null, | ||
hashPath: null, | ||
runtimeStoreManifestName: null, | ||
serviceable: false); | ||
serviceable: false)); | ||
|
||
runtimeLibraries.Add(runtimeLibrary); | ||
} | ||
|
||
/* | ||
* We now need to modify runtimeLibraries to eliminate those that don't have any runtime assets. We follow the following steps: | ||
* 0. Construct a reverse dependencies list: all runtimeLibraries that depend on this one | ||
* 1. If runtimeAssemblyGroups, nativeLibraryGroups, dependencies, and resourceAssemblies are all empty, remove this runtimeLibrary as well as any dependencies on it. | ||
* 2. Add all runtimeLibraries to a list of to-be-processed libraries called libraryCandidatesForRemoval | ||
* 3. libraryCandidatesForRemoval.Pop() --> if there are no runtimeAssemblyGroups, nativeLibraryGroups, or resourceAssemblies, and either dependencies is empty or all | ||
* dependencies have something else that depends on them, remove it (and from libraryCandidatesForRemoval), adding everything that depends on this to | ||
* libraryCandidatesForRemoval if it isn't already there | ||
* Repeat 3 until libraryCandidatesForRemoval is empty | ||
*/ | ||
var references = runtimeLibraries.ToDictionary(lib => lib.Library.Name, lib => lib); | ||
foreach (var reference in runtimeLibraries) | ||
{ | ||
foreach (var dependency in reference.Library.Dependencies) | ||
{ | ||
if (references.TryGetValue(dependency.Name, out var dep)) | ||
{ | ||
dep.Dependents.Add(reference.Library.Name); | ||
} | ||
} | ||
} | ||
|
||
var unprocessedReferences = runtimeLibraries.ToHashSet(); | ||
HashSet<ModifiableRuntimeLibrary> temp = new(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reason for the separate If not, it would probably be a good idea to rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. That's exactly how I'd been thinking about it, but you're right that it wasn't actually being iterated over, so there's no need for a temp HashSet. Deleted it |
||
while (unprocessedReferences.Any()) | ||
{ | ||
var lib = unprocessedReferences.First(); | ||
unprocessedReferences.Remove(lib); | ||
|
||
if (lib.Library.RuntimeAssemblyGroups.Count == 0 && lib.Library.NativeLibraryGroups.Count == 0 && lib.Library.ResourceAssemblies.Count == 0) | ||
{ | ||
if (lib.Library.Dependencies.All(d => !references.TryGetValue(d.Name, out var dependency) || dependency.Dependents.Count > 1)) | ||
{ | ||
runtimeLibraries.Remove(lib); | ||
references.Remove(lib.Library.Name); | ||
foreach (var dependency in lib.Library.Dependencies) | ||
{ | ||
if (references.TryGetValue(dependency.Name, out ModifiableRuntimeLibrary? value)) | ||
{ | ||
value.Dependents.Remove(lib.Library.Name); | ||
} | ||
} | ||
|
||
foreach (var dependent in lib.Dependents) | ||
{ | ||
if (references.TryGetValue(dependent, out var dep)) | ||
{ | ||
temp.Add(dep); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (!unprocessedReferences.Any()) | ||
{ | ||
unprocessedReferences = temp; | ||
temp = new(); | ||
} | ||
} | ||
|
||
List<CompilationLibrary> compilationLibraries = new(); | ||
if (IncludeCompilationLibraries) | ||
{ | ||
|
@@ -386,7 +446,6 @@ public DependencyContext Build(string[] userRuntimeAssemblies = null) | |
// | ||
// Otherwise, it is the set of all runtimes compatible with (inheriting) | ||
// the target runtime-identifier. | ||
|
||
var runtimeFallbackGraph = | ||
(_runtimeGraph == null || _runtimeIdentifier == null) ? | ||
new RuntimeFallbacks[] { } : | ||
|
@@ -399,11 +458,11 @@ public DependencyContext Build(string[] userRuntimeAssemblies = null) | |
targetInfo, | ||
_compilationOptions ?? CompilationOptions.Default, | ||
compilationLibraries, | ||
runtimeLibraries, | ||
runtimeLibraries.Select(library => library.Library), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the library and its dependencies are actually immutable, I think you need to recreate each library after the graph walk so that any dependencies that were removed don't show up as dangling links in the deps file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not very confident about what the deps.json is supposed to look at, but it looks ok to me. This is a runtime change, which means it keeps them as compile-time dependencies. Here's an example from the test:
Meanwhile, System.NotConflicting.Reference doesn't and looks like this:
(They should be more complicated if they were real.) In other words, I think keeping the dependencies list as it was before is correct; it removes the runtime references while retaining the note that they're connected. |
||
runtimeFallbackGraph); | ||
} | ||
|
||
private RuntimeLibrary GetProjectRuntimeLibrary() | ||
private ModifiableRuntimeLibrary GetProjectRuntimeLibrary() | ||
{ | ||
RuntimeAssetGroup[] runtimeAssemblyGroups = new[] { new RuntimeAssetGroup(string.Empty, _mainProjectInfo.OutputName) }; | ||
|
||
|
@@ -418,7 +477,7 @@ private RuntimeLibrary GetProjectRuntimeLibrary() | |
} | ||
} | ||
|
||
return new RuntimeLibrary( | ||
return new ModifiableRuntimeLibrary(new RuntimeLibrary( | ||
type: "project", | ||
name: _mainProjectInfo.Name, | ||
version: _mainProjectInfo.Version, | ||
|
@@ -430,7 +489,7 @@ private RuntimeLibrary GetProjectRuntimeLibrary() | |
path: null, | ||
hashPath: null, | ||
runtimeStoreManifestName: GetRuntimeStoreManifestName(_mainProjectInfo.Name, _mainProjectInfo.Version), | ||
serviceable: false); | ||
serviceable: false)); | ||
} | ||
|
||
private List<Dependency> GetProjectDependencies() | ||
|
@@ -477,11 +536,11 @@ private List<Dependency> GetProjectDependencies() | |
return dependencies; | ||
} | ||
|
||
private IEnumerable<RuntimeLibrary> GetRuntimePackLibraries() | ||
private IEnumerable<ModifiableRuntimeLibrary> GetRuntimePackLibraries() | ||
{ | ||
if (_runtimePackAssets == null) | ||
{ | ||
return Enumerable.Empty<RuntimeLibrary>(); | ||
return []; | ||
} | ||
return _runtimePackAssets.Select(runtimePack => | ||
{ | ||
|
@@ -493,20 +552,20 @@ private IEnumerable<RuntimeLibrary> GetRuntimePackLibraries() | |
runtimePack.Value.Where(asset => asset.AssetType == AssetType.Native) | ||
.Select(asset => CreateRuntimeFile(asset.DestinationSubPath, asset.SourcePath))); | ||
|
||
return new RuntimeLibrary( | ||
return new ModifiableRuntimeLibrary(new RuntimeLibrary( | ||
type: "runtimepack", | ||
name: runtimePack.Key, | ||
version: runtimePack.Value.First().PackageVersion, | ||
hash: string.Empty, | ||
runtimeAssemblyGroups: new[] { runtimeAssemblyGroup }, | ||
nativeLibraryGroups: new[] { nativeLibraryGroup }, | ||
resourceAssemblies: Enumerable.Empty<ResourceAssembly>(), | ||
dependencies: Enumerable.Empty<Dependency>(), | ||
serviceable: false); | ||
runtimeAssemblyGroups: [runtimeAssemblyGroup], | ||
nativeLibraryGroups: [nativeLibraryGroup], | ||
resourceAssemblies: [], | ||
dependencies: [], | ||
serviceable: false)); | ||
}); | ||
} | ||
|
||
private RuntimeLibrary GetRuntimeLibrary(DependencyLibrary library, string[] userRuntimeAssemblies) | ||
private ModifiableRuntimeLibrary GetRuntimeLibrary(DependencyLibrary library, string[] userRuntimeAssemblies) | ||
{ | ||
GetCommonLibraryProperties(library, | ||
out string hash, | ||
|
@@ -582,7 +641,7 @@ private RuntimeLibrary GetRuntimeLibrary(DependencyLibrary library, string[] use | |
|
||
} | ||
|
||
var runtimeLibrary = new RuntimeLibrary( | ||
var runtimeLibrary = new ModifiableRuntimeLibrary(new RuntimeLibrary( | ||
type: library.Type, | ||
name: library.Name, | ||
version: library.Version.ToString(), | ||
|
@@ -594,7 +653,7 @@ private RuntimeLibrary GetRuntimeLibrary(DependencyLibrary library, string[] use | |
path: path, | ||
hashPath: hashPath, | ||
runtimeStoreManifestName: GetRuntimeStoreManifestName(library.Name, library.Version.ToString()), | ||
serviceable: serviceable); | ||
serviceable: serviceable)); | ||
|
||
return runtimeLibrary; | ||
} | ||
|
@@ -885,5 +944,17 @@ private struct LibraryDependency | |
public string Name { get; set; } | ||
public NuGetVersion MinVersion { get; set; } | ||
} | ||
|
||
private class ModifiableRuntimeLibrary | ||
{ | ||
public RuntimeLibrary Library { get; set; } | ||
public HashSet<string> Dependents { get; set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's easy to get confused about what "dependent" means (libraries that depend on this one, ie the opposite of a dependency), so it would be good ta have a clarifying comment here. |
||
|
||
public ModifiableRuntimeLibrary(RuntimeLibrary library) | ||
{ | ||
this.Library = library; | ||
this.Dependents = new(); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Reference" seems to mean something slightly different than "library". Is there a reason not to name these
libraries
andlibrary
instead ofreferences
andreference
?