diff options
Diffstat (limited to 'src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs')
-rw-r--r-- | src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs | 145 |
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 | |||
3 | namespace 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 | } | ||