aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs')
-rw-r--r--src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs b/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
new file mode 100644
index 00000000..cb6e2748
--- /dev/null
+++ b/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
@@ -0,0 +1,145 @@
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.Burn.Bundles
4{
5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Data;
8 using WixToolset.Data.Rows;
9
10 internal class OrderPackagesAndRollbackBoundariesCommand
11 {
12 public Table WixGroupTable { private get; set; }
13
14 public RowDictionary<WixBundleRollbackBoundaryRow> Boundaries { private get; set; }
15
16 public IDictionary<string, PackageFacade> PackageFacades { private get; set; }
17
18 public IEnumerable<PackageFacade> OrderedPackageFacades { get; private set; }
19
20 public IEnumerable<WixBundleRollbackBoundaryRow> UsedRollbackBoundaries { get; private set; }
21
22 public void Execute()
23 {
24 List<PackageFacade> orderedFacades = new List<PackageFacade>();
25 List<WixBundleRollbackBoundaryRow> usedBoundaries = new List<WixBundleRollbackBoundaryRow>();
26
27 // Process the chain of packages to add them in the correct order
28 // and assign the forward rollback boundaries as appropriate. Remember
29 // rollback boundaries are authored as elements in the chain which
30 // we re-interpret here to add them as attributes on the next available
31 // package in the chain. Essentially we mark some packages as being
32 // the start of a rollback boundary when installing and repairing.
33 // We handle uninstall (aka: backwards) rollback boundaries after
34 // we get these install/repair (aka: forward) rollback boundaries
35 // defined.
36 WixBundleRollbackBoundaryRow previousRollbackBoundary = null;
37 WixBundleRollbackBoundaryRow lastRollbackBoundary = null;
38 bool boundaryHadX86Package = false;
39
40 foreach (WixGroupRow row in this.WixGroupTable.Rows)
41 {
42 if (ComplexReferenceChildType.Package == row.ChildType && ComplexReferenceParentType.PackageGroup == row.ParentType && "WixChain" == row.ParentId)
43 {
44 PackageFacade facade = null;
45 if (PackageFacades.TryGetValue(row.ChildId, out facade))
46 {
47 if (null != previousRollbackBoundary)
48 {
49 usedBoundaries.Add(previousRollbackBoundary);
50 facade.Package.RollbackBoundary = previousRollbackBoundary.ChainPackageId;
51 previousRollbackBoundary = null;
52
53 boundaryHadX86Package = (facade.Package.x64 == YesNoType.Yes);
54 }
55
56 // Error if MSI transaction has x86 package preceding x64 packages
57 if ((lastRollbackBoundary != null) && (lastRollbackBoundary.Transaction == YesNoType.Yes)
58 && boundaryHadX86Package
59 && (facade.Package.x64 == YesNoType.Yes))
60 {
61 Messaging.Instance.OnMessage(WixErrors.MsiTransactionX86BeforeX64(lastRollbackBoundary.SourceLineNumbers));
62 }
63 boundaryHadX86Package = boundaryHadX86Package || (facade.Package.x64 == YesNoType.No);
64
65 orderedFacades.Add(facade);
66 }
67 else // must be a rollback boundary.
68 {
69 // Discard the next rollback boundary if we have a previously defined boundary.
70 WixBundleRollbackBoundaryRow nextRollbackBoundary = Boundaries.Get(row.ChildId);
71 if (null != previousRollbackBoundary)
72 {
73 Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.ChainPackageId));
74 }
75 else
76 {
77 previousRollbackBoundary = nextRollbackBoundary;
78 lastRollbackBoundary = nextRollbackBoundary;
79 }
80 }
81 }
82 }
83
84 if (null != previousRollbackBoundary)
85 {
86 Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(previousRollbackBoundary.SourceLineNumbers, previousRollbackBoundary.ChainPackageId));
87 }
88
89 // With the forward rollback boundaries assigned, we can now go
90 // through the packages with rollback boundaries and assign backward
91 // rollback boundaries. Backward rollback boundaries are used when
92 // the chain is going "backwards" which (AFAIK) only happens during
93 // uninstall.
94 //
95 // Consider the scenario with three packages: A, B and C. Packages A
96 // and C are marked as rollback boundary packages and package B is
97 // not. The naive implementation would execute the chain like this
98 // (numbers indicate where rollback boundaries would end up):
99 // install: 1 A B 2 C
100 // uninstall: 2 C B 1 A
101 //
102 // The uninstall chain is wrong, A and B should be grouped together
103 // not C and B. The fix is to label packages with a "backwards"
104 // rollback boundary used during uninstall. The backwards rollback
105 // boundaries are assigned to the package *before* the next rollback
106 // boundary. Using our example from above again, I'll mark the
107 // backwards rollback boundaries prime (aka: with ').
108 // install: 1 A B 1' 2 C 2'
109 // uninstall: 2' C 2 1' B A 1
110 //
111 // If the marked boundaries are ignored during install you get the
112 // same thing as above (good) and if the non-marked boundaries are
113 // ignored during uninstall then A and B are correctly grouped.
114 // Here's what it looks like without all the markers:
115 // install: 1 A B 2 C
116 // uninstall: 2 C 1 B A
117 // Woot!
118 string previousRollbackBoundaryId = null;
119 PackageFacade previousFacade = null;
120
121 foreach (PackageFacade package in orderedFacades)
122 {
123 if (null != package.Package.RollbackBoundary)
124 {
125 if (null != previousFacade)
126 {
127 previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId;
128 }
129
130 previousRollbackBoundaryId = package.Package.RollbackBoundary;
131 }
132
133 previousFacade = package;
134 }
135
136 if (!String.IsNullOrEmpty(previousRollbackBoundaryId) && null != previousFacade)
137 {
138 previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId;
139 }
140
141 this.OrderedPackageFacades = orderedFacades;
142 this.UsedRollbackBoundaries = usedBoundaries;
143 }
144 }
145}