main
cs 181 lines 7.91 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.Burn.Bind
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Data;
9 using WixToolset.Core.Burn.Bundles;
10 using WixToolset.Extensibility.Services;
11 using WixToolset.Data.Symbols;
12
13 internal class ProcessDependencyProvidersCommand
14 {
15 public ProcessDependencyProvidersCommand(IServiceProvider serviceProvider, IntermediateSection section, PackageFacades facades)
16 {
17 this.Messaging = serviceProvider.GetService<IMessaging>();
18 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
19
20 this.Section = section;
21 this.Facades = facades;
22 }
23
24 public string BundleProviderKey { get; private set; }
25
26 private IMessaging Messaging { get; }
27
28 private IBackendHelper BackendHelper { get; }
29
30 private IntermediateSection Section { get; }
31
32 private PackageFacades Facades { get; }
33
34 /// <summary>
35 /// Sets the explicitly provided bundle provider key, if provided. And...
36 /// Imports authored dependency providers for each package in the manifest,
37 /// and generates dependency providers for certain package types that do not
38 /// have a provider defined.
39 /// </summary>
40 public void Execute()
41 {
42 this.ProcessHarvestedProviders();
43
44 var dependencySymbols = this.Section.Symbols.OfType<WixDependencyProviderSymbol>();
45
46 foreach (var dependency in dependencySymbols)
47 {
48 // Sets the provider key for the bundle, if it is not set already.
49 if (dependency.Bundle)
50 {
51 if (String.IsNullOrEmpty(this.BundleProviderKey))
52 {
53 this.BundleProviderKey = dependency.ProviderKey;
54 }
55 else
56 {
57 this.Messaging.Write(BurnBackendErrors.BundleMultipleProviders(dependency.SourceLineNumbers, dependency.ProviderKey, this.BundleProviderKey));
58 }
59 }
60
61 // Import any authored dependencies. These may merge with imported provides from MSI packages.
62 var packageId = dependency.ParentRef;
63
64 if (this.Facades.TryGetFacadeByPackageId(packageId, out var facade))
65 {
66 if (String.IsNullOrEmpty(dependency.ProviderKey))
67 {
68 switch (facade.SpecificPackageSymbol)
69 {
70 // The WixDependencyExtension allows an empty Key for MSIs and MSPs.
71 case WixBundleMsiPackageSymbol msiPackage:
72 dependency.ProviderKey = msiPackage.ProductCode;
73 break;
74 case WixBundleMspPackageSymbol mspPackage:
75 dependency.ProviderKey = mspPackage.PatchCode;
76 break;
77 }
78 }
79
80 if (String.IsNullOrEmpty(dependency.Version))
81 {
82 dependency.Version = facade.PackageSymbol.Version;
83 }
84
85 // If the version is still missing, a version could not be gathered from the package and was not authored.
86 if (String.IsNullOrEmpty(dependency.Version))
87 {
88 this.Messaging.Write(ErrorMessages.MissingDependencyVersion(facade.PackageId));
89 }
90
91 if (String.IsNullOrEmpty(dependency.DisplayName))
92 {
93 dependency.DisplayName = facade.PackageSymbol.DisplayName;
94 }
95 }
96 }
97
98 var dependencySymbolsByPackageId = this.GetDependencySymbolsByPackageId(dependencySymbols);
99
100 // Generate providers for MSI and MSP packages that still do not have providers.
101 foreach (var facade in this.Facades.Values)
102 {
103 string key = null;
104
105 if (facade.SpecificPackageSymbol is WixBundleMsiPackageSymbol msiPackage)
106 {
107 key = msiPackage.ProductCode;
108 }
109 else if (facade.SpecificPackageSymbol is WixBundleMspPackageSymbol mspPackage)
110 {
111 key = mspPackage.PatchCode;
112 }
113
114 if (!String.IsNullOrEmpty(key) && !dependencySymbolsByPackageId.Contains(facade.PackageId))
115 {
116 this.Section.AddSymbol(new WixDependencyProviderSymbol(facade.PackageSymbol.SourceLineNumbers, facade.PackageSymbol.Id)
117 {
118 ParentRef = facade.PackageId,
119 ProviderKey = $"{key}_v{facade.PackageSymbol.Version}",
120 Version = facade.PackageSymbol.Version,
121 DisplayName = facade.PackageSymbol.DisplayName
122 });
123 }
124 }
125 }
126
127 private HashSet<string> GetDependencySymbolsByPackageId(IEnumerable<WixDependencyProviderSymbol> dependencySymbols)
128 {
129 var dependencySymbolsByKey = new Dictionary<string, WixDependencyProviderSymbol>();
130 var dependencySymbolsByPackageId = new HashSet<string>();
131
132 foreach (var dependency in dependencySymbols)
133 {
134 if (dependencySymbolsByKey.TryGetValue(dependency.ProviderKey, out var collision))
135 {
136 // If not a perfect dependency collision, display an error.
137 if (dependency.ProviderKey != collision.ProviderKey ||
138 dependency.Version != collision.Version ||
139 dependency.DisplayName != collision.DisplayName)
140 {
141 this.Messaging.Write(ErrorMessages.DuplicateProviderDependencyKey(dependency.ProviderKey, dependency.ParentRef));
142 }
143 }
144 else
145 {
146 dependencySymbolsByKey.Add(dependency.ProviderKey, dependency);
147 }
148
149 dependencySymbolsByPackageId.Add(dependency.ParentRef);
150 }
151
152 return dependencySymbolsByPackageId;
153 }
154
155 private void ProcessHarvestedProviders()
156 {
157 var harvestedDependencies = this.Section.Symbols.OfType<WixBundleHarvestedDependencyProviderSymbol>().ToList();
158 foreach (var harvestedDependency in harvestedDependencies)
159 {
160 if (!this.Facades.TryGetFacadesByPackagePayloadId(harvestedDependency.PackagePayloadRef, out var facades))
161 {
162 this.Messaging.Write(ErrorMessages.IdentifierNotFound("Package.PayloadRef", harvestedDependency.PackagePayloadRef));
163 continue;
164 }
165
166 foreach (var facade in facades)
167 {
168 var depId = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", facade.PackageId, harvestedDependency.Id.Id));
169 this.Section.AddSymbol(new WixDependencyProviderSymbol(harvestedDependency.SourceLineNumbers, depId)
170 {
171 ParentRef = facade.PackageId,
172 ProviderKey = harvestedDependency.ProviderKey,
173 Version = harvestedDependency.Version,
174 DisplayName = harvestedDependency.DisplayName,
175 Attributes = WixDependencyProviderAttributes.ProvidesAttributesImported | (WixDependencyProviderAttributes)harvestedDependency.ProviderAttributes,
176 });
177 }
178 }
179 }
180 }
181 }