Skip to content

Commit 4c178ef

Browse files
committed
Improve diagnosability of manifest conflicts
1 parent 6707432 commit 4c178ef

File tree

1 file changed

+61
-33
lines changed

1 file changed

+61
-33
lines changed

src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ namespace Microsoft.NET.Sdk.WorkloadManifestReader
1818
public class WorkloadResolver : IWorkloadResolver
1919
{
2020
private readonly Dictionary<string, WorkloadManifest> _manifests = new Dictionary<string, WorkloadManifest>(StringComparer.OrdinalIgnoreCase);
21-
private readonly Dictionary<WorkloadId, WorkloadDefinition> _workloads = new Dictionary<WorkloadId, WorkloadDefinition>();
22-
private readonly Dictionary<WorkloadPackId, WorkloadPack> _packs = new Dictionary<WorkloadPackId, WorkloadPack>();
21+
private readonly Dictionary<WorkloadId, (WorkloadDefinition workload, WorkloadManifest manifest)> _workloads = new Dictionary<WorkloadId, (WorkloadDefinition, WorkloadManifest)>();
22+
private readonly Dictionary<WorkloadPackId, (WorkloadPack pack, WorkloadManifest manifest)> _packs = new Dictionary<WorkloadPackId, (WorkloadPack, WorkloadManifest)>();
2323
private IWorkloadManifestProvider? _manifestProvider;
2424
private string[] _currentRuntimeIdentifiers;
2525
private readonly string [] _dotnetRootPaths;
@@ -97,15 +97,14 @@ private void LoadManifestsFromProvider(IWorkloadManifestProvider manifestProvide
9797
using (var manifestStream = openManifestStream())
9898
{
9999
var manifest = WorkloadManifestReader.ReadWorkloadManifest(manifestId, manifestStream);
100-
if (_manifests.ContainsKey(manifestId))
100+
if(!string.Equals (manifestId, manifest.Id, StringComparison.OrdinalIgnoreCase))
101101
{
102-
throw new Exception($"Duplicate workload manifest {manifestId}");
102+
throw new WorkloadManifestCompositionException($"Manifest '{manifestId}' from provider {manifestProvider} does not match payload id '{manifest.Id}'");
103103
}
104-
if(!string.Equals (manifestId, manifest.Id, StringComparison.OrdinalIgnoreCase))
104+
if (!_manifests.TryAdd(manifestId, manifest))
105105
{
106-
throw new Exception($"Manifest provider {manifestProvider} supplied manifest '{manifestId}' does not match payload id '{manifest.Id}'");
106+
throw new WorkloadManifestCompositionException($"Duplicate manifest '{manifestId}' from provider {manifestProvider}");
107107
}
108-
_manifests.Add(manifestId, manifest);
109108
}
110109
}
111110
}
@@ -125,12 +124,12 @@ private void ComposeWorkloadManifests()
125124
{
126125
if (FXVersion.Compare(dependency.Value, resolvedDependency.ParsedVersion) > 0)
127126
{
128-
throw new Exception($"Inconsistency in workload manifest '{manifest.Id}': requires '{dependency.Key}' version at least {dependency.Value} but found {resolvedDependency.Version}");
127+
throw new WorkloadManifestCompositionException($"Inconsistency in workload manifest '{manifest.Id}': requires '{dependency.Key}' version at least {dependency.Value} but found {resolvedDependency.Version}");
129128
}
130129
}
131130
else
132131
{
133-
throw new Exception($"Inconsistency in workload manifest '{manifest.Id}': missing dependency '{dependency.Key}'");
132+
throw new WorkloadManifestCompositionException($"Inconsistency in workload manifest '{manifest.Id}': missing dependency '{dependency.Key}'");
134133
}
135134
}
136135
}
@@ -140,11 +139,14 @@ private void ComposeWorkloadManifests()
140139
{
141140
if (workload.Value is WorkloadRedirect redirect)
142141
{
143-
(redirects ?? (redirects = new HashSet<WorkloadRedirect>())).Add(redirect);
142+
(redirects ??= new HashSet<WorkloadRedirect>()).Add(redirect);
144143
}
145144
else
146145
{
147-
_workloads.Add(workload.Key, (WorkloadDefinition)workload.Value);
146+
if (!_workloads.TryAdd(workload.Key, ((WorkloadDefinition)workload.Value, manifest)))
147+
{
148+
throw new WorkloadManifestCompositionException($"Workload '{workload.Key}' in manifest '{manifest.Id}' conflicts with manifest '{_workloads[workload.Key].manifest.Id}'");
149+
}
148150
}
149151
}
150152

