-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automatically pack transitive assets when PrivateAssets=all
When using `<PackageReference PrivateAssets=all `, transitive dependencies were not being brough in. This required package authors to reference explicitly each and every package they wanted packaged as private assets. This was particularly annoying for build and analyzer packages, which need to package all their dependencies privately. This commit leverages the SDK-provided `RunResolvePackageDependencies` which returns the transitive closure of all referenced packages as a list of package>parent list. We use that to add the concept of "implicit package references" that basically share the PrivateAssets=all that brought them in. The inference target then just considers both `@(PackageReference)` as well as `@(ImplicitPackageReference)` to determine the primary output dependencies to pack, but otherwise the existing logic remains unchanged. This behavior still honors the `Pack=false` on the PackageReference, but also skips the implificly defined packages like NETStandard.Library and Microsoft.NETCore. Fixes NuGet/Home#5103.
- Loading branch information
Showing
4 changed files
with
204 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Microsoft.Build.Framework; | ||
using Microsoft.Build.Utilities; | ||
|
||
namespace NuGetizer.Tasks | ||
{ | ||
public class InferImplicitPackageReference : Task | ||
{ | ||
[Required] | ||
public ITaskItem[] PackageReferences { get; set; } = Array.Empty<ITaskItem>(); | ||
|
||
[Required] | ||
public ITaskItem[] PackageDependencies { get; set; } = Array.Empty<ITaskItem>(); | ||
|
||
[Output] | ||
public ITaskItem[] ImplicitPackageReferences { get; set; } = Array.Empty<ITaskItem>(); | ||
|
||
public override bool Execute() | ||
{ | ||
var packages = new ConcurrentDictionary<PackageIdentity, List<PackageIdentity>>(); | ||
Func<string, PackageIdentity> parse = value => | ||
{ | ||
var parts = value.Split('/'); | ||
return new PackageIdentity(parts[0], parts[1]); | ||
}; | ||
|
||
// Build the list of parent>child relationships. | ||
foreach (var dependency in PackageDependencies.Where(x => x.ItemSpec.Contains('/'))) | ||
{ | ||
var identity = parse(dependency.ItemSpec); | ||
var parent = dependency.GetMetadata("ParentPackage"); | ||
if (!string.IsNullOrEmpty(parent)) | ||
{ | ||
packages.GetOrAdd(parse(parent), _ => new List<PackageIdentity>()) | ||
.Add(identity); | ||
} | ||
} | ||
|
||
var inferred = new HashSet<PackageIdentity>(); | ||
|
||
foreach (var reference in PackageReferences.Where(x => | ||
"all".Equals(x.GetMetadata("PrivateAssets"), StringComparison.OrdinalIgnoreCase) && | ||
// Unless explicitly set to Pack=false | ||
(!x.TryGetBoolMetadata("Pack", out var pack) || pack != false) && | ||
// NETCore/NETStandard are implicitly defined, we never need to bring them as deps. | ||
!(bool.TryParse(x.GetMetadata("IsImplicitlyDefined"), out var isImplicit) && isImplicit))) | ||
{ | ||
var identity = new PackageIdentity(reference.ItemSpec, reference.GetMetadata("Version")); | ||
foreach (var dependency in FindDependencies(identity, packages)) | ||
{ | ||
inferred.Add(dependency); | ||
} | ||
} | ||
|
||
ImplicitPackageReferences = inferred | ||
.Select(x => new TaskItem( | ||
x.Id, | ||
new Dictionary<string, string> | ||
{ | ||
{ "Version", x.Version } , | ||
{ "PrivateAssets", "all" }, | ||
})) | ||
.ToArray(); | ||
|
||
return true; | ||
} | ||
|
||
IEnumerable<PackageIdentity> FindDependencies(PackageIdentity identity, IDictionary<PackageIdentity, List<PackageIdentity>> packages) | ||
{ | ||
if (packages.TryGetValue(identity, out var dependencies)) | ||
{ | ||
foreach (var dependency in dependencies) | ||
{ | ||
yield return dependency; | ||
foreach (var child in FindDependencies(dependency, packages)) | ||
{ | ||
yield return child; | ||
} | ||
} | ||
} | ||
} | ||
|
||
class PackageIdentity | ||
{ | ||
public PackageIdentity(string id, string version) | ||
=> (Id, Version) | ||
= (id, version); | ||
|
||
public string Id { get; } | ||
public string Version { get; } | ||
|
||
public override bool Equals(object obj) | ||
=> obj is PackageIdentity dependency && | ||
dependency.Id == Id && | ||
dependency.Version == Version; | ||
|
||
public override int GetHashCode() => Tuple.Create(Id, Version).GetHashCode(); | ||
|
||
public override string ToString() => Id + "/" + Version; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters