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