@@ -156,7 +158,10 @@ private void ComposeWorkloadManifests()
156158
while (redirects.RemoveWhere(redirect => {
157159
if (_workloads.TryGetValue(redirect.ReplaceWith, out var replacement))
158160
{
159-
_workloads.Add(redirect.Id, replacement);
161+
if (!_workloads.TryAdd(redirect.Id, replacement))
162+
{
163+
throw new WorkloadManifestCompositionException($"Workload '{redirect.Id}' in manifest '{manifest.Id}' conflicts with manifest '{_workloads[redirect.Id].manifest.Id}'");
164+
}
160165
return true;
161166
}
162167
return false;
@@ -170,7 +175,10 @@ private void ComposeWorkloadManifests()
170175

171176
foreach (var pack in manifest.Packs)
172177
{
173-
_packs.Add(pack.Key, pack.Value);
178+
if (!_packs.TryAdd(pack.Key, (pack.Value, manifest)))
179+
{
180+
throw new WorkloadManifestCompositionException($"Workload pack '{pack.Key}' in manifest '{manifest.Id}' conflicts with manifest '{_packs[pack.Key].manifest.Id}'");
181+
}
174182
}
175183
}
176184
}
@@ -184,7 +192,7 @@ private void ComposeWorkloadManifests()
184192
/// </remarks>
185193
public IEnumerable<PackInfo> GetInstalledWorkloadPacksOfKind(WorkloadPackKind kind)
186194
{
187-
foreach (var pack in _packs.Values)
195+
foreach ((var pack, _) in _packs.Values)
188196
{
189197
if (pack.Kind != kind)
190198
{
@@ -302,12 +310,12 @@ private string GetPackPath(string [] dotnetRootPaths, WorkloadPackId packageId,
302310
private HashSet<WorkloadPackId> GetInstalledPacks()
303311
{
304312
var installedPacks = new HashSet<WorkloadPackId>();
305-
foreach (var pack in _packs)
313+
foreach ((WorkloadPackId id, (WorkloadPack pack, WorkloadManifest _)) in _packs)
306314
{
307-
ResolvePackPath(pack.Value, out bool isInstalled);
315+
ResolvePackPath(pack, out bool isInstalled);
308316
if (isInstalled)
309317
{
310-
installedPacks.Add(pack.Key);
318+
installedPacks.Add(id);
311319
}
312320
}
313321
return installedPacks;
@@ -322,10 +330,11 @@ public IEnumerable<string> GetPacksInWorkload(string workloadId)
322330

323331
var id = new WorkloadId(workloadId);
324332

325-
if (!_workloads.TryGetValue(id, out var workload))
333+
if (!_workloads.TryGetValue(id, out var value))
326334
{
327335
throw new Exception($"Workload not found: {id}. Known workloads: {string.Join(" ", _workloads.Select(workload => workload.Key.ToString()))}");
328336
}
337+
var workload = value.workload;
329338

330339
if (workload.Extends?.Count > 0)
331340
{
@@ -343,7 +352,7 @@ internal IEnumerable<WorkloadPackId> GetPacksInWorkload(WorkloadDefinition workl
343352

344353
IEnumerable<WorkloadPackId> ExpandPacks (WorkloadId workloadId)
345354
{
346-
if (!_workloads.TryGetValue (workloadId, out var workloadInfo))
355+
if (!(_workloads.TryGetValue (workloadId) is (WorkloadDefinition workloadInfo, _)))
347356
{
348357
// inconsistent manifest
349358
throw new Exception("Workload not found");
@@ -388,11 +397,11 @@ IEnumerable<WorkloadPackId> ExpandPacks (WorkloadId workloadId)
388397
throw new ArgumentException($"'{nameof(packId)}' cannot be null or whitespace", nameof(packId));
389398
}
390399

391-
if (_packs.TryGetValue(new WorkloadPackId (packId), out var pack))
400+
if (_packs.TryGetValue(new WorkloadPackId (packId)) is (WorkloadPack pack, _))
392401
{
393402
if (ResolveId(pack) is WorkloadPackId resolvedPackageId)
394403
{
395-
var aliasedPath = GetPackPath(_dotnetRootPaths, resolvedPackageId, pack.Version, pack.Kind, out bool exists);
404+
var aliasedPath = GetPackPath(_dotnetRootPaths, resolvedPackageId, pack.Version, pack.Kind, out _);
396405
return CreatePackInfo(pack, aliasedPath, resolvedPackageId);
397406
}
398407
}
@@ -409,12 +418,12 @@ IEnumerable<WorkloadPackId> ExpandPacks (WorkloadId workloadId)
409418
public ISet<WorkloadInfo> GetWorkloadSuggestionForMissingPacks(IList<string> packIds)
410419
{
411420
var requestedPacks = new HashSet<WorkloadPackId>(packIds.Select(p => new WorkloadPackId(p)));
412-
var expandedWorkloads = _workloads.Select(w => (w.Key, new HashSet<WorkloadPackId>(GetPacksInWorkload(w.Value))));
421+
var expandedWorkloads = _workloads.Select(w => (w.Key, new HashSet<WorkloadPackId>(GetPacksInWorkload(w.Value.workload))));
413422
var finder = new WorkloadSuggestionFinder(GetInstalledPacks(), requestedPacks, expandedWorkloads);
414423

415424
return new HashSet<WorkloadInfo>
416425
(
417-
finder.GetBestSuggestion().Workloads.Select(s => new WorkloadInfo(s.ToString(), _workloads[s].Description))
426+
finder.GetBestSuggestion().Workloads.Select(s => new WorkloadInfo(s.ToString(), _workloads[s].workload.Description))
418427
);
419428
}
420429

@@ -423,7 +432,10 @@ public ISet<WorkloadInfo> GetWorkloadSuggestionForMissingPacks(IList<string> pac
423432
/// </summary>
424433
public IEnumerable<WorkloadDefinition> GetAvailableWorkloads()
425434
{
426-
return _workloads.Values;
435+
foreach ((WorkloadId _, (WorkloadDefinition workload, WorkloadManifest _)) in _workloads)
436+
{
437+
yield return workload;
438+
}
427439
}
428440

429441
/// <summary>
@@ -436,12 +448,12 @@ public IEnumerable<WorkloadId> GetUpdatedWorkloads(WorkloadResolver advertisingM
436448
{
437449
foreach(var workloadId in installedWorkloads)
438450
{
439-
var existingWorkload = _workloads[workloadId];
451+
var existingWorkload = _workloads[workloadId].workload;
440452
var existingPacks = GetPacksInWorkload(existingWorkload).ToHashSet();
441-
var updatedWorkload = advertisingManifestResolver._workloads[workloadId];
453+
var updatedWorkload = advertisingManifestResolver._workloads[workloadId].workload;
442454
var updatedPacks = advertisingManifestResolver.GetPacksInWorkload(updatedWorkload);
443455

444-
if (!existingPacks.SetEquals(updatedPacks) || existingPacks.Any(p=> PackHasChanged(_packs[p], advertisingManifestResolver._packs[p])))
456+
if (!existingPacks.SetEquals(updatedPacks) || existingPacks.Any(p=> PackHasChanged(_packs[p].pack, advertisingManifestResolver._packs[p].pack)))
445457
{
446458
yield return workloadId;
447459
}
@@ -539,12 +551,11 @@ public WorkloadInfo(string id, string? description)
539551

540552
public WorkloadInfo GetWorkloadInfo(WorkloadId WorkloadId)
541553
{
542-
if (!_workloads.TryGetValue(WorkloadId, out var workload))
554+
if (_workloads.TryGetValue(WorkloadId) is (WorkloadDefinition workload, _))
543555
{
544-
throw new Exception("Workload not found");
556+
return new WorkloadInfo(workload.Id.ToString(), workload.Description);
545557
}
546-
547-
return new WorkloadInfo(workload.Id.ToString(), workload.Description);
558+
throw new Exception("Workload not found");
548559
}
549560

550561
public bool IsWorkloadPlatformCompatible(WorkloadId workloadId)
@@ -562,10 +573,10 @@ public bool IsWorkloadPlatformCompatible(WorkloadId workloadId)
562573
}
563574
}
564575

565-
#if !NETCOREAPP
566576

567577
static class DictionaryExtensions
568578
{
579+
#if !NETCOREAPP
569580
public static bool TryAdd<TKey,TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value) where TKey : notnull
570581
{
571582
if (dictionary.ContainsKey(key))
@@ -575,6 +586,23 @@ public static bool TryAdd<TKey,TValue>(this Dictionary<TKey, TValue> dictionary,
575586
dictionary.Add(key, value);
576587
return true;
577588
}
578-
}
589+
590+
public static void Deconstruct<TKey,TValue>(this KeyValuePair<TKey,TValue> kvp, out TKey key, out TValue value)
591+
{
592+
key = kvp.Key;
593+
value = kvp.Value;
594+
}
579595
#endif
596+
597+
public static TValue? TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
598+
where TKey : notnull
599+
where TValue : struct
600+
{
601+
if (dictionary.TryGetValue(key, out TValue value))
602+
{
603+
return value;
604+
}
605+
return default(TValue?);
606+
}
607+
}
580608
}

0 commit comments

Comments
 (0)