aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/WixBundleRollbackBoundarySymbol.cs8
-rw-r--r--src/burn/engine/elevation.cpp15
-rw-r--r--src/burn/engine/logging.cpp35
-rw-r--r--src/burn/engine/logging.h7
-rw-r--r--src/burn/engine/package.cpp62
-rw-r--r--src/burn/engine/package.h1
-rw-r--r--src/burn/engine/plan.cpp6
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs25
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs6
-rw-r--r--src/wix/WixToolset.Core/Compiler_Bundle.cs26
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs11
12 files changed, 158 insertions, 46 deletions
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundleRollbackBoundarySymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundleRollbackBoundarySymbol.cs
index e54e898e..1f91cef2 100644
--- a/src/api/wix/WixToolset.Data/Symbols/WixBundleRollbackBoundarySymbol.cs
+++ b/src/api/wix/WixToolset.Data/Symbols/WixBundleRollbackBoundarySymbol.cs
@@ -12,6 +12,7 @@ namespace WixToolset.Data
12 { 12 {
13 new IntermediateFieldDefinition(nameof(WixBundleRollbackBoundarySymbolFields.Vital), IntermediateFieldType.Number), 13 new IntermediateFieldDefinition(nameof(WixBundleRollbackBoundarySymbolFields.Vital), IntermediateFieldType.Number),
14 new IntermediateFieldDefinition(nameof(WixBundleRollbackBoundarySymbolFields.Transaction), IntermediateFieldType.Number), 14 new IntermediateFieldDefinition(nameof(WixBundleRollbackBoundarySymbolFields.Transaction), IntermediateFieldType.Number),
15 new IntermediateFieldDefinition(nameof(WixBundlePackageSymbolFields.LogPathVariable), IntermediateFieldType.String),
15 }, 16 },
16 typeof(WixBundleRollbackBoundarySymbol)); 17 typeof(WixBundleRollbackBoundarySymbol));
17 } 18 }
@@ -23,6 +24,7 @@ namespace WixToolset.Data.Symbols
23 { 24 {
24 Vital, 25 Vital,
25 Transaction, 26 Transaction,
27 LogPathVariable,
26 } 28 }
27 29
28 public class WixBundleRollbackBoundarySymbol : IntermediateSymbol 30 public class WixBundleRollbackBoundarySymbol : IntermediateSymbol
@@ -48,5 +50,11 @@ namespace WixToolset.Data.Symbols
48 get => (bool?)this.Fields[(int)WixBundleRollbackBoundarySymbolFields.Transaction]; 50 get => (bool?)this.Fields[(int)WixBundleRollbackBoundarySymbolFields.Transaction];
49 set => this.Set((int)WixBundleRollbackBoundarySymbolFields.Transaction, value); 51 set => this.Set((int)WixBundleRollbackBoundarySymbolFields.Transaction, value);
50 } 52 }
53
54 public string LogPathVariable
55 {
56 get => (string)this.Fields[(int)WixBundleRollbackBoundarySymbolFields.LogPathVariable];
57 set => this.Set((int)WixBundleRollbackBoundarySymbolFields.LogPathVariable, value);
58 }
51 } 59 }
52} \ No newline at end of file 60} \ No newline at end of file
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp
index 0e1cf0b7..ba6b1dd3 100644
--- a/src/burn/engine/elevation.cpp
+++ b/src/burn/engine/elevation.cpp
@@ -3246,7 +3246,10 @@ static HRESULT OnMsiBeginTransaction(
3246 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); 3246 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary);
3247 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); 3247 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId);
3248 3248
3249 pRollbackBoundary->sczLogPath = sczLogPath; 3249 if (sczLogPath && *sczLogPath)
3250 {
3251 pRollbackBoundary->sczLogPath = sczLogPath;
3252 }
3250 3253
3251 hr = MsiEngineBeginTransaction(pRollbackBoundary); 3254 hr = MsiEngineBeginTransaction(pRollbackBoundary);
3252 3255
@@ -3284,7 +3287,10 @@ static HRESULT OnMsiCommitTransaction(
3284 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); 3287 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary);
3285 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); 3288 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId);
3286 3289
3287 pRollbackBoundary->sczLogPath = sczLogPath; 3290 if (sczLogPath && *sczLogPath)
3291 {
3292 pRollbackBoundary->sczLogPath = sczLogPath;
3293 }
3288 3294
3289 hr = MsiEngineCommitTransaction(pRollbackBoundary); 3295 hr = MsiEngineCommitTransaction(pRollbackBoundary);
3290 3296
@@ -3322,7 +3328,10 @@ static HRESULT OnMsiRollbackTransaction(
3322 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary); 3328 hr = PackageFindRollbackBoundaryById(pPackages, sczId, &pRollbackBoundary);
3323 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId); 3329 ExitOnFailure(hr, "Failed to find rollback boundary: %ls", sczId);
3324 3330
3325 pRollbackBoundary->sczLogPath = sczLogPath; 3331 if (sczLogPath && *sczLogPath)
3332 {
3333 pRollbackBoundary->sczLogPath = sczLogPath;
3334 }
3326 3335
3327 hr = MsiEngineRollbackTransaction(pRollbackBoundary); 3336 hr = MsiEngineRollbackTransaction(pRollbackBoundary);
3328 3337
diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp
index 9aaf60ed..33295acd 100644
--- a/src/burn/engine/logging.cpp
+++ b/src/burn/engine/logging.cpp
@@ -242,11 +242,6 @@ extern "C" HRESULT LoggingSetPackageVariable(
242 // Make sure that no package log files are created when logging has been disabled via Log element. 242 // Make sure that no package log files are created when logging has been disabled via Log element.
243 if (BURN_LOGGING_STATE_DISABLED == pLog->state) 243 if (BURN_LOGGING_STATE_DISABLED == pLog->state)
244 { 244 {
245 if (psczLogPath)
246 {
247 *psczLogPath = NULL;
248 }
249
250 ExitFunction(); 245 ExitFunction();
251 } 246 }
252 247
@@ -272,6 +267,36 @@ LExit:
272 return hr; 267 return hr;
273} 268}
274 269
270extern "C" HRESULT LoggingSetTransactionVariable(
271 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
272 __in_z_opt LPCWSTR wzSuffix,
273 __in BURN_LOGGING* pLog,
274 __in BURN_VARIABLES* pVariables
275 )
276{
277 HRESULT hr = S_OK;
278
279 // Make sure that no log files are created when logging has been disabled via Log element.
280 if (BURN_LOGGING_STATE_DISABLED == pLog->state)
281 {
282 ExitFunction();
283 }
284
285 if (pRollbackBoundary && pRollbackBoundary->sczLogPathVariable && *pRollbackBoundary->sczLogPathVariable)
286 {
287 hr = StrAllocFormatted(&pRollbackBoundary->sczLogPath, L"%ls%hs%ls_%03u_%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pRollbackBoundary->sczId, pLog->sczExtension);
288 ExitOnFailure(hr, "Failed to allocate path for transaction log.");
289
290 hr = VariableSetString(pVariables, pRollbackBoundary->sczLogPathVariable, pRollbackBoundary->sczLogPath, FALSE, FALSE);
291 ExitOnFailure(hr, "Failed to set log path into variable.");
292 }
293
294LExit:
295 ++vdwPackageSequence;
296
297 return hr;
298}
299
275extern "C" LPCSTR LoggingBurnActionToString( 300extern "C" LPCSTR LoggingBurnActionToString(
276 __in BOOTSTRAPPER_ACTION action 301 __in BOOTSTRAPPER_ACTION action
277 ) 302 )
diff --git a/src/burn/engine/logging.h b/src/burn/engine/logging.h
index 492e14b6..367b94a3 100644
--- a/src/burn/engine/logging.h
+++ b/src/burn/engine/logging.h
@@ -62,6 +62,13 @@ HRESULT LoggingSetPackageVariable(
62 __out_opt LPWSTR* psczLogPath 62 __out_opt LPWSTR* psczLogPath
63 ); 63 );
64 64
65HRESULT LoggingSetTransactionVariable(
66 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary,
67 __in_z_opt LPCWSTR wzSuffix,
68 __in BURN_LOGGING* pLog,
69 __in BURN_VARIABLES* pVariables
70 );
71
65LPCSTR LoggingBurnActionToString( 72LPCSTR LoggingBurnActionToString(
66 __in BOOTSTRAPPER_ACTION action 73 __in BOOTSTRAPPER_ACTION action
67 ); 74 );
diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp
index cd871bc5..bea48cb5 100644
--- a/src/burn/engine/package.cpp
+++ b/src/burn/engine/package.cpp
@@ -36,6 +36,7 @@ extern "C" HRESULT PackagesParseFromXml(
36 BSTR bstrNodeName = NULL; 36 BSTR bstrNodeName = NULL;
37 DWORD cMspPackages = 0; 37 DWORD cMspPackages = 0;
38 LPWSTR scz = NULL; 38 LPWSTR scz = NULL;
39 BOOL fFoundXml = FALSE;
39 40
40 // select rollback boundary nodes 41 // select rollback boundary nodes
41 hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes); 42 hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes);
@@ -63,15 +64,19 @@ extern "C" HRESULT PackagesParseFromXml(
63 64
64 // @Id 65 // @Id
65 hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId); 66 hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId);
66 ExitOnFailure(hr, "Failed to get @Id."); 67 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Id.");
67 68
68 // @Vital 69 // @Vital
69 hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital); 70 hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital);
70 ExitOnFailure(hr, "Failed to get @Vital."); 71 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Vital.");
71 72
72 // @Transaction 73 // @Transaction
73 hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransactionAuthored); 74 hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransactionAuthored);
74 ExitOnFailure(hr, "Failed to get @Transaction."); 75 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Transaction.");
76
77 // @LogPathVariable
78 hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pRollbackBoundary->sczLogPathVariable);
79 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @LogPathVariable.");
75 80
76 // prepare next iteration 81 // prepare next iteration
77 ReleaseNullObject(pixnNode); 82 ReleaseNullObject(pixnNode);
@@ -110,11 +115,13 @@ extern "C" HRESULT PackagesParseFromXml(
110 115
111 // @Id 116 // @Id
112 hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId); 117 hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId);
113 ExitOnFailure(hr, "Failed to get @Id."); 118 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Id.");
114 119
115 // @Cache 120 // @Cache
116 hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz); 121 hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz);
117 if (SUCCEEDED(hr)) 122 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Cache.");
123
124 if (fFoundXml)
118 { 125 {
119 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"remove", -1)) 126 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"remove", -1))
120 { 127 {
@@ -134,72 +141,62 @@ extern "C" HRESULT PackagesParseFromXml(
134 ExitOnRootFailure(hr, "Invalid cache type: %ls", scz); 141 ExitOnRootFailure(hr, "Invalid cache type: %ls", scz);
135 } 142 }
136 } 143 }
137 ExitOnFailure(hr, "Failed to get @Cache.");
138 144
139 // @CacheId 145 // @CacheId
140 hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId); 146 hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId);
141 ExitOnFailure(hr, "Failed to get @CacheId."); 147 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @CacheId.");
142 148
143 // @Size 149 // @Size
144 hr = XmlGetAttributeUInt64(pixnNode, L"Size", &pPackage->qwSize); 150 hr = XmlGetAttributeUInt64(pixnNode, L"Size", &pPackage->qwSize);
145 ExitOnFailure(hr, "Failed to get @Size."); 151 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Size.");
146 152
147 // @InstallSize 153 // @InstallSize
148 hr = XmlGetAttributeUInt64(pixnNode, L"InstallSize", &pPackage->qwInstallSize); 154 hr = XmlGetAttributeUInt64(pixnNode, L"InstallSize", &pPackage->qwInstallSize);
149 ExitOnFailure(hr, "Failed to get @InstallSize."); 155 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallSize.");
150 156
151 // @PerMachine 157 // @PerMachine
152 hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine); 158 hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine);
153 ExitOnFailure(hr, "Failed to get @PerMachine."); 159 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @PerMachine.");
154 160
155 // @Permanent 161 // @Permanent
156 hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable); 162 hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable);
157 ExitOnFailure(hr, "Failed to get @Permanent."); 163 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Permanent.");
158 pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable. 164 pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable.
159 pPackage->fCanAffectRegistration = pPackage->fUninstallable; 165 pPackage->fCanAffectRegistration = pPackage->fUninstallable;
160 166
161 // @Vital 167 // @Vital
162 hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital); 168 hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital);
163 ExitOnFailure(hr, "Failed to get @Vital."); 169 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Vital.");
164 170
165 // @LogPathVariable 171 // @LogPathVariable
166 hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable); 172 hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable);
167 if (E_NOTFOUND != hr) 173 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @LogPathVariable.");
168 {
169 ExitOnFailure(hr, "Failed to get @LogPathVariable.");
170 }
171 174
172 // @RollbackLogPathVariable 175 // @RollbackLogPathVariable
173 hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable); 176 hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable);
174 if (E_NOTFOUND != hr) 177 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RollbackLogPathVariable.");
175 {
176 ExitOnFailure(hr, "Failed to get @RollbackLogPathVariable.");
177 }
178 178
179 // @InstallCondition 179 // @InstallCondition
180 hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition); 180 hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition);
181 if (E_NOTFOUND != hr) 181 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallCondition.");
182 {
183 ExitOnFailure(hr, "Failed to get @InstallCondition.");
184 }
185 182
186 // @RollbackBoundaryForward 183 // @RollbackBoundaryForward
187 hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz); 184 hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz);
188 if (E_NOTFOUND != hr) 185 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RollbackBoundaryForward.");
189 {
190 ExitOnFailure(hr, "Failed to get @RollbackBoundaryForward.");
191 186
192 hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward); 187 if (fFoundXml)
188 {
189 hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward);
193 ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz); 190 ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz);
194 } 191 }
195 192
196 // @RollbackBoundaryBackward 193 // @RollbackBoundaryBackward
197 hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz); 194 hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz);
198 if (E_NOTFOUND != hr) 195 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RollbackBoundaryBackward.");
199 {
200 ExitOnFailure(hr, "Failed to get @RollbackBoundaryBackward.");
201 196
202 hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward); 197 if (fFoundXml)
198 {
199 hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward);
203 ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz); 200 ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz);
204 } 201 }
205 202
@@ -378,6 +375,7 @@ extern "C" void PackagesUninitialize(
378 { 375 {
379 ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId); 376 ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId);
380 ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPath); 377 ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPath);
378 ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPathVariable);
381 } 379 }
382 MemFree(pPackages->rgRollbackBoundaries); 380 MemFree(pPackages->rgRollbackBoundaries);
383 } 381 }
diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h
index 3d8233d1..f2a1c304 100644
--- a/src/burn/engine/package.h
+++ b/src/burn/engine/package.h
@@ -195,6 +195,7 @@ typedef struct _BURN_ROLLBACK_BOUNDARY
195 BOOL fTransactionAuthored; 195 BOOL fTransactionAuthored;
196 BOOL fTransaction; 196 BOOL fTransaction;
197 BOOL fActiveTransaction; // only valid during Apply. 197 BOOL fActiveTransaction; // only valid during Apply.
198 LPWSTR sczLogPathVariable;
198 LPWSTR sczLogPath; 199 LPWSTR sczLogPath;
199} BURN_ROLLBACK_BOUNDARY; 200} BURN_ROLLBACK_BOUNDARY;
200 201
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp
index 85d958a6..58981352 100644
--- a/src/burn/engine/plan.cpp
+++ b/src/burn/engine/plan.cpp
@@ -1709,8 +1709,8 @@ LExit:
1709extern "C" HRESULT PlanRollbackBoundaryBegin( 1709extern "C" HRESULT PlanRollbackBoundaryBegin(
1710 __in BURN_PLAN* pPlan, 1710 __in BURN_PLAN* pPlan,
1711 __in BURN_USER_EXPERIENCE* pUX, 1711 __in BURN_USER_EXPERIENCE* pUX,
1712 __in BURN_LOGGING* /*pLog*/, 1712 __in BURN_LOGGING* pLog,
1713 __in BURN_VARIABLES* /*pVariables*/, 1713 __in BURN_VARIABLES* pVariables,
1714 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary 1714 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1715 ) 1715 )
1716{ 1716{
@@ -1744,6 +1744,8 @@ extern "C" HRESULT PlanRollbackBoundaryBegin(
1744 } 1744 }
1745 else 1745 else
1746 { 1746 {
1747 LoggingSetTransactionVariable(pRollbackBoundary, NULL, pLog, pVariables); // ignore errors.
1748
1747 // Add begin MSI transaction to execute plan. 1749 // Add begin MSI transaction to execute plan.
1748 hr = PlanExecuteCheckpoint(pPlan); 1750 hr = PlanExecuteCheckpoint(pPlan);
1749 ExitOnFailure(hr, "Failed to append checkpoint before MSI transaction begin action."); 1751 ExitOnFailure(hr, "Failed to append checkpoint before MSI transaction begin action.");
diff --git a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index 97007022..cd00232a 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -393,7 +393,7 @@ namespace WixToolset.Core.Burn
393 // Generate the core-defined BA manifest tables... 393 // Generate the core-defined BA manifest tables...
394 string baManifestPath; 394 string baManifestPath;
395 { 395 {
396 var command = new CreateBootstrapperApplicationManifestCommand(section, bundleSymbol, orderedFacades, uxPayloadIndex, payloadSymbols, packagesPayloads, this.IntermediateFolder, this.InternalBurnBackendHelper); 396 var command = new CreateBootstrapperApplicationManifestCommand(section, bundleSymbol, boundaries, orderedFacades, uxPayloadIndex, payloadSymbols, packagesPayloads, this.IntermediateFolder, this.InternalBurnBackendHelper);
397 command.Execute(); 397 command.Execute();
398 398
399 var baManifestPayload = command.BootstrapperApplicationManifestPayloadRow; 399 var baManifestPayload = command.BootstrapperApplicationManifestPayloadRow;
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
index 764a38ab..42e3381a 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
@@ -15,10 +15,11 @@ namespace WixToolset.Core.Burn.Bundles
15 15
16 internal class CreateBootstrapperApplicationManifestCommand 16 internal class CreateBootstrapperApplicationManifestCommand
17 { 17 {
18 public CreateBootstrapperApplicationManifestCommand(IntermediateSection section, WixBundleSymbol bundleSymbol, IEnumerable<PackageFacade> chainPackages, int lastUXPayloadIndex, Dictionary<string, WixBundlePayloadSymbol> payloadSymbols, Dictionary<string, Dictionary<string, WixBundlePayloadSymbol>> packagesPayloads, string intermediateFolder, IInternalBurnBackendHelper internalBurnBackendHelper) 18 public CreateBootstrapperApplicationManifestCommand(IntermediateSection section, WixBundleSymbol bundleSymbol, IEnumerable<WixBundleRollbackBoundarySymbol> boundaries, IEnumerable<PackageFacade> chainPackages, int lastUXPayloadIndex, Dictionary<string, WixBundlePayloadSymbol> payloadSymbols, Dictionary<string, Dictionary<string, WixBundlePayloadSymbol>> packagesPayloads, string intermediateFolder, IInternalBurnBackendHelper internalBurnBackendHelper)
19 { 19 {
20 this.Section = section; 20 this.Section = section;
21 this.BundleSymbol = bundleSymbol; 21 this.BundleSymbol = bundleSymbol;
22 this.RollbackBoundaries = boundaries;
22 this.ChainPackages = chainPackages; 23 this.ChainPackages = chainPackages;
23 this.LastUXPayloadIndex = lastUXPayloadIndex; 24 this.LastUXPayloadIndex = lastUXPayloadIndex;
24 this.Payloads = payloadSymbols; 25 this.Payloads = payloadSymbols;
@@ -31,6 +32,8 @@ namespace WixToolset.Core.Burn.Bundles
31 32
32 private WixBundleSymbol BundleSymbol { get; } 33 private WixBundleSymbol BundleSymbol { get; }
33 34
35 private IEnumerable<WixBundleRollbackBoundarySymbol> RollbackBoundaries { get; }
36
34 private IEnumerable<PackageFacade> ChainPackages { get; } 37 private IEnumerable<PackageFacade> ChainPackages { get; }
35 38
36 private IInternalBurnBackendHelper InternalBurnBackendHelper { get; } 39 private IInternalBurnBackendHelper InternalBurnBackendHelper { get; }
@@ -70,6 +73,8 @@ namespace WixToolset.Core.Burn.Bundles
70 73
71 this.WriteCommandLineInfo(writer); 74 this.WriteCommandLineInfo(writer);
72 75
76 this.WriteRollbackBoundaryInfo(writer);
77
73 this.WritePackageInfo(writer); 78 this.WritePackageInfo(writer);
74 79
75 this.WriteFeatureInfo(writer); 80 this.WriteFeatureInfo(writer);
@@ -116,6 +121,24 @@ namespace WixToolset.Core.Burn.Bundles
116 writer.WriteEndElement(); 121 writer.WriteEndElement();
117 } 122 }
118 123
124 private void WriteRollbackBoundaryInfo(XmlTextWriter writer)
125 {
126 foreach (var rollbackBoundary in this.RollbackBoundaries)
127 {
128 writer.WriteStartElement("WixRollbackBoundary");
129 writer.WriteAttributeString("Id", rollbackBoundary.Id.Id);
130 writer.WriteAttributeString("Vital", rollbackBoundary.Vital == false ? "no" : "yes");
131 writer.WriteAttributeString("Transaction", rollbackBoundary.Transaction == true ? "yes" : "no");
132
133 if (!String.IsNullOrEmpty(rollbackBoundary.LogPathVariable))
134 {
135 writer.WriteAttributeString("LogPathVariable", rollbackBoundary.LogPathVariable);
136 }
137
138 writer.WriteEndElement();
139 }
140 }
141
119 private void WritePackageInfo(XmlTextWriter writer) 142 private void WritePackageInfo(XmlTextWriter writer)
120 { 143 {
121 foreach (var package in this.ChainPackages) 144 foreach (var package in this.ChainPackages)
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
index 27b1f234..19dc7933 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
@@ -188,6 +188,12 @@ namespace WixToolset.Core.Burn.Bundles
188 writer.WriteAttributeString("Id", rollbackBoundary.Id.Id); 188 writer.WriteAttributeString("Id", rollbackBoundary.Id.Id);
189 writer.WriteAttributeString("Vital", rollbackBoundary.Vital == false ? "no" : "yes"); 189 writer.WriteAttributeString("Vital", rollbackBoundary.Vital == false ? "no" : "yes");
190 writer.WriteAttributeString("Transaction", rollbackBoundary.Transaction == true ? "yes" : "no"); 190 writer.WriteAttributeString("Transaction", rollbackBoundary.Transaction == true ? "yes" : "no");
191
192 if (!String.IsNullOrEmpty(rollbackBoundary.LogPathVariable))
193 {
194 writer.WriteAttributeString("LogPathVariable", rollbackBoundary.LogPathVariable);
195 }
196
191 writer.WriteEndElement(); 197 writer.WriteEndElement();
192 } 198 }
193 199
diff --git a/src/wix/WixToolset.Core/Compiler_Bundle.cs b/src/wix/WixToolset.Core/Compiler_Bundle.cs
index 20af017d..851561d1 100644
--- a/src/wix/WixToolset.Core/Compiler_Bundle.cs
+++ b/src/wix/WixToolset.Core/Compiler_Bundle.cs
@@ -1860,6 +1860,7 @@ namespace WixToolset.Core
1860 Identifier id = null; 1860 Identifier id = null;
1861 var vital = YesNoType.Yes; 1861 var vital = YesNoType.Yes;
1862 var transaction = YesNoType.No; 1862 var transaction = YesNoType.No;
1863 string logPathVariable = null;
1863 1864
1864 // This list lets us evaluate extension attributes *after* all core attributes 1865 // This list lets us evaluate extension attributes *after* all core attributes
1865 // have been parsed and dealt with, regardless of authoring order. 1866 // have been parsed and dealt with, regardless of authoring order.
@@ -1885,6 +1886,9 @@ namespace WixToolset.Core
1885 case "Transaction": 1886 case "Transaction":
1886 transaction = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); 1887 transaction = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1887 break; 1888 break;
1889 case "LogPathVariable":
1890 logPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1891 break;
1888 default: 1892 default:
1889 allowed = false; 1893 allowed = false;
1890 break; 1894 break;
@@ -1932,9 +1936,21 @@ namespace WixToolset.Core
1932 1936
1933 this.Core.ParseForExtensionElements(node); 1937 this.Core.ParseForExtensionElements(node);
1934 1938
1939 if (transaction == YesNoType.Yes)
1940 {
1941 if (logPathVariable == null)
1942 {
1943 logPathVariable = String.Concat("WixBundleLog_", id.Id);
1944 }
1945 }
1946 else if (logPathVariable != null)
1947 {
1948 this.Core.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "LogPathVariable", logPathVariable, "Transaction"));
1949 }
1950
1935 if (!this.Core.EncounteredError) 1951 if (!this.Core.EncounteredError)
1936 { 1952 {
1937 this.CreateRollbackBoundary(sourceLineNumbers, id, vital, transaction, parentType, parentId, previousType, previousId); 1953 this.CreateRollbackBoundary(sourceLineNumbers, id, vital, transaction, logPathVariable, parentType, parentId, previousType, previousId);
1938 } 1954 }
1939 1955
1940 return id.Id; 1956 return id.Id;
@@ -2759,11 +2775,12 @@ namespace WixToolset.Core
2759 /// <param name="id">Identifier for the rollback boundary.</param> 2775 /// <param name="id">Identifier for the rollback boundary.</param>
2760 /// <param name="vital">Indicates whether the rollback boundary is vital or not.</param> 2776 /// <param name="vital">Indicates whether the rollback boundary is vital or not.</param>
2761 /// <param name="transaction">Indicates whether the rollback boundary will use an MSI transaction.</param> 2777 /// <param name="transaction">Indicates whether the rollback boundary will use an MSI transaction.</param>
2778 /// <param name="logPathVariable">The variable for the path of the MSI transaction log.</param>
2762 /// <param name="parentType">Type of parent group.</param> 2779 /// <param name="parentType">Type of parent group.</param>
2763 /// <param name="parentId">Identifier of parent group.</param> 2780 /// <param name="parentId">Identifier of parent group.</param>
2764 /// <param name="previousType">Type of previous item, if any.</param> 2781 /// <param name="previousType">Type of previous item, if any.</param>
2765 /// <param name="previousId">Identifier of previous item, if any.</param> 2782 /// <param name="previousId">Identifier of previous item, if any.</param>
2766 private void CreateRollbackBoundary(SourceLineNumber sourceLineNumbers, Identifier id, YesNoType vital, YesNoType transaction, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) 2783 private void CreateRollbackBoundary(SourceLineNumber sourceLineNumbers, Identifier id, YesNoType vital, YesNoType transaction, string logPathVariable, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
2767 { 2784 {
2768 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id)); 2785 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));
2769 2786
@@ -2777,6 +2794,11 @@ namespace WixToolset.Core
2777 if (YesNoType.NotSet != transaction) 2794 if (YesNoType.NotSet != transaction)
2778 { 2795 {
2779 rollbackBoundary.Transaction = (transaction == YesNoType.Yes); 2796 rollbackBoundary.Transaction = (transaction == YesNoType.Yes);
2797
2798 if (logPathVariable != null)
2799 {
2800 rollbackBoundary.LogPathVariable = logPathVariable;
2801 }
2780 } 2802 }
2781 2803
2782 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, null); 2804 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, null);
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs
index a566b490..573ae4b4 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs
@@ -2,6 +2,7 @@
2 2
3namespace WixToolsetTest.CoreIntegration 3namespace WixToolsetTest.CoreIntegration
4{ 4{
5 using System.Collections.Generic;
5 using System.IO; 6 using System.IO;
6 using WixBuildTools.TestSupport; 7 using WixBuildTools.TestSupport;
7 using WixToolset.Core.TestPackage; 8 using WixToolset.Core.TestPackage;
@@ -50,6 +51,8 @@ namespace WixToolsetTest.CoreIntegration
50 var intermediateFolder = Path.Combine(baseFolder, "obj"); 51 var intermediateFolder = Path.Combine(baseFolder, "obj");
51 var binFolder = Path.Combine(baseFolder, "bin"); 52 var binFolder = Path.Combine(baseFolder, "bin");
52 var exePath = Path.Combine(binFolder, "test.exe"); 53 var exePath = Path.Combine(binFolder, "test.exe");
54 var baFolderPath = Path.Combine(baseFolder, "ba");
55 var extractFolderPath = Path.Combine(baseFolder, "extract");
53 56
54 BuildMsiPackages(folder, intermediateFolder, binFolder); 57 BuildMsiPackages(folder, intermediateFolder, binFolder);
55 58
@@ -68,6 +71,14 @@ namespace WixToolsetTest.CoreIntegration
68 result.AssertSuccess(); 71 result.AssertSuccess();
69 72
70 Assert.True(File.Exists(exePath)); 73 Assert.True(File.Exists(exePath));
74
75 var extractResult = BundleExtractor.ExtractBAContainer(null, exePath, baFolderPath, extractFolderPath);
76 extractResult.AssertSuccess();
77
78 var rollbackBoundaries = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:RollbackBoundary");
79 Assert.Equal(2, rollbackBoundaries.Count);
80 Assert.Equal("<RollbackBoundary Id='WixDefaultBoundary' Vital='yes' Transaction='no' />", rollbackBoundaries[0].GetTestXml());
81 Assert.Equal("<RollbackBoundary Id='rba31DvS6_ninGllmavuS.cp4RYckk' Vital='yes' Transaction='yes' LogPathVariable='WixBundleLog_rba31DvS6_ninGllmavuS.cp4RYckk' />", rollbackBoundaries[1].GetTestXml());
71 } 82 }
72 } 83 }
73 84