aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Bal/wixext
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Bal/wixext')
-rw-r--r--src/ext/Bal/wixext/BalBurnBackendExtension.cs294
-rw-r--r--src/ext/Bal/wixext/BalCompiler.cs269
-rw-r--r--src/ext/Bal/wixext/BalErrors.cs48
-rw-r--r--src/ext/Bal/wixext/BalWarnings.cs24
-rw-r--r--src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs17
5 files changed, 573 insertions, 79 deletions
diff --git a/src/ext/Bal/wixext/BalBurnBackendExtension.cs b/src/ext/Bal/wixext/BalBurnBackendExtension.cs
index d34c159a..6f615796 100644
--- a/src/ext/Bal/wixext/BalBurnBackendExtension.cs
+++ b/src/ext/Bal/wixext/BalBurnBackendExtension.cs
@@ -5,6 +5,8 @@ namespace WixToolset.Bal
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq; 7 using System.Linq;
8 using System.Text;
9 using System.Xml;
8 using WixToolset.Bal.Symbols; 10 using WixToolset.Bal.Symbols;
9 using WixToolset.Data; 11 using WixToolset.Data;
10 using WixToolset.Data.Burn; 12 using WixToolset.Data.Burn;
@@ -29,12 +31,48 @@ namespace WixToolset.Bal
29 31
30 protected override IReadOnlyCollection<IntermediateSymbolDefinition> SymbolDefinitions => BurnSymbolDefinitions; 32 protected override IReadOnlyCollection<IntermediateSymbolDefinition> SymbolDefinitions => BurnSymbolDefinitions;
31 33
34 public override bool TryProcessSymbol(IntermediateSection section, IntermediateSymbol symbol)
35 {
36 if (symbol is WixBalPackageInfoSymbol balPackageInfoSymbol)
37 {
38 // There might be a more efficient way to do this,
39 // but this is an easy way to ensure we're creating valid XML.
40 var sb = new StringBuilder();
41 using (var writer = XmlWriter.Create(sb))
42 {
43 writer.WriteStartElement(symbol.Definition.Name, BurnConstants.BootstrapperApplicationDataNamespace);
44
45 writer.WriteAttributeString("PackageId", balPackageInfoSymbol.PackageId);
46
47 if (balPackageInfoSymbol.DisplayInternalUICondition != null)
48 {
49 writer.WriteAttributeString("DisplayInternalUICondition", balPackageInfoSymbol.DisplayInternalUICondition);
50 }
51
52 if (balPackageInfoSymbol.PrimaryPackageType != BalPrimaryPackageType.None)
53 {
54 writer.WriteAttributeString("PrimaryPackageType", balPackageInfoSymbol.PrimaryPackageType.ToString().ToLower());
55 }
56
57 writer.WriteEndElement();
58 }
59
60 this.BackendHelper.AddBootstrapperApplicationData(sb.ToString());
61
62 return true;
63 }
64 else
65 {
66 return base.TryProcessSymbol(section, symbol);
67 }
68 }
69
32 public override void SymbolsFinalized(IntermediateSection section) 70 public override void SymbolsFinalized(IntermediateSection section)
33 { 71 {
34 base.SymbolsFinalized(section); 72 base.SymbolsFinalized(section);
35 73
36 this.VerifyBalConditions(section); 74 this.VerifyBalConditions(section);
37 this.VerifyBalPackageInfos(section); 75 this.VerifyDisplayInternalUICondition(section);
38 this.VerifyOverridableVariables(section); 76 this.VerifyOverridableVariables(section);
39 77
40 var baSymbol = section.Symbols.OfType<WixBootstrapperApplicationDllSymbol>().SingleOrDefault(); 78 var baSymbol = section.Symbols.OfType<WixBootstrapperApplicationDllSymbol>().SingleOrDefault();
@@ -44,24 +82,31 @@ namespace WixToolset.Bal
44 return; 82 return;
45 } 83 }
46 84
85 var isIuiBA = baId.StartsWith("WixInternalUIBootstrapperApplication");
47 var isStdBA = baId.StartsWith("WixStandardBootstrapperApplication"); 86 var isStdBA = baId.StartsWith("WixStandardBootstrapperApplication");
48 var isMBA = baId.StartsWith("WixManagedBootstrapperApplicationHost"); 87 var isMBA = baId.StartsWith("WixManagedBootstrapperApplicationHost");
49 var isDNC = baId.StartsWith("WixDotNetCoreBootstrapperApplicationHost"); 88 var isDNC = baId.StartsWith("WixDotNetCoreBootstrapperApplicationHost");
50 var isSCD = isDNC && this.VerifySCD(section); 89 var isSCD = isDNC && this.VerifySCD(section);
51 90
91 if (isIuiBA)
92 {
93 // This needs to happen before VerifyPrereqPackages because it can add prereq packages.
94 this.VerifyPrimaryPackages(section);
95 }
96
52 if (isDNC) 97 if (isDNC)
53 { 98 {
54 this.FinalizeBAFactorySymbol(section); 99 this.FinalizeBAFactorySymbol(section);
55 } 100 }
56 101
57 if (isStdBA || isMBA || isDNC) 102 if (isIuiBA || isStdBA || isMBA || isDNC)
58 { 103 {
59 this.VerifyBAFunctions(section); 104 this.VerifyBAFunctions(section);
60 } 105 }
61 106
62 if (isMBA || (isDNC && !isSCD)) 107 if (isIuiBA || isMBA || (isDNC && !isSCD))
63 { 108 {
64 this.VerifyPrereqPackages(section, isDNC); 109 this.VerifyPrereqPackages(section, isDNC, isIuiBA);
65 } 110 }
66 } 111 }
67 112
@@ -133,12 +178,241 @@ namespace WixToolset.Bal
133 } 178 }
134 } 179 }
135 180
136 private void VerifyBalPackageInfos(IntermediateSection section) 181 private void VerifyDisplayInternalUICondition(IntermediateSection section)
182 {
183 foreach (var balPackageInfoSymbol in section.Symbols.OfType<WixBalPackageInfoSymbol>().ToList())
184 {
185 if (balPackageInfoSymbol.DisplayInternalUICondition != null)
186 {
187 this.BackendHelper.ValidateBundleCondition(balPackageInfoSymbol.SourceLineNumbers, "*Package", "bal:DisplayInternalUICondition", balPackageInfoSymbol.DisplayInternalUICondition, BundleConditionPhase.Plan);
188 }
189 }
190 }
191
192 private void VerifyPrimaryPackages(IntermediateSection section)
193 {
194 WixBalPackageInfoSymbol defaultPrimaryPackage = null;
195 WixBalPackageInfoSymbol x86PrimaryPackage = null;
196 WixBalPackageInfoSymbol x64PrimaryPackage = null;
197 WixBalPackageInfoSymbol arm64PrimaryPackage = null;
198 var nonPermanentNonPrimaryPackages = new List<WixBundlePackageSymbol>();
199
200 var balPackageInfoSymbolsByPackageId = section.Symbols.OfType<WixBalPackageInfoSymbol>().ToDictionary(x => x.PackageId);
201 var mbaPrereqInfoSymbolsByPackageId = section.Symbols.OfType<WixMbaPrereqInformationSymbol>().ToDictionary(x => x.PackageId);
202 var msiPackageSymbolsByPackageId = section.Symbols.OfType<WixBundleMsiPackageSymbol>().ToDictionary(x => x.Id.Id);
203 var packageSymbols = section.Symbols.OfType<WixBundlePackageSymbol>().ToList();
204 foreach (var packageSymbol in packageSymbols)
205 {
206 var packageId = packageSymbol.Id?.Id;
207 var isPrereq = false;
208 var primaryPackageType = BalPrimaryPackageType.None;
209
210 if (mbaPrereqInfoSymbolsByPackageId.TryGetValue(packageId, out var _))
211 {
212 isPrereq = true;
213 }
214
215 if (balPackageInfoSymbolsByPackageId.TryGetValue(packageId, out var balPackageInfoSymbol))
216 {
217 primaryPackageType = balPackageInfoSymbol.PrimaryPackageType;
218 }
219
220 if (packageSymbol.Permanent)
221 {
222 if (primaryPackageType != BalPrimaryPackageType.None)
223 {
224 this.Messaging.Write(BalErrors.IuibaPermanentPrimaryPackageType(packageSymbol.SourceLineNumbers));
225 }
226 else
227 {
228 if (!isPrereq)
229 {
230 var prereqInfoSymbol = section.AddSymbol(new WixMbaPrereqInformationSymbol(packageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Global, packageId))
231 {
232 PackageId = packageId,
233 });
234
235 mbaPrereqInfoSymbolsByPackageId.Add(packageId, prereqInfoSymbol);
236 }
237
238 this.VerifyIuibaPrereqPackage(packageSymbol);
239 }
240 }
241 else
242 {
243 if (isPrereq)
244 {
245 if (primaryPackageType == BalPrimaryPackageType.None)
246 {
247 this.Messaging.Write(BalErrors.IuibaNonPermanentPrereqPackage(packageSymbol.SourceLineNumbers));
248 }
249 else
250 {
251 this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(
252 packageSymbol.SourceLineNumbers,
253 packageSymbol.Type + "Package",
254 "PrereqPackage",
255 "yes",
256 "PrimaryPackageType"));
257 }
258 }
259 else if (primaryPackageType == BalPrimaryPackageType.None)
260 {
261 nonPermanentNonPrimaryPackages.Add(packageSymbol);
262 }
263 else if (packageSymbol.Type != WixBundlePackageType.Msi)
264 {
265 this.Messaging.Write(BalErrors.IuibaNonMsiPrimaryPackage(packageSymbol.SourceLineNumbers));
266 }
267 else if (!msiPackageSymbolsByPackageId.TryGetValue(packageId, out var msiPackageSymbol))
268 {
269 throw new WixException($"Missing WixBundleMsiPackageSymbol for package '{packageId}'");
270 }
271 else if (msiPackageSymbol.EnableFeatureSelection)
272 {
273 this.Messaging.Write(BalErrors.IuibaPrimaryPackageEnableFeatureSelection(packageSymbol.SourceLineNumbers));
274 }
275 else
276 {
277 if (primaryPackageType == BalPrimaryPackageType.Default)
278 {
279 if (defaultPrimaryPackage == null)
280 {
281 defaultPrimaryPackage = balPackageInfoSymbol;
282 }
283 else
284 {
285 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType(balPackageInfoSymbol.SourceLineNumbers, "default"));
286 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType2(defaultPrimaryPackage.SourceLineNumbers));
287 }
288 }
289 else if (balPackageInfoSymbol.PrimaryPackageType == BalPrimaryPackageType.X86)
290 {
291 if (x86PrimaryPackage == null)
292 {
293 x86PrimaryPackage = balPackageInfoSymbol;
294 }
295 else
296 {
297 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType(balPackageInfoSymbol.SourceLineNumbers, "x86"));
298 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType2(x86PrimaryPackage.SourceLineNumbers));
299 }
300 }
301 else if (balPackageInfoSymbol.PrimaryPackageType == BalPrimaryPackageType.X64)
302 {
303 if (x64PrimaryPackage == null)
304 {
305 x64PrimaryPackage = balPackageInfoSymbol;
306 }
307 else
308 {
309 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType(balPackageInfoSymbol.SourceLineNumbers, "x64"));
310 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType2(x64PrimaryPackage.SourceLineNumbers));
311 }
312 }
313 else if (balPackageInfoSymbol.PrimaryPackageType == BalPrimaryPackageType.ARM64)
314 {
315 if (arm64PrimaryPackage == null)
316 {
317 arm64PrimaryPackage = balPackageInfoSymbol;
318 }
319 else
320 {
321 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType(balPackageInfoSymbol.SourceLineNumbers, "arm64"));
322 this.Messaging.Write(BalErrors.MultiplePrimaryPackageType2(arm64PrimaryPackage.SourceLineNumbers));
323 }
324 }
325 else
326 {
327 throw new NotImplementedException();
328 }
329
330 this.VerifyIuibaPrimaryPackage(packageSymbol, balPackageInfoSymbol);
331 }
332 }
333 }
334
335 if (defaultPrimaryPackage == null && nonPermanentNonPrimaryPackages.Count == 1)
336 {
337 var packageSymbol = nonPermanentNonPrimaryPackages[0];
338
339 if (packageSymbol.Type == WixBundlePackageType.Msi)
340 {
341 var packageId = packageSymbol.Id?.Id;
342 var msiPackageSymbol = section.Symbols.OfType<WixBundleMsiPackageSymbol>()
343 .SingleOrDefault(x => x.Id.Id == packageId);
344 if (!msiPackageSymbol.EnableFeatureSelection)
345 {
346 if (!balPackageInfoSymbolsByPackageId.TryGetValue(packageId, out var balPackageInfoSymbol))
347 {
348 balPackageInfoSymbol = section.AddSymbol(new WixBalPackageInfoSymbol(packageSymbol.SourceLineNumbers, new Identifier(AccessModifier.Global, packageId))
349 {
350 PackageId = packageId,
351 });
352
353 balPackageInfoSymbolsByPackageId.Add(packageId, balPackageInfoSymbol);
354 }
355
356 balPackageInfoSymbol.PrimaryPackageType = BalPrimaryPackageType.Default;
357 defaultPrimaryPackage = balPackageInfoSymbol;
358 nonPermanentNonPrimaryPackages.RemoveAt(0);
359
360 this.VerifyIuibaPrimaryPackage(packageSymbol, balPackageInfoSymbol);
361 }
362 }
363 }
364
365 if (nonPermanentNonPrimaryPackages.Count > 0)
366 {
367 foreach (var packageSymbol in nonPermanentNonPrimaryPackages)
368 {
369 this.Messaging.Write(BalErrors.IuibaNonPermanentNonPrimaryPackage(packageSymbol.SourceLineNumbers));
370 }
371 }
372 else if (defaultPrimaryPackage == null)
373 {
374 this.Messaging.Write(BalErrors.MissingIUIPrimaryPackage());
375 }
376 else
377 {
378 var foundPrimaryPackage = false;
379 var chainPackageGroupSymbols = section.Symbols.OfType<WixGroupSymbol>()
380 .Where(x => x.ChildType == ComplexReferenceChildType.Package &&
381 x.ParentType == ComplexReferenceParentType.PackageGroup &&
382 x.ParentId == BurnConstants.BundleChainPackageGroupId);
383 foreach (var chainPackageGroupSymbol in chainPackageGroupSymbols)
384 {
385 var packageId = chainPackageGroupSymbol.ChildId;
386 if (balPackageInfoSymbolsByPackageId.TryGetValue(packageId, out var balPackageInfo) && balPackageInfo.PrimaryPackageType != BalPrimaryPackageType.None)
387 {
388 foundPrimaryPackage = true;
389 }
390 else if (foundPrimaryPackage && mbaPrereqInfoSymbolsByPackageId.TryGetValue(packageId, out var mbaPrereqInformationSymbol))
391 {
392 this.Messaging.Write(BalWarnings.IuibaPrereqPackageAfterPrimaryPackage(chainPackageGroupSymbol.SourceLineNumbers));
393 }
394 }
395 }
396 }
397
398 private void VerifyIuibaPrereqPackage(WixBundlePackageSymbol packageSymbol)
399 {
400 if (packageSymbol.Cache == BundleCacheType.Force)
401 {
402 this.Messaging.Write(BalWarnings.IuibaForceCachePrereq(packageSymbol.SourceLineNumbers));
403 }
404 }
405
406 private void VerifyIuibaPrimaryPackage(WixBundlePackageSymbol packageSymbol, WixBalPackageInfoSymbol balPackageInfoSymbol)
137 { 407 {
138 var balPackageInfoSymbols = section.Symbols.OfType<WixBalPackageInfoSymbol>().ToList(); 408 if (packageSymbol.InstallCondition != null)
139 foreach (var balPackageInfoSymbol in balPackageInfoSymbols) 409 {
410 this.Messaging.Write(BalWarnings.IuibaPrimaryPackageInstallCondition(packageSymbol.SourceLineNumbers));
411 }
412
413 if (balPackageInfoSymbol.DisplayInternalUICondition != null)
140 { 414 {
141 this.BackendHelper.ValidateBundleCondition(balPackageInfoSymbol.SourceLineNumbers, "*Package", "bal:DisplayInternalUICondition", balPackageInfoSymbol.DisplayInternalUICondition, BundleConditionPhase.Plan); 415 this.Messaging.Write(BalWarnings.IuibaPrimaryPackageDisplayInternalUICondition(packageSymbol.SourceLineNumbers));
142 } 416 }
143 } 417 }
144 418
@@ -161,10 +435,10 @@ namespace WixToolset.Bal
161 } 435 }
162 } 436 }
163 437
164 private void VerifyPrereqPackages(IntermediateSection section, bool isDNC) 438 private void VerifyPrereqPackages(IntermediateSection section, bool isDNC, bool isIuiBA)
165 { 439 {
166 var prereqInfoSymbols = section.Symbols.OfType<WixMbaPrereqInformationSymbol>().ToList(); 440 var prereqInfoSymbols = section.Symbols.OfType<WixMbaPrereqInformationSymbol>().ToList();
167 if (prereqInfoSymbols.Count == 0) 441 if (!isIuiBA && prereqInfoSymbols.Count == 0)
168 { 442 {
169 var message = isDNC ? BalErrors.MissingDNCPrereq() : BalErrors.MissingMBAPrereq(); 443 var message = isDNC ? BalErrors.MissingDNCPrereq() : BalErrors.MissingMBAPrereq();
170 this.Messaging.Write(message); 444 this.Messaging.Write(message);
diff --git a/src/ext/Bal/wixext/BalCompiler.cs b/src/ext/Bal/wixext/BalCompiler.cs
index 1721f252..bc2ba861 100644
--- a/src/ext/Bal/wixext/BalCompiler.cs
+++ b/src/ext/Bal/wixext/BalCompiler.cs
@@ -16,7 +16,8 @@ namespace WixToolset.Bal
16 /// </summary> 16 /// </summary>
17 public sealed class BalCompiler : BaseCompilerExtension 17 public sealed class BalCompiler : BaseCompilerExtension
18 { 18 {
19 private readonly Dictionary<string, WixMbaPrereqInformationSymbol> prereqInfoSymbolsByPackageId; 19 private readonly Dictionary<string, WixBalPackageInfoSymbol> packageInfoSymbolsByPackageId = new Dictionary<string, WixBalPackageInfoSymbol>();
20 private readonly Dictionary<string, WixMbaPrereqInformationSymbol> prereqInfoSymbolsByPackageId = new Dictionary<string, WixMbaPrereqInformationSymbol>();
20 21
21 private enum WixDotNetCoreBootstrapperApplicationHostTheme 22 private enum WixDotNetCoreBootstrapperApplicationHostTheme
22 { 23 {
@@ -32,6 +33,13 @@ namespace WixToolset.Bal
32 Standard, 33 Standard,
33 } 34 }
34 35
36 private enum WixInternalUIBootstrapperApplicationTheme
37 {
38 Unknown,
39 None,
40 Standard,
41 }
42
35 private enum WixStandardBootstrapperApplicationTheme 43 private enum WixStandardBootstrapperApplicationTheme
36 { 44 {
37 Unknown, 45 Unknown,
@@ -43,14 +51,6 @@ namespace WixToolset.Bal
43 RtfLicense, 51 RtfLicense,
44 } 52 }
45 53
46 /// <summary>
47 /// Instantiate a new BalCompiler.
48 /// </summary>
49 public BalCompiler()
50 {
51 this.prereqInfoSymbolsByPackageId = new Dictionary<string, WixMbaPrereqInformationSymbol>();
52 }
53
54 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/bal"; 54 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/bal";
55 55
56 /// <summary> 56 /// <summary>
@@ -83,6 +83,9 @@ namespace WixToolset.Bal
83 case "BootstrapperApplication": 83 case "BootstrapperApplication":
84 switch (element.Name.LocalName) 84 switch (element.Name.LocalName)
85 { 85 {
86 case "WixInternalUIBootstrapperApplication":
87 this.ParseWixInternalUIBootstrapperApplicationElement(intermediate, section, element);
88 break;
86 case "WixStandardBootstrapperApplication": 89 case "WixStandardBootstrapperApplication":
87 this.ParseWixStandardBootstrapperApplicationElement(intermediate, section, element); 90 this.ParseWixStandardBootstrapperApplicationElement(intermediate, section, element);
88 break; 91 break;
@@ -113,7 +116,6 @@ namespace WixToolset.Bal
113 public override void ParseAttribute(Intermediate intermediate, IntermediateSection section, XElement parentElement, XAttribute attribute, IDictionary<string, string> context) 116 public override void ParseAttribute(Intermediate intermediate, IntermediateSection section, XElement parentElement, XAttribute attribute, IDictionary<string, string> context)
114 { 117 {
115 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(parentElement); 118 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(parentElement);
116 WixMbaPrereqInformationSymbol prereqInfo;
117 119
118 switch (parentElement.Name.LocalName) 120 switch (parentElement.Name.LocalName)
119 { 121 {
@@ -137,42 +139,63 @@ namespace WixToolset.Bal
137 case "MsiPackage": 139 case "MsiPackage":
138 case "MspPackage": 140 case "MspPackage":
139 var displayInternalUICondition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); 141 var displayInternalUICondition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
140 section.AddSymbol(new WixBalPackageInfoSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, packageId)) 142 var packageInfo = this.GetBalPackageInfoSymbol(section, sourceLineNumbers, packageId);
141 { 143 packageInfo.DisplayInternalUICondition = displayInternalUICondition;
142 PackageId = packageId,
143 DisplayInternalUICondition = displayInternalUICondition,
144 });
145 break; 144 break;
146 default: 145 default:
147 this.ParseHelper.UnexpectedAttribute(parentElement, attribute); 146 this.ParseHelper.UnexpectedAttribute(parentElement, attribute);
148 break; 147 break;
149 } 148 }
150 break; 149 break;
151 case "PrereqLicenseFile": 150 case "PrimaryPackageType":
152 151 {
153 if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out prereqInfo)) 152 var primaryPackageType = BalPrimaryPackageType.None;
153 var primaryPackageTypeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
154 switch (primaryPackageTypeValue)
154 { 155 {
155 // at the time the extension attribute is parsed, the compiler might not yet have 156 case "default":
156 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element. 157 primaryPackageType = BalPrimaryPackageType.Default;
157 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage"); 158 break;
158 159 case "x86":
159 if (null != prereqPackage && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, prereqPackage)) 160 primaryPackageType = BalPrimaryPackageType.X86;
160 { 161 break;
161 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers) 162 case "x64":
162 { 163 primaryPackageType = BalPrimaryPackageType.X64;
163 PackageId = packageId, 164 break;
164 }); 165 case "arm64":
165 166 primaryPackageType = BalPrimaryPackageType.ARM64;
166 this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo); 167 break;
167 } 168 default:
168 else 169 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, parentElement.Name.LocalName, "PrimaryPackageType", primaryPackageTypeValue, "default", "x86", "x64", "arm64"));
169 {
170 this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile"));
171 break; 170 break;
172 }
173 } 171 }
174 172
175 if (null != prereqInfo.LicenseUrl) 173 // at the time the extension attribute is parsed, the compiler might not yet have
174 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element.
175 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage");
176 var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId);
177 if (prereqInfo != null)
178 {
179 this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqPackage", "yes", "PrimaryPackageType"));
180 }
181 else
182 {
183 var packageInfo = this.GetBalPackageInfoSymbol(section, sourceLineNumbers, packageId);
184 packageInfo.PrimaryPackageType = primaryPackageType;
185 }
186 break;
187 }
188 case "PrereqLicenseFile":
189 {
190 // at the time the extension attribute is parsed, the compiler might not yet have
191 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element.
192 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage");
193 var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId);
194 if (prereqInfo == null)
195 {
196 this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile"));
197 }
198 else if (null != prereqInfo.LicenseUrl)
176 { 199 {
177 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile", "PrereqLicenseUrl")); 200 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile", "PrereqLicenseUrl"));
178 } 201 }
@@ -181,31 +204,19 @@ namespace WixToolset.Bal
181 prereqInfo.LicenseFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); 204 prereqInfo.LicenseFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
182 } 205 }
183 break; 206 break;
207 }
184 case "PrereqLicenseUrl": 208 case "PrereqLicenseUrl":
209 {
210 // at the time the extension attribute is parsed, the compiler might not yet have
211 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element.
212 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage");
213 var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId);
185 214
186 if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out prereqInfo)) 215 if (prereqInfo == null)
187 { 216 {
188 // at the time the extension attribute is parsed, the compiler might not yet have 217 this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl"));
189 // parsed the PrereqPackage attribute, so we need to get it directly from the parent element.
190 var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage");
191
192 if (null != prereqPackage && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, prereqPackage))
193 {
194 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers)
195 {
196 PackageId = packageId,
197 });
198
199 this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo);
200 }
201 else
202 {
203 this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl"));
204 break;
205 }
206 } 218 }
207 219 else if (null != prereqInfo.LicenseFile)
208 if (null != prereqInfo.LicenseFile)
209 { 220 {
210 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl", "PrereqLicenseFile")); 221 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl", "PrereqLicenseFile"));
211 } 222 }
@@ -214,19 +225,9 @@ namespace WixToolset.Bal
214 prereqInfo.LicenseUrl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); 225 prereqInfo.LicenseUrl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
215 } 226 }
216 break; 227 break;
228 }
217 case "PrereqPackage": 229 case "PrereqPackage":
218 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute)) 230 this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, attribute, packageId);
219 {
220 if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out _))
221 {
222 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers)
223 {
224 PackageId = packageId,
225 });
226
227 this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo);
228 }
229 }
230 break; 231 break;
231 default: 232 default:
232 this.ParseHelper.UnexpectedAttribute(parentElement, attribute); 233 this.ParseHelper.UnexpectedAttribute(parentElement, attribute);
@@ -300,6 +301,41 @@ namespace WixToolset.Bal
300 } 301 }
301 } 302 }
302 303
304 private WixBalPackageInfoSymbol GetBalPackageInfoSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, string packageId)
305 {
306 if (!this.packageInfoSymbolsByPackageId.TryGetValue(packageId, out var packageInfo))
307 {
308 packageInfo = section.AddSymbol(new WixBalPackageInfoSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, packageId))
309 {
310 PackageId = packageId,
311 });
312
313 this.packageInfoSymbolsByPackageId.Add(packageId, packageInfo);
314 }
315
316 return packageInfo;
317 }
318
319 private WixMbaPrereqInformationSymbol GetMbaPrereqInformationSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute prereqAttribute, string packageId)
320 {
321 WixMbaPrereqInformationSymbol prereqInfo = null;
322
323 if (prereqAttribute != null && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, prereqAttribute))
324 {
325 if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out _))
326 {
327 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, packageId))
328 {
329 PackageId = packageId,
330 });
331
332 this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo);
333 }
334 }
335
336 return prereqInfo;
337 }
338
303 /// <summary> 339 /// <summary>
304 /// Parses a Condition element for Bundles. 340 /// Parses a Condition element for Bundles.
305 /// </summary> 341 /// </summary>
@@ -418,6 +454,101 @@ namespace WixToolset.Bal
418 } 454 }
419 } 455 }
420 456
457 private void ParseWixInternalUIBootstrapperApplicationElement(Intermediate intermediate, IntermediateSection section, XElement node)
458 {
459 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
460 WixInternalUIBootstrapperApplicationTheme? theme = null;
461 string themeFile = null;
462 string logoFile = null;
463 string localizationFile = null;
464
465 foreach (var attrib in node.Attributes())
466 {
467 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
468 {
469 switch (attrib.Name.LocalName)
470 {
471 case "LogoFile":
472 logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
473 break;
474 case "ThemeFile":
475 themeFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
476 break;
477 case "LocalizationFile":
478 localizationFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
479 break;
480 case "Theme":
481 var themeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
482 switch (themeValue)
483 {
484 case "none":
485 theme = WixInternalUIBootstrapperApplicationTheme.None;
486 break;
487 case "standard":
488 theme = WixInternalUIBootstrapperApplicationTheme.Standard;
489 break;
490 default:
491 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Theme", themeValue, "none", "standard"));
492 theme = WixInternalUIBootstrapperApplicationTheme.Unknown;
493 break;
494 }
495 break;
496 default:
497 this.ParseHelper.UnexpectedAttribute(node, attrib);
498 break;
499 }
500 }
501 else
502 {
503 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
504 }
505 }
506
507 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
508
509 if (!theme.HasValue)
510 {
511 theme = WixInternalUIBootstrapperApplicationTheme.Standard;
512 }
513
514 if (!this.Messaging.EncounteredError)
515 {
516 if (!String.IsNullOrEmpty(logoFile))
517 {
518 section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaLogo"))
519 {
520 Value = logoFile,
521 });
522 }
523
524 if (!String.IsNullOrEmpty(themeFile))
525 {
526 section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaThemeXml"))
527 {
528 Value = themeFile,
529 });
530 }
531
532 if (!String.IsNullOrEmpty(localizationFile))
533 {
534 section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaThemeWxl"))
535 {
536 Value = localizationFile,
537 });
538 }
539
540 var baId = "WixInternalUIBootstrapperApplication";
541 switch (theme)
542 {
543 case WixInternalUIBootstrapperApplicationTheme.Standard:
544 baId = "WixInternalUIBootstrapperApplication.Standard";
545 break;
546 }
547
548 this.CreateBARef(section, sourceLineNumbers, node, baId);
549 }
550 }
551
421 /// <summary> 552 /// <summary>
422 /// Parses a WixStandardBootstrapperApplication element for Bundles. 553 /// Parses a WixStandardBootstrapperApplication element for Bundles.
423 /// </summary> 554 /// </summary>
diff --git a/src/ext/Bal/wixext/BalErrors.cs b/src/ext/Bal/wixext/BalErrors.cs
index e9f68b24..a7a00a4b 100644
--- a/src/ext/Bal/wixext/BalErrors.cs
+++ b/src/ext/Bal/wixext/BalErrors.cs
@@ -18,11 +18,41 @@ namespace WixToolset.Bal
18 return Message(sourceLineNumbers, Ids.BAFunctionsPayloadRequiredInUXContainer, "The BAFunctions DLL Payload element must be located inside the BootstrapperApplication container."); 18 return Message(sourceLineNumbers, Ids.BAFunctionsPayloadRequiredInUXContainer, "The BAFunctions DLL Payload element must be located inside the BootstrapperApplication container.");
19 } 19 }
20 20
21 public static Message IuibaNonMsiPrimaryPackage(SourceLineNumber sourceLineNumbers)
22 {
23 return Message(sourceLineNumbers, Ids.IuibaNonMsiPrimaryPackage, "When using WixInternalUIBootstrapperApplication, each primary package must be an MsiPackage.");
24 }
25
26 public static Message IuibaNonPermanentNonPrimaryPackage(SourceLineNumber sourceLineNumbers)
27 {
28 return Message(sourceLineNumbers, Ids.IuibaNonPermanentNonPrimaryPackage, "When using WixInternalUIBootstrapperApplication, packages must either be non-permanent and have the bal:PrimaryPackageType attribute, or be permanent and have the bal:PrereqPackage attribute set to 'yes'.");
29 }
30
31 public static Message IuibaNonPermanentPrereqPackage(SourceLineNumber sourceLineNumbers)
32 {
33 return Message(sourceLineNumbers, Ids.IuibaNonPermanentPrereqPackage, "When using WixInternalUIBootstrapperApplication and bal:PrereqPackage is set to 'yes', the package must be permanent.");
34 }
35
36 public static Message IuibaPermanentPrimaryPackageType(SourceLineNumber sourceLineNumbers)
37 {
38 return Message(sourceLineNumbers, Ids.IuibaPermanentPrimaryPackageType, "When using WixInternalUIBootstrapperApplication, packages with the bal:PrimaryPackageType attribute must not be permanent.");
39 }
40
41 public static Message IuibaPrimaryPackageEnableFeatureSelection(SourceLineNumber sourceLineNumbers)
42 {
43 return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageEnableFeatureSelection, "When using WixInternalUIBootstrapperApplication, primary packages must not have feature selection enabled because it interferes with the user selecting feature through the MSI UI.");
44 }
45
21 public static Message MissingDNCPrereq() 46 public static Message MissingDNCPrereq()
22 { 47 {
23 return Message(null, Ids.MissingDNCPrereq, "There must be at least one PrereqPackage when using the DotNetCoreBootstrapperApplicationHost with SelfContainedDeployment set to \"no\"."); 48 return Message(null, Ids.MissingDNCPrereq, "There must be at least one PrereqPackage when using the DotNetCoreBootstrapperApplicationHost with SelfContainedDeployment set to \"no\".");
24 } 49 }
25 50
51 public static Message MissingIUIPrimaryPackage()
52 {
53 return Message(null, Ids.MissingIUIPrimaryPackage, "When using WixInternalUIBootstrapperApplication, there must be one package with bal:PrimaryPackageType=\"default\".");
54 }
55
26 public static Message MissingMBAPrereq() 56 public static Message MissingMBAPrereq()
27 { 57 {
28 return Message(null, Ids.MissingMBAPrereq, "There must be at least one PrereqPackage when using the ManagedBootstrapperApplicationHost.\nThis is typically done by using the WixNetFxExtension and referencing one of the NetFxAsPrereq package groups."); 58 return Message(null, Ids.MissingMBAPrereq, "There must be at least one PrereqPackage when using the ManagedBootstrapperApplicationHost.\nThis is typically done by using the WixNetFxExtension and referencing one of the NetFxAsPrereq package groups.");
@@ -38,6 +68,16 @@ namespace WixToolset.Bal
38 return Message(sourceLineNumbers, Ids.MultiplePrereqLicenses, "There may only be one package in the bundle that has either the PrereqLicenseFile attribute or the PrereqLicenseUrl attribute."); 68 return Message(sourceLineNumbers, Ids.MultiplePrereqLicenses, "There may only be one package in the bundle that has either the PrereqLicenseFile attribute or the PrereqLicenseUrl attribute.");
39 } 69 }
40 70
71 public static Message MultiplePrimaryPackageType(SourceLineNumber sourceLineNumbers, string primaryPackageType)
72 {
73 return Message(sourceLineNumbers, Ids.MultiplePrimaryPackageType, "There may only be one package in the bundle with PrimaryPackageType of '{0}'.", primaryPackageType);
74 }
75
76 public static Message MultiplePrimaryPackageType2(SourceLineNumber sourceLineNumbers)
77 {
78 return Message(sourceLineNumbers, Ids.MultiplePrimaryPackageType2, "The location of the package related to the previous error.");
79 }
80
41 public static Message NonUpperCaseOverridableVariable(SourceLineNumber sourceLineNumbers, string name, string expectedName) 81 public static Message NonUpperCaseOverridableVariable(SourceLineNumber sourceLineNumbers, string name, string expectedName)
42 { 82 {
43 return Message(sourceLineNumbers, Ids.NonUpperCaseOverridableVariable, "Overridable variable '{0}' must be '{1}' with Bundle/@CommandLineVariables value 'upperCase'.", name, expectedName); 83 return Message(sourceLineNumbers, Ids.NonUpperCaseOverridableVariable, "Overridable variable '{0}' must be '{1}' with Bundle/@CommandLineVariables value 'upperCase'.", name, expectedName);
@@ -62,6 +102,14 @@ namespace WixToolset.Bal
62 BAFunctionsPayloadRequiredInUXContainer = 6805, 102 BAFunctionsPayloadRequiredInUXContainer = 6805,
63 MissingDNCPrereq = 6806, 103 MissingDNCPrereq = 6806,
64 NonUpperCaseOverridableVariable = 6807, 104 NonUpperCaseOverridableVariable = 6807,
105 MissingIUIPrimaryPackage = 6808,
106 MultiplePrimaryPackageType = 6809,
107 MultiplePrimaryPackageType2 = 6810,
108 IuibaNonPermanentNonPrimaryPackage = 6811,
109 IuibaNonPermanentPrereqPackage = 6812,
110 IuibaPermanentPrimaryPackageType = 6813,
111 IuibaNonMsiPrimaryPackage = 6814,
112 IuibaPrimaryPackageEnableFeatureSelection = 6815,
65 } 113 }
66 } 114 }
67} 115}
diff --git a/src/ext/Bal/wixext/BalWarnings.cs b/src/ext/Bal/wixext/BalWarnings.cs
index 18b25062..96e7a523 100644
--- a/src/ext/Bal/wixext/BalWarnings.cs
+++ b/src/ext/Bal/wixext/BalWarnings.cs
@@ -8,6 +8,26 @@ namespace WixToolset.Bal
8 8
9 public static class BalWarnings 9 public static class BalWarnings
10 { 10 {
11 public static Message IuibaForceCachePrereq(SourceLineNumber sourceLineNumbers)
12 {
13 return Message(sourceLineNumbers, Ids.IuibaForceCachePrereq, "WixInternalUIBootstrapperApplication does not support the value of 'force' for Cache on prereq packages. Prereq packages are only cached when they need to be installed.");
14 }
15
16 public static Message IuibaPrereqPackageAfterPrimaryPackage(SourceLineNumber sourceLineNumbers)
17 {
18 return Message(sourceLineNumbers, Ids.IuibaPrereqPackageAfterPrimaryPackage, "When using WixInternalUIBootstrapperApplication, all prereq packages should be before the primary package in the chain. The prereq packages are always installed before the primary package.");
19 }
20
21 public static Message IuibaPrimaryPackageDisplayInternalUICondition(SourceLineNumber sourceLineNumbers)
22 {
23 return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageDisplayInternalUICondition, "WixInternalUIBootstrapperApplication ignores DisplayInternalUICondition for the primary package so that the MSI UI is always shown.");
24 }
25
26 public static Message IuibaPrimaryPackageInstallCondition(SourceLineNumber sourceLineNumbers)
27 {
28 return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageInstallCondition, "WixInternalUIBootstrapperApplication ignores InstallCondition for the primary package so that the MSI UI is always shown.");
29 }
30
11 public static Message UnmarkedBAFunctionsDLL(SourceLineNumber sourceLineNumbers) 31 public static Message UnmarkedBAFunctionsDLL(SourceLineNumber sourceLineNumbers)
12 { 32 {
13 return Message(sourceLineNumbers, Ids.UnmarkedBAFunctionsDLL, "WixStandardBootstrapperApplication doesn't automatically load BAFunctions.dll. Use the bal:BAFunctions attribute to indicate that it should be loaded."); 33 return Message(sourceLineNumbers, Ids.UnmarkedBAFunctionsDLL, "WixStandardBootstrapperApplication doesn't automatically load BAFunctions.dll. Use the bal:BAFunctions attribute to indicate that it should be loaded.");
@@ -26,6 +46,10 @@ namespace WixToolset.Bal
26 public enum Ids 46 public enum Ids
27 { 47 {
28 UnmarkedBAFunctionsDLL = 6501, 48 UnmarkedBAFunctionsDLL = 6501,
49 IuibaForceCachePrereq = 6502,
50 IuibaPrimaryPackageInstallCondition = 6503,
51 IuibaPrimaryPackageDisplayInternalUICondition = 6504,
52 IuibaPrereqPackageAfterPrimaryPackage = 6505,
29 } 53 }
30 } 54 }
31} 55}
diff --git a/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs b/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
index b09cb191..08d4ce4e 100644
--- a/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
+++ b/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
@@ -13,6 +13,7 @@ namespace WixToolset.Bal
13 { 13 {
14 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.PackageId), IntermediateFieldType.String), 14 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.PackageId), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.DisplayInternalUICondition), IntermediateFieldType.String), 15 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.DisplayInternalUICondition), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.PrimaryPackageType), IntermediateFieldType.Number),
16 }, 17 },
17 typeof(WixBalPackageInfoSymbol)); 18 typeof(WixBalPackageInfoSymbol));
18 } 19 }
@@ -26,6 +27,16 @@ namespace WixToolset.Bal.Symbols
26 { 27 {
27 PackageId, 28 PackageId,
28 DisplayInternalUICondition, 29 DisplayInternalUICondition,
30 PrimaryPackageType,
31 }
32
33 public enum BalPrimaryPackageType
34 {
35 None,
36 Default,
37 X86,
38 X64,
39 ARM64,
29 } 40 }
30 41
31 public class WixBalPackageInfoSymbol : IntermediateSymbol 42 public class WixBalPackageInfoSymbol : IntermediateSymbol
@@ -51,5 +62,11 @@ namespace WixToolset.Bal.Symbols
51 get => this.Fields[(int)WixBalPackageInfoSymbolFields.DisplayInternalUICondition].AsString(); 62 get => this.Fields[(int)WixBalPackageInfoSymbolFields.DisplayInternalUICondition].AsString();
52 set => this.Set((int)WixBalPackageInfoSymbolFields.DisplayInternalUICondition, value); 63 set => this.Set((int)WixBalPackageInfoSymbolFields.DisplayInternalUICondition, value);
53 } 64 }
65
66 public BalPrimaryPackageType PrimaryPackageType
67 {
68 get => (BalPrimaryPackageType)this.Fields[(int)WixBalPackageInfoSymbolFields.PrimaryPackageType].AsNumber();
69 set => this.Set((int)WixBalPackageInfoSymbolFields.PrimaryPackageType, (int)value);
70 }
54 } 71 }
55} 72}