forked from dotnet/sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPreprocessPackageDependenciesDesignTime.cs
172 lines (143 loc) · 6.88 KB
/
PreprocessPackageDependenciesDesignTime.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.NET.Build.Tasks
{
/// <summary>
/// Filters and projects items produced by <see cref="ResolvePackageDependencies"/> for consumption by
/// the dependencies tree, via design-time builds.
/// </summary>
/// <remarks>
/// Only top-level package references are retained (i.e. those referenced directly by the project, not
/// those only brought in transitively).
///
/// Only package references applicable to <see cref="TargetFramework"/> are retained.
///
/// Changes to the implementation of this class must be coordinated with <c>PackageRuleHandler</c>
/// in the dotnet/project-system repo.
/// </remarks>
public class PreprocessPackageDependenciesDesignTime : TaskBase
{
public const string ResolvedMetadata = "Resolved";
/// <summary>
/// Information about each package in the project, with metadata:
/// - Name = "MetadataExtractor"
/// - Path = "metadataextractor/1.0.0"
/// - ResolvedPath = "C:\Users\drnoakes\.nuget\packages\metadataextractor\1.0.0"
/// - Type = "package"
/// - Version = "2.3.0"
/// - DiagnosticLevel = ""
/// </summary>
[Required]
public ITaskItem[] PackageDefinitions { get; set; }
/// <summary>
/// Items with metadata "ParentTarget" and "ParentPackage", which allows determining the hierarchy of package references.
/// </summary>
[Required]
public ITaskItem[] PackageDependencies { get; set; }
/// <summary>
/// Eg: "Microsoft.NETCore.App;NETStandard.Library"
/// </summary>
[Required]
public string DefaultImplicitPackages { get; set; }
/// <summary>
/// The TargetFramework, which may be an alias
/// Eg: "netcoreapp3.1", "net5.0-windows", etc.
/// Only packages targeting this framework will be returned.
/// </summary>
[Required]
public string TargetFramework { get; set; }
[Output]
public ITaskItem[] PackageDependenciesDesignTime { get; private set; }
protected override void ExecuteCore()
{
var implicitPackageReferences = GetImplicitPackageReferences(DefaultImplicitPackages);
// We have two types of data:
//
// 1) "PackageDependencies" which place a package in a given target/hierarchy
// 2) "PackageDefinitions" which provide general metadata about a package
//
// First, we scan PackageDependencies to build the set of packages in our target.
var allowItemSpecs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var dependency in PackageDependencies)
{
if (dependency.HasMetadataValue(MetadataKeys.ParentPackage))
{
// ignore non-top-level packages (those with ParentPackage)
continue;
}
var target = dependency.GetMetadata(MetadataKeys.ParentTarget);
if (!StringComparer.OrdinalIgnoreCase.Equals(target, TargetFramework))
{
// skip dependencies for other targets
continue;
}
allowItemSpecs.Add(dependency.ItemSpec);
}
// Second, find PackageDefinitions that match our allowed item specs
var outputItems = new List<ITaskItem>(allowItemSpecs.Count);
foreach (var packageDef in PackageDefinitions)
{
if (!allowItemSpecs.Contains(packageDef.ItemSpec))
{
// We are not interested in this definition (not top-level, or wrong target)
continue;
}
var dependencyType = GetDependencyType(packageDef.GetMetadata(MetadataKeys.Type));
if (dependencyType == DependencyType.Package ||
dependencyType == DependencyType.Unresolved)
{
var name = packageDef.GetMetadata(MetadataKeys.Name);
if (string.IsNullOrEmpty(name))
{
// Name is required
continue;
}
var version = packageDef.GetMetadata(MetadataKeys.Version) ?? string.Empty;
var resolvedPath = packageDef.GetMetadata(MetadataKeys.ResolvedPath);
var resolved = !string.IsNullOrEmpty(resolvedPath);
var path = (resolved
? resolvedPath
: packageDef.GetMetadata(MetadataKeys.Path)) ?? string.Empty;
var isImplicitlyDefined = implicitPackageReferences.Contains(name);
var diagnosticLevel = packageDef.GetMetadata(MetadataKeys.DiagnosticLevel) ?? string.Empty;
var outputItem = new TaskItem(packageDef.ItemSpec);
outputItem.SetMetadata(MetadataKeys.Name, name);
outputItem.SetMetadata(MetadataKeys.Version, version);
outputItem.SetMetadata(MetadataKeys.Path, path);
outputItem.SetMetadata(MetadataKeys.IsImplicitlyDefined, isImplicitlyDefined.ToString());
outputItem.SetMetadata(MetadataKeys.DiagnosticLevel, diagnosticLevel);
outputItem.SetMetadata(ResolvedMetadata, resolved.ToString());
outputItems.Add(outputItem);
}
}
PackageDependenciesDesignTime = outputItems.ToArray();
}
internal static HashSet<string> GetImplicitPackageReferences(string defaultImplicitPackages)
{
var implicitPackageReferences = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrEmpty(defaultImplicitPackages))
{
return implicitPackageReferences;
}
var packageNames = defaultImplicitPackages.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
if (packageNames.Length == 0)
{
return implicitPackageReferences;
}
foreach (var packageReference in packageNames)
{
implicitPackageReferences.Add(packageReference);
}
return implicitPackageReferences;
}
private static DependencyType GetDependencyType(string dependencyTypeString)
{
Enum.TryParse(dependencyTypeString, ignoreCase: true, out DependencyType dependencyType);
return dependencyType;
}
}
}