aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Compiler_Dependency.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Compiler_Dependency.cs')
-rw-r--r--src/WixToolset.Core/Compiler_Dependency.cs385
1 files changed, 385 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Compiler_Dependency.cs b/src/WixToolset.Core/Compiler_Dependency.cs
new file mode 100644
index 00000000..74982fba
--- /dev/null
+++ b/src/WixToolset.Core/Compiler_Dependency.cs
@@ -0,0 +1,385 @@
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
3namespace WixToolset.Core
4{
5 using System;
6 using System.Xml.Linq;
7 using WixToolset.Data;
8 using WixToolset.Data.Symbols;
9
10 /// <summary>
11 /// Compiler of the WiX toolset.
12 /// </summary>
13 internal partial class Compiler : ICompiler
14 {
15 // The root registry key for the dependency extension. We write to Software\Classes explicitly
16 // based on the current security context instead of HKCR. See
17 // http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information.
18 private const string DependencyRegistryRoot = @"Software\Classes\Installer\Dependencies\";
19
20 private static readonly char[] InvalidDependencyCharacters = new char[] { ' ', '\"', ';', '\\' };
21
22 /// <summary>
23 /// Processes the ProviderKey bundle attribute.
24 /// </summary>
25 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
26 /// <param name="parentElement">Parent element of attribute.</param>
27 /// <param name="attribute">The XML attribute for the ProviderKey attribute.</param>
28 private void ParseBundleProviderKeyAttribute(SourceLineNumber sourceLineNumbers, XElement parentElement, XAttribute attribute)
29 {
30 var providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attribute);
31 int illegalChar;
32
33 // Make sure the key does not contain any illegal characters or values.
34 if (String.IsNullOrEmpty(providerKey))
35 {
36 this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, parentElement.Name.LocalName, attribute.Name.LocalName));
37 }
38 else if (0 <= (illegalChar = providerKey.IndexOfAny(InvalidDependencyCharacters)))
39 {
40 this.Messaging.Write(CompilerErrors.IllegalCharactersInProvider(sourceLineNumbers, "ProviderKey", providerKey[illegalChar], String.Join(" ", InvalidDependencyCharacters)));
41 }
42 else if ("ALL" == providerKey)
43 {
44 this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, parentElement.Name.LocalName, "ProviderKey", providerKey));
45 }
46
47 if (!this.Messaging.EncounteredError)
48 {
49 // Generate the primary key for the row.
50 var id = this.Core.CreateIdentifier("dep", attribute.Name.LocalName, providerKey);
51
52 // Create the provider symbol for the bundle. The Component_ field is required
53 // in the table definition but unused for bundles, so just set it to the valid ID.
54 this.Core.AddSymbol(new WixDependencyProviderSymbol(sourceLineNumbers, id)
55 {
56 ComponentRef = id.Id,
57 ProviderKey = providerKey,
58 Attributes = WixDependencyProviderAttributes.ProvidesAttributesBundle,
59 });
60 }
61 }
62
63 /// <summary>
64 /// Processes the Provides element.
65 /// </summary>
66 /// <param name="node">The XML node for the Provides element.</param>
67 /// <param name="packageType">The type of the package being chained into a bundle, or null if building an MSI package.</param>
68 /// <param name="parentId">The identifier of the parent component or package.</param>
69 /// <param name="possibleKeyPath">Possible KeyPath identifier.</param>
70 /// <returns>Yes if this is the keypath.</returns>
71 private YesNoType ParseProvidesElement(XElement node, WixBundlePackageType? packageType, string parentId, out string possibleKeyPath)
72 {
73 possibleKeyPath = null;
74
75 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
76 Identifier id = null;
77 string key = null;
78 string version = null;
79 string displayName = null;
80
81 foreach (var attrib in node.Attributes())
82 {
83 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
84 {
85 switch (attrib.Name.LocalName)
86 {
87 case "Id":
88 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
89 break;
90 case "Key":
91 key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
92 break;
93 case "Version":
94 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
95 break;
96 case "DisplayName":
97 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
98 break;
99 default:
100 this.Core.UnexpectedAttribute(node, attrib);
101 break;
102 }
103 }
104 else
105 {
106 this.Core.ParseExtensionAttribute(node, attrib);
107 }
108 }
109
110 // Make sure the key is valid. The key will default to the ProductCode for MSI packages
111 // and the package code for MSP packages in the binder if not specified.
112 if (!String.IsNullOrEmpty(key))
113 {
114 int illegalChar;
115
116 // Make sure the key does not contain any illegal characters or values.
117 if (0 <= (illegalChar = key.IndexOfAny(InvalidDependencyCharacters)))
118 {
119 this.Messaging.Write(CompilerErrors.IllegalCharactersInProvider(sourceLineNumbers, "Key", key[illegalChar], String.Join(" ", InvalidDependencyCharacters)));
120 }
121 else if ("ALL" == key)
122 {
123 this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Key", key));
124 }
125 }
126 else if (!packageType.HasValue)
127 {
128 // Make sure the ProductCode is authored and set the key.
129 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, "ProductCode");
130 key = "!(bind.property.ProductCode)";
131 }
132 else if (WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msu == packageType)
133 {
134 // Must specify the provider key when authored for a package.
135 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
136 }
137
138 // The Version attribute should not be authored in or for an MSI package.
139 if (!String.IsNullOrEmpty(version))
140 {
141 switch (packageType)
142 {
143 case null:
144 this.Messaging.Write(CompilerWarnings.DiscouragedVersionAttribute(sourceLineNumbers));
145 break;
146 case WixBundlePackageType.Msi:
147 this.Messaging.Write(CompilerWarnings.DiscouragedVersionAttribute(sourceLineNumbers, parentId));
148 break;
149 }
150 }
151 else if (WixBundlePackageType.Msp == packageType || WixBundlePackageType.Msu == packageType)
152 {
153 // Must specify the Version when authored for packages that do not contain a version.
154 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
155 }
156
157 // Need the element ID for child element processing, so generate now if not authored.
158 if (null == id)
159 {
160 id = this.Core.CreateIdentifier("dep", node.Name.LocalName, parentId, key);
161 }
162
163 foreach (var child in node.Elements())
164 {
165 if (CompilerCore.WixNamespace == child.Name.Namespace)
166 {
167 switch (child.Name.LocalName)
168 {
169 case "Requires":
170 this.ParseRequiresElement(child, id.Id, requiresAction: !packageType.HasValue);
171 break;
172 case "RequiresRef":
173 this.ParseRequiresRefElement(child, id.Id, requiresAction: !packageType.HasValue);
174 break;
175 default:
176 this.Core.UnexpectedElement(node, child);
177 break;
178 }
179 }
180 else
181 {
182 this.Core.ParseExtensionElement(node, child);
183 }
184 }
185
186 if (!this.Messaging.EncounteredError)
187 {
188 var symbol = this.Core.AddSymbol(new WixDependencyProviderSymbol(sourceLineNumbers, id)
189 {
190 ComponentRef = parentId,
191 ProviderKey = key,
192 });
193
194 if (!String.IsNullOrEmpty(version))
195 {
196 symbol.Version = version;
197 }
198
199 if (!String.IsNullOrEmpty(displayName))
200 {
201 symbol.DisplayName = displayName;
202 }
203
204 if (!packageType.HasValue)
205 {
206 // Generate registry rows for the provider using binder properties.
207 var keyProvides = String.Concat(DependencyRegistryRoot, key);
208 var root = RegistryRootType.MachineUser;
209
210 var value = "[ProductCode]";
211 this.Core.CreateRegistryRow(sourceLineNumbers, root, keyProvides, null, value, parentId);
212
213 value = !String.IsNullOrEmpty(version) ? version : "[ProductVersion]";
214 var versionRegistrySymbol = this.Core.CreateRegistryRow(sourceLineNumbers, root, keyProvides, "Version", value, parentId);
215
216 value = !String.IsNullOrEmpty(displayName) ? displayName : "[ProductName]";
217 this.Core.CreateRegistryRow(sourceLineNumbers, root, keyProvides, "DisplayName", value, parentId);
218
219 // Use the Version registry value and use that as a potential key path.
220 possibleKeyPath = versionRegistrySymbol.Id;
221 }
222 }
223
224 return YesNoType.NotSet;
225 }
226
227 /// <summary>
228 /// Processes the Requires element.
229 /// </summary>
230 /// <param name="node">The XML node for the Requires element.</param>
231 /// <param name="providerId">The parent provider identifier.</param>
232 /// <param name="requiresAction">Whether the Requires custom action should be referenced.</param>
233 private void ParseRequiresElement(XElement node, string providerId, bool requiresAction)
234 {
235 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
236 Identifier id = null;
237 string providerKey = null;
238 string minVersion = null;
239 string maxVersion = null;
240 var attributes = WixDependencySymbolAttributes.None;
241 var illegalChar = -1;
242
243 foreach (var attrib in node.Attributes())
244 {
245 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
246 {
247 switch (attrib.Name.LocalName)
248 {
249 case "Id":
250 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
251 break;
252 case "ProviderKey":
253 providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
254 break;
255 case "Minimum":
256 minVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
257 break;
258 case "Maximum":
259 maxVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
260 break;
261 case "IncludeMinimum":
262 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
263 {
264 attributes |= WixDependencySymbolAttributes.RequiresAttributesMinVersionInclusive;
265 }
266 break;
267 case "IncludeMaximum":
268 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
269 {
270 attributes |= WixDependencySymbolAttributes.RequiresAttributesMaxVersionInclusive;
271 }
272 break;
273 default:
274 this.Core.UnexpectedAttribute(node, attrib);
275 break;
276 }
277 }
278 else
279 {
280 this.Core.ParseExtensionAttribute(node, attrib);
281 }
282 }
283
284 this.Core.ParseForExtensionElements(node);
285
286 if (null == id)
287 {
288 // Generate an ID only if this element is authored under a Provides element; otherwise, a RequiresRef
289 // element will be necessary and the Id attribute will be required.
290 if (!String.IsNullOrEmpty(providerId))
291 {
292 id = this.Core.CreateIdentifier("dep", node.Name.LocalName, providerKey);
293 }
294 else
295 {
296 this.Messaging.Write(ErrorMessages.ExpectedAttributeWhenElementNotUnderElement(sourceLineNumbers, node.Name.LocalName, "Id", "Provides"));
297 id = Identifier.Invalid;
298 }
299 }
300
301 if (String.IsNullOrEmpty(providerKey))
302 {
303 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProviderKey"));
304 }
305 // Make sure the key does not contain any illegal characters.
306 else if (0 <= (illegalChar = providerKey.IndexOfAny(InvalidDependencyCharacters)))
307 {
308 this.Messaging.Write(CompilerErrors.IllegalCharactersInProvider(sourceLineNumbers, "ProviderKey", providerKey[illegalChar], String.Join(" ", InvalidDependencyCharacters)));
309 }
310
311 if (!this.Messaging.EncounteredError)
312 {
313 var symbol = this.Core.AddSymbol(new WixDependencySymbol(sourceLineNumbers, id)
314 {
315 ProviderKey = providerKey,
316 MinVersion = minVersion,
317 MaxVersion = maxVersion,
318 Attributes = attributes
319 });
320
321 // Create the relationship between this WixDependency symbol and the WixDependencyProvider symbol.
322 if (!String.IsNullOrEmpty(providerId))
323 {
324 this.Core.AddSymbol(new WixDependencyRefSymbol(sourceLineNumbers)
325 {
326 WixDependencyProviderRef = providerId,
327 WixDependencyRef = id.Id,
328 });
329 }
330 }
331 }
332
333 /// <summary>
334 /// Processes the RequiresRef element.
335 /// </summary>
336 /// <param name="node">The XML node for the RequiresRef element.</param>
337 /// <param name="providerId">The parent provider identifier.</param>
338 /// <param name="requiresAction">Whether the Requires custom action should be referenced.</param>
339 private void ParseRequiresRefElement(XElement node, string providerId, bool requiresAction)
340 {
341 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
342 string id = null;
343
344 foreach (var attrib in node.Attributes())
345 {
346 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
347 {
348 switch (attrib.Name.LocalName)
349 {
350 case "Id":
351 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
352 break;
353 default:
354 this.Core.UnexpectedAttribute(node, attrib);
355 break;
356 }
357 }
358 else
359 {
360 this.Core.ParseExtensionAttribute(node, attrib);
361 }
362 }
363
364 this.Core.ParseForExtensionElements(node);
365
366 if (String.IsNullOrEmpty(id))
367 {
368 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
369 }
370
371 if (!this.Messaging.EncounteredError)
372 {
373 // Create a link dependency on the row that contains information we'll need during bind.
374 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixDependency, id);
375
376 // Create the relationship between the WixDependency row and the parent WixDependencyProvider row.
377 this.Core.AddSymbol(new WixDependencyRefSymbol(sourceLineNumbers)
378 {
379 WixDependencyProviderRef = providerId,
380 WixDependencyRef = id,
381 });
382 }
383 }
384 }
385}