aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-02-26 11:24:10 -0800
committerRob Mensching <rob@firegiant.com>2021-02-27 07:47:08 -0800
commit5fd1b7ff82f17d55c8357fe76898a1bdc5953476 (patch)
tree5ec191ebf43009daf9bde6d0c26879b181b9a71b
parent760fb810ba5ecc3c6ce752a9bfa3755f7b7c0f6a (diff)
downloadwix-5fd1b7ff82f17d55c8357fe76898a1bdc5953476.tar.gz
wix-5fd1b7ff82f17d55c8357fe76898a1bdc5953476.tar.bz2
wix-5fd1b7ff82f17d55c8357fe76898a1bdc5953476.zip
Absorb Dependency.wixext into Core
Partly resolves wixtoolset/issues#5949
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs12
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ProcessDependencyReferencesCommand.cs111
-rw-r--r--src/WixToolset.Core/Compiler.cs13
-rw-r--r--src/WixToolset.Core/CompilerErrors.cs13
-rw-r--r--src/WixToolset.Core/CompilerWarnings.cs53
-rw-r--r--src/WixToolset.Core/Compiler_Bundle.cs9
-rw-r--r--src/WixToolset.Core/Compiler_Dependency.cs385
-rw-r--r--src/WixToolset.Core/Compiler_Module.cs3
-rw-r--r--src/WixToolset.Core/Compiler_Package.cs (renamed from src/WixToolset.Core/Compiler_2.cs)3
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs30
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.en-us.wxl11
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.wxs14
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/PackageComponents.wxs10
-rw-r--r--src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/example.txt1
14 files changed, 668 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index 25a093fd..a3f2da94 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -325,6 +325,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind
325 command.Execute(); 325 command.Execute();
326 } 326 }
327 327
328 // Process dependency references.
329 if (SectionType.Product == section.Type || SectionType.Module == section.Type)
330 {
331 var dependencyRefs = section.Symbols.OfType<WixDependencyRefSymbol>().ToList();
332
333 if (dependencyRefs.Any())
334 {
335 var command = new ProcessDependencyReferencesCommand(this.WindowsInstallerBackendHelper, section, dependencyRefs);
336 command.Execute();
337 }
338 }
339
328 // If there are any backend extensions, give them the opportunity to process 340 // If there are any backend extensions, give them the opportunity to process
329 // the section now that the fields have all be resolved. 341 // the section now that the fields have all be resolved.
330 // 342 //
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessDependencyReferencesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessDependencyReferencesCommand.cs
new file mode 100644
index 00000000..899d06e1
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessDependencyReferencesCommand.cs
@@ -0,0 +1,111 @@
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.WindowsInstaller.Bind
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Symbols;
10 using WixToolset.Extensibility.Services;
11
12 internal class ProcessDependencyReferencesCommand
13 {
14 // The root registry key for the dependency extension. We write to Software\Classes explicitly
15 // based on the current security context instead of HKCR. See
16 // http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information.
17 private const string DependencyRegistryRoot = @"Software\Classes\Installer\Dependencies\";
18 private const string RegistryDependents = "Dependents";
19
20 public ProcessDependencyReferencesCommand(IWindowsInstallerBackendHelper backendHelper, IntermediateSection section, IEnumerable<WixDependencyRefSymbol> dependencyRefSymbols)
21 {
22 this.Section = section;
23 this.DependencyRefSymbols = dependencyRefSymbols;
24 }
25
26 private IntermediateSection Section { get; }
27
28 private IEnumerable<WixDependencyRefSymbol> DependencyRefSymbols { get; }
29
30 public void Execute()
31 {
32 var wixDependencyRows = this.Section.Symbols.OfType<WixDependencySymbol>().ToDictionary(d => d.Id.Id);
33 var wixDependencyProviderRows = this.Section.Symbols.OfType<WixDependencyProviderSymbol>().ToDictionary(d => d.Id.Id);
34
35 // For each relationship, get the provides and requires rows to generate registry values.
36 foreach (var wixDependencyRefRow in this.DependencyRefSymbols)
37 {
38 var providesId = wixDependencyRefRow.WixDependencyProviderRef;
39 var requiresId = wixDependencyRefRow.WixDependencyRef;
40
41 // If we do not find both symbols, skip the registry key generation.
42 if (!wixDependencyRows.TryGetValue(requiresId, out var wixDependencyRow))
43 {
44 continue;
45 }
46
47 if (!wixDependencyProviderRows.TryGetValue(providesId, out var wixDependencyProviderRow))
48 {
49 continue;
50 }
51
52 // Format the root registry key using the required provider key and the current provider key.
53 var requiresKey = wixDependencyRow.Id.Id;
54 var providesKey = wixDependencyRow.ProviderKey;
55 var keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyRegistryRoot, requiresKey, RegistryDependents, providesKey);
56
57 // Get the component ID from the provider.
58 var componentId = wixDependencyProviderRow.ComponentRef;
59
60 var id = Common.GenerateIdentifier("reg", providesId, requiresId, "(Default)");
61 this.Section.AddSymbol(new RegistrySymbol(wixDependencyRefRow.SourceLineNumbers, new Identifier(AccessModifier.Private, id))
62 {
63 ComponentRef = componentId,
64 Root = RegistryRootType.MachineUser,
65 Key = keyRequires,
66 Name = "*",
67 });
68
69 if (!String.IsNullOrEmpty(wixDependencyRow.MinVersion))
70 {
71 id = Common.GenerateIdentifier("reg", providesId, requiresId, "MinVersion");
72 this.Section.AddSymbol(new RegistrySymbol(wixDependencyRefRow.SourceLineNumbers, new Identifier(AccessModifier.Private, id))
73 {
74 ComponentRef = componentId,
75 Root = RegistryRootType.MachineUser,
76 Key = keyRequires,
77 Name = "MinVersion",
78 Value = wixDependencyRow.MinVersion
79 });
80 }
81
82 string maxVersion = (string)wixDependencyRow[3];
83 if (!String.IsNullOrEmpty(wixDependencyRow.MaxVersion))
84 {
85 id = Common.GenerateIdentifier("reg", providesId, requiresId, "MaxVersion");
86 this.Section.AddSymbol(new RegistrySymbol(wixDependencyRefRow.SourceLineNumbers, new Identifier(AccessModifier.Private, id))
87 {
88 ComponentRef = componentId,
89 Root = RegistryRootType.MachineUser,
90 Key = keyRequires,
91 Name = "MaxVersion",
92 Value = wixDependencyRow.MaxVersion
93 });
94 }
95
96 if (wixDependencyRow.Attributes != WixDependencySymbolAttributes.None)
97 {
98 id = Common.GenerateIdentifier("reg", providesId, requiresId, "Attributes");
99 this.Section.AddSymbol(new RegistrySymbol(wixDependencyRefRow.SourceLineNumbers, new Identifier(AccessModifier.Private, id))
100 {
101 ComponentRef = componentId,
102 Root = RegistryRootType.MachineUser,
103 Key = keyRequires,
104 Name = "Attributes",
105 Value = String.Concat("#", (int)wixDependencyRow.Attributes)
106 });
107 }
108 }
109 }
110 }
111}
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs
index 85261cce..ac99a8a1 100644
--- a/src/WixToolset.Core/Compiler.cs
+++ b/src/WixToolset.Core/Compiler.cs
@@ -2366,6 +2366,16 @@ namespace WixToolset.Core
2366 var foundExtension = false; 2366 var foundExtension = false;
2367 this.ParseProgIdElement(child, id.Id, YesNoType.NotSet, null, null, null, ref foundExtension, YesNoType.NotSet); 2367 this.ParseProgIdElement(child, id.Id, YesNoType.NotSet, null, null, null, ref foundExtension, YesNoType.NotSet);
2368 break; 2368 break;
2369 case "Provides":
2370 if (win64)
2371 {
2372 this.Messaging.Write(CompilerWarnings.Win64Component(sourceLineNumbers, id.Id));
2373 }
2374
2375 keyPathSet = this.ParseProvidesElement(child, null, id.Id, out keyPossible);
2376 keyBit = ComponentKeyPathType.Registry;
2377 break;
2378
2369 case "RegistryKey": 2379 case "RegistryKey":
2370 keyPathSet = this.ParseRegistryKeyElement(child, id.Id, null, null, win64, out keyPossible); 2380 keyPathSet = this.ParseRegistryKeyElement(child, id.Id, null, null, win64, out keyPossible);
2371 keyBit = ComponentKeyPathType.Registry; 2381 keyBit = ComponentKeyPathType.Registry;
@@ -6290,6 +6300,9 @@ namespace WixToolset.Core
6290 case "RelatedBundle": 6300 case "RelatedBundle":
6291 this.ParseRelatedBundleElement(child); 6301 this.ParseRelatedBundleElement(child);
6292 break; 6302 break;
6303 case "Requires":
6304 this.ParseRequiresElement(child, null, false);
6305 break;
6293 case "SetDirectory": 6306 case "SetDirectory":
6294 this.ParseSetDirectoryElement(child); 6307 this.ParseSetDirectoryElement(child);
6295 break; 6308 break;
diff --git a/src/WixToolset.Core/CompilerErrors.cs b/src/WixToolset.Core/CompilerErrors.cs
index da64c376..9b3d85b9 100644
--- a/src/WixToolset.Core/CompilerErrors.cs
+++ b/src/WixToolset.Core/CompilerErrors.cs
@@ -6,6 +6,16 @@ namespace WixToolset.Core
6 6
7 internal static class CompilerErrors 7 internal static class CompilerErrors
8 { 8 {
9 public static Message IllegalCharactersInProvider(SourceLineNumber sourceLineNumbers, string attributeName, char illegalChar, string illegalChars)
10 {
11 return Message(sourceLineNumbers, Ids.IllegalCharactersInProvider, "The provider key authored into the {0} attribute contains an illegal character, '{1}'. Please author the provider key without any of the following characters: {2}", attributeName, illegalChar, illegalChars);
12 }
13
14 public static Message ReservedValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string attributeValue)
15 {
16 return Message(sourceLineNumbers, Ids.ReservedValue, "The {0}/@{1} attribute value '{2}' is reserved and cannot be used here. Please choose a different value.", elementName, attributeName, attributeValue);
17 }
18
9 public static Message IllegalName(SourceLineNumber sourceLineNumbers, string parentElement, string name) 19 public static Message IllegalName(SourceLineNumber sourceLineNumbers, string parentElement, string name)
10 { 20 {
11 return Message(sourceLineNumbers, Ids.IllegalName, "The Tag/@Name attribute value, '{1}', contains invalid filename identifiers. The Tag/@Name may have defaulted from the {0}/@Name attrbute. If so, use the Tag/@Name attribute to provide a valid filename. Any character except for the follow may be used: \\ ? | > < : / * \".", parentElement, name); 21 return Message(sourceLineNumbers, Ids.IllegalName, "The Tag/@Name attribute value, '{1}', contains invalid filename identifiers. The Tag/@Name may have defaulted from the {0}/@Name attrbute. If so, use the Tag/@Name attribute to provide a valid filename. Any character except for the follow may be used: \\ ? | > < : / * \".", parentElement, name);
@@ -23,6 +33,9 @@ namespace WixToolset.Core
23 33
24 public enum Ids 34 public enum Ids
25 { 35 {
36 IllegalCharactersInProvider = 5400,
37 ReservedValue = 5401,
38
26 IllegalName = 6601, 39 IllegalName = 6601,
27 ExampleRegid = 6602, 40 ExampleRegid = 6602,
28 } 41 }
diff --git a/src/WixToolset.Core/CompilerWarnings.cs b/src/WixToolset.Core/CompilerWarnings.cs
new file mode 100644
index 00000000..3b9666dd
--- /dev/null
+++ b/src/WixToolset.Core/CompilerWarnings.cs
@@ -0,0 +1,53 @@
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 WixToolset.Data;
6
7 internal static class CompilerWarnings
8 {
9 public static Message DiscouragedVersionAttribute(SourceLineNumber sourceLineNumbers)
10 {
11 return Message(sourceLineNumbers, Ids.DiscouragedVersionAttribute, "The Provides/@Version attribute should not be specified in an MSI package. The ProductVersion will be used by default.");
12 }
13
14 public static Message DiscouragedVersionAttribute(SourceLineNumber sourceLineNumbers, string id)
15 {
16 return Message(sourceLineNumbers, Ids.DiscouragedVersionAttribute, "The Provides/@Version attribute should not be specified for MSI package {0}. The ProductVersion will be used by default.", id);
17 }
18
19 public static Message PropertyRemoved(string name)
20 {
21 return Message(null, Ids.PropertyRemoved, "The property {0} was authored in the package with a value and will be removed. The property should not be authored.", name);
22 }
23
24 public static Message ProvidesKeyNotFound(SourceLineNumber sourceLineNumbers, string id)
25 {
26 return Message(sourceLineNumbers, Ids.ProvidesKeyNotFound, "The provider key with identifier {0} was not found in the WixDependencyProvider table. Related registry rows will not be removed from authoring.", id);
27 }
28
29 public static Message RequiresKeyNotFound(SourceLineNumber sourceLineNumbers, string id)
30 {
31 return Message(sourceLineNumbers, Ids.RequiresKeyNotFound, "The dependency key with identifier {0} was not found in the WixDependency table. Related registry rows will not be removed from authoring.", id);
32 }
33
34 public static Message Win64Component(SourceLineNumber sourceLineNumbers, string componentId)
35 {
36 return Message(sourceLineNumbers, Ids.Win64Component, "The Provides element should not be authored in the 64-bit component with identifier {0}. The dependency feature may not work if installing this package on 64-bit Windows operating systems prior to Windows 7 and Windows Server 2008 R2. Set the Component/@Bitness attribute to \"always32\" to ensure the dependency feature works correctly on legacy operating systems.", componentId);
37 }
38
39 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
40 {
41 return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, format, args);
42 }
43
44 public enum Ids
45 {
46 ProvidesKeyNotFound = 5431,
47 RequiresKeyNotFound = 5432,
48 PropertyRemoved = 5433,
49 DiscouragedVersionAttribute = 5434,
50 Win64Component = 5435,
51 }
52 }
53}
diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs
index 1ee09166..b8c7b7b1 100644
--- a/src/WixToolset.Core/Compiler_Bundle.cs
+++ b/src/WixToolset.Core/Compiler_Bundle.cs
@@ -196,6 +196,9 @@ namespace WixToolset.Core
196 case "ParentName": 196 case "ParentName":
197 parentName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 197 parentName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
198 break; 198 break;
199 case "ProviderKey":
200 this.ParseBundleProviderKeyAttribute(sourceLineNumbers, node, attrib);
201 break;
199 case "SplashScreenSourceFile": 202 case "SplashScreenSourceFile":
200 splashScreenSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 203 splashScreenSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
201 break; 204 break;
@@ -340,6 +343,9 @@ namespace WixToolset.Core
340 case "RelatedBundle": 343 case "RelatedBundle":
341 this.ParseRelatedBundleElement(child); 344 this.ParseRelatedBundleElement(child);
342 break; 345 break;
346 case "Requires":
347 this.ParseRequiresElement(child, null, false);
348 break;
343 case "SetVariable": 349 case "SetVariable":
344 this.ParseSetVariableElement(child); 350 this.ParseSetVariableElement(child);
345 break; 351 break;
@@ -2386,6 +2392,9 @@ namespace WixToolset.Core
2386 case "PayloadGroupRef": 2392 case "PayloadGroupRef":
2387 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id, ComplexReferenceChildType.Unknown, null); 2393 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id, ComplexReferenceChildType.Unknown, null);
2388 break; 2394 break;
2395 case "Provides":
2396 this.ParseProvidesElement(child, packageType, id.Id, out _);
2397 break;
2389 case "ExitCode": 2398 case "ExitCode":
2390 allowed = (packageType == WixBundlePackageType.Exe); 2399 allowed = (packageType == WixBundlePackageType.Exe);
2391 if (allowed) 2400 if (allowed)
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}
diff --git a/src/WixToolset.Core/Compiler_Module.cs b/src/WixToolset.Core/Compiler_Module.cs
index d730ae5d..6953467f 100644
--- a/src/WixToolset.Core/Compiler_Module.cs
+++ b/src/WixToolset.Core/Compiler_Module.cs
@@ -190,6 +190,9 @@ namespace WixToolset.Core
190 case "PropertyRef": 190 case "PropertyRef":
191 this.ParseSimpleRefElement(child, SymbolDefinitions.Property); 191 this.ParseSimpleRefElement(child, SymbolDefinitions.Property);
192 break; 192 break;
193 case "Requires":
194 this.ParseRequiresElement(child, null, false);
195 break;
193 case "SetDirectory": 196 case "SetDirectory":
194 this.ParseSetDirectoryElement(child); 197 this.ParseSetDirectoryElement(child);
195 break; 198 break;
diff --git a/src/WixToolset.Core/Compiler_2.cs b/src/WixToolset.Core/Compiler_Package.cs
index 295392c8..d2728e9c 100644
--- a/src/WixToolset.Core/Compiler_2.cs
+++ b/src/WixToolset.Core/Compiler_Package.cs
@@ -306,6 +306,9 @@ namespace WixToolset.Core
306 case "PropertyRef": 306 case "PropertyRef":
307 this.ParseSimpleRefElement(child, SymbolDefinitions.Property); 307 this.ParseSimpleRefElement(child, SymbolDefinitions.Property);
308 break; 308 break;
309 case "Requires":
310 this.ParseRequiresElement(child, null, false);
311 break;
309 case "SetDirectory": 312 case "SetDirectory":
310 this.ParseSetDirectoryElement(child); 313 this.ParseSetDirectoryElement(child);
311 break; 314 break;
diff --git a/src/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs b/src/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs
new file mode 100644
index 00000000..14eb8ff7
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs
@@ -0,0 +1,30 @@
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 WixToolsetTest.CoreIntegration
4{
5 using WixBuildTools.TestSupport;
6 using WixToolset.Core.TestPackage;
7 using Xunit;
8
9 public class DependencyExtensionFixture
10 {
11 [Fact]
12 public void CanBuildUsingProvides()
13 {
14 var folder = TestData.Get(@"TestData\UsingProvides");
15 var build = new Builder(folder, null, new[] { folder });
16
17 var results = build.BuildAndQuery(Build, "WixDependencyProvider");
18 Assert.Equal(new[]
19 {
20 "WixDependencyProvider:dep74OfIcniaqxA7EprRGBw4Oyy3r8\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tUsingProvides\t\t\t",
21 }, results);
22 }
23
24 private static void Build(string[] args)
25 {
26 var result = WixRunner.Execute(args)
27 .AssertSuccess();
28 }
29 }
30}
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.en-us.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.en-us.wxl
new file mode 100644
index 00000000..38c12ac1
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.en-us.wxl
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="utf-8"?>
2
3<!--
4This file contains the declaration of all the localizable strings.
5-->
6<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
7
8 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
9 <String Id="FeatureTitle">MsiPackage</String>
10
11</WixLocalization>
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.wxs
new file mode 100644
index 00000000..07da1215
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.wxs
@@ -0,0 +1,14 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3
4 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
5
6 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
7 <ComponentGroupRef Id="ProductComponents" />
8 </Feature>
9 </Package>
10
11 <Fragment>
12 <Directory Id="INSTALLFOLDER" Name="ProgramFilesFolder:\MsiPackage" />
13 </Fragment>
14</Wix>
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/PackageComponents.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/PackageComponents.wxs
new file mode 100644
index 00000000..7e459e9a
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/PackageComponents.wxs
@@ -0,0 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Fragment>
3 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Source="example.txt" />
6 <Provides Key="UsingProvides" />
7 </Component>
8 </ComponentGroup>
9 </Fragment>
10</Wix>
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/example.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/example.txt
new file mode 100644
index 00000000..1b4ffe8a
--- /dev/null
+++ b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/example.txt
@@ -0,0 +1 @@
This is example.txt. \ No newline at end of file