@joebigelow / wix-1 / commits / d5985a16

Add option to hosts to always install prereqs.

Add PrereqPackage to BundlePackage Implements 4718

Sean Hall committed May 13, 2022 at 15:39 UTC d5985a1688bc878e42ffd3ce3939fa52303cab16
35 files changed +1154 -42
src/ext/Bal/dnchost/dnchost.cpp
+19
@@ -76,6 +76,16 @@ extern "C" HRESULT WINAPI BootstrapperApplicationCreate(
76 vstate.fInitialized = TRUE;
77 }
78
79 + if (vstate.prereqData.fAlwaysInstallPrereqs && !vstate.prereqData.fCompleted)
80 + {
81 + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application since it's configured to always run before loading the runtime.");
82 +
83 + hr = CreatePrerequisiteBA(&vstate, pEngine, pArgs, pResults);
84 + BalExitOnFailure(hr, "Failed to create the pre-requisite bootstrapper application.");
85 +
86 + ExitFunction();
87 + }
88 +
89 if (!vstate.fInitializedRuntime)
90 {
91 hr = LoadRuntime(&vstate);
@@ -214,6 +224,15 @@ static HRESULT LoadDncConfiguration(
224 hr = StrAllocConcat(&pState->sczBaFactoryRuntimeConfigPath, L".runtimeconfig.json", 0);
225 BalExitOnFailure(hr, "Failed to concat extension to runtime config path.");
226
227 + hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixMbaPrereqOptions", &pixnHost);
228 + BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find WixMbaPrereqOptions element in bootstrapper application config.");
229 +
230 + if (fXmlFound)
231 + {
232 + hr = XmlGetAttributeNumber(pixnHost, L"AlwaysInstallPrereqs", reinterpret_cast<DWORD*>(&pState->prereqData.fAlwaysInstallPrereqs));
233 + BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get AlwaysInstallPrereqs value.");
234 + }
235 +
236 pState->type = DNCHOSTTYPE_FDD;
237
238 hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixDncOptions", &pixnHost);
src/ext/Bal/mbahost/mbahost.cpp
+49 -5
@@ -24,6 +24,10 @@ static HRESULT GetAppDomain(
24 static HRESULT LoadModulePaths(
25 __in MBASTATE* pState
26 );
27 +static HRESULT LoadMbaConfiguration(
28 + __in MBASTATE* pState,
29 + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs
30 + );
31 static HRESULT CheckSupportedFrameworks(
32 __in LPCWSTR wzConfigPath
33 );
@@ -96,12 +100,28 @@ extern "C" HRESULT WINAPI BootstrapperApplicationCreate(
100
101 if (!vstate.fInitialized)
102 {
103 + hr = XmlInitialize();
104 + BalExitOnFailure(hr, "Failed to initialize XML.");
105 +
106 hr = LoadModulePaths(&vstate);
107 BalExitOnFailure(hr, "Failed to load the module paths.");
108
109 + hr = LoadMbaConfiguration(&vstate, pArgs);
110 + BalExitOnFailure(hr, "Failed to get the mba configuration.");
111 +
112 vstate.fInitialized = TRUE;
113 }
114
115 + if (vstate.prereqData.fAlwaysInstallPrereqs && !vstate.prereqData.fCompleted)
116 + {
117 + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading prerequisite bootstrapper application since it's configured to always run before loading the runtime.");
118 +
119 + hr = CreatePrerequisiteBA(&vstate, pEngine, pArgs, pResults);
120 + BalExitOnFailure(hr, "Failed to create the pre-requisite bootstrapper application.");
121 +
122 + ExitFunction();
123 + }
124 +
125 if (!vstate.fInitializedRuntime)
126 {
127 hr = LoadRuntime(&vstate);
@@ -264,6 +284,35 @@ LExit:
284 return hr;
285 }
286
287 +static HRESULT LoadMbaConfiguration(
288 + __in MBASTATE* pState,
289 + __in const BOOTSTRAPPER_CREATE_ARGS* pArgs
290 + )
291 +{
292 + HRESULT hr = S_OK;
293 + IXMLDOMDocument* pixdManifest = NULL;
294 + IXMLDOMNode* pixnHost = NULL;
295 + BOOL fXmlFound = FALSE;
296 +
297 + hr = XmlLoadDocumentFromFile(pArgs->pCommand->wzBootstrapperApplicationDataPath, &pixdManifest);
298 + BalExitOnFailure(hr, "Failed to load BalManifest '%ls'", pArgs->pCommand->wzBootstrapperApplicationDataPath);
299 +
300 + hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixMbaPrereqOptions", &pixnHost);
301 + BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to find WixMbaPrereqOptions element in bootstrapper application config.");
302 +
303 + if (fXmlFound)
304 + {
305 + hr = XmlGetAttributeNumber(pixnHost, L"AlwaysInstallPrereqs", reinterpret_cast<DWORD*>(&pState->prereqData.fAlwaysInstallPrereqs));
306 + BalExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get AlwaysInstallPrereqs value.");
307 + }
308 +
309 +LExit:
310 + ReleaseObject(pixnHost);
311 + ReleaseObject(pixdManifest);
312 +
313 + return hr;
314 +}
315 +
316 // Checks whether at least one of required supported frameworks is installed via the NETFX registry keys.
317 static HRESULT CheckSupportedFrameworks(
318 __in LPCWSTR wzConfigPath
@@ -280,9 +329,6 @@ static HRESULT CheckSupportedFrameworks(
329 DWORD dwFrameworkInstalled = 0;
330 BOOL fUpdatedManifest = FALSE;
331
283 - hr = XmlInitialize();
284 - ExitOnFailure(hr, "Failed to initialize XML.");
285 -
332 hr = XmlLoadDocumentFromFile(wzConfigPath, &pixdManifest);
333 ExitOnFailure(hr, "Failed to load bootstrapper config file from path: %ls", wzConfigPath);
334
@@ -342,8 +388,6 @@ LExit:
388 ReleaseObject(pNodeList);
389 ReleaseObject(pixdManifest);
390
345 - XmlUninitialize();
346 -
391 return hr;
392 }
393
src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs
+36 -2
@@ -107,7 +107,41 @@ namespace WixToolsetTest.Bal
107 }
108
109 [Fact]
110 - public void CantBuildUsingMBAWithNoPrereqs()
110 + public void CanBuildUsingMBAWithAlwaysInstallPrereqs()
111 + {
112 + using (var fs = new DisposableFileSystem())
113 + {
114 + var baseFolder = fs.GetFolder();
115 + var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
116 + var bundleSourceFolder = TestData.Get(@"TestData\MBA");
117 + var intermediateFolder = Path.Combine(baseFolder, "obj");
118 + var baFolderPath = Path.Combine(baseFolder, "ba");
119 + var extractFolderPath = Path.Combine(baseFolder, "extract");
120 +
121 + var compileResult = WixRunner.Execute(new[]
122 + {
123 + "build",
124 + Path.Combine(bundleSourceFolder, "AlwaysInstallPrereqsBundle.wxs"),
125 + "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
126 + "-intermediateFolder", intermediateFolder,
127 + "-o", bundleFile,
128 + });
129 +
130 + compileResult.AssertSuccess();
131 +
132 + Assert.True(File.Exists(bundleFile));
133 +
134 + var extractResult = BundleExtractor.ExtractBAContainer(null, bundleFile, baFolderPath, extractFolderPath);
135 + extractResult.AssertSuccess();
136 +
137 + var wixMbaPrereqOptionsElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:WixMbaPrereqOptions");
138 + var wixMbaPrereqOptions = (XmlNode)Assert.Single(wixMbaPrereqOptionsElements);
139 + Assert.Equal("<WixMbaPrereqOptions AlwaysInstallPrereqs='1' />", wixMbaPrereqOptions.GetTestXml());
140 + }
141 + }
142 +
143 + [Fact]
144 + public void CannotBuildUsingMBAWithNoPrereqs()
145 {
146 using (var fs = new DisposableFileSystem())
147 {
@@ -133,7 +167,7 @@ namespace WixToolsetTest.Bal
167 }
168
169 [Fact]
136 - public void CantBuildUsingOverridableWrongCase()
170 + public void CannotBuildUsingOverridableWrongCase()
171 {
172 using (var fs = new DisposableFileSystem())
173 {
src/ext/Bal/test/WixToolsetTest.Bal/TestData/MBA/AlwaysInstallPrereqsBundle.wxs new
+12
@@ -0,0 +1,12 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
3 + xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
4 + <Bundle Name="AlwaysInstallPrereqsBundle" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{36E9E102-E0E4-4A91-941D-6681A49216E6}">
5 + <BootstrapperApplication>
6 + <bal:WixManagedBootstrapperApplicationHost AlwaysInstallPrereqs="yes" />
7 + </BootstrapperApplication>
8 + <Chain>
9 + <ExePackage bal:PrereqPackage="yes" Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
10 + </Chain>
11 + </Bundle>
12 +</Wix>
src/ext/Bal/wixext/BalBurnBackendExtension.cs
+1
@@ -24,6 +24,7 @@ namespace WixToolset.Bal
24 BalSymbolDefinitions.WixMbaPrereqInformation,
25 BalSymbolDefinitions.WixStdbaOptions,
26 BalSymbolDefinitions.WixStdbaOverridableVariable,
27 + BalSymbolDefinitions.WixMbaPrereqOptions,
28 };
29
30 protected override IReadOnlyCollection<IntermediateSymbolDefinition> SymbolDefinitions => BurnSymbolDefinitions;
src/ext/Bal/wixext/BalCompiler.cs
+26 -1
@@ -117,6 +117,7 @@ namespace WixToolset.Bal
117
118 switch (parentElement.Name.LocalName)
119 {
120 + case "BundlePackage":
121 case "ExePackage":
122 case "MsiPackage":
123 case "MspPackage":
@@ -216,7 +217,7 @@ namespace WixToolset.Bal
217 case "PrereqPackage":
218 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute))
219 {
219 - if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out prereqInfo))
220 + if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out _))
221 {
222 prereqInfo = section.AddSymbol(new WixMbaPrereqInformationSymbol(sourceLineNumbers)
223 {
@@ -703,6 +704,7 @@ namespace WixToolset.Bal
704 private void ParseWixManagedBootstrapperApplicationHostElement(Intermediate intermediate, IntermediateSection section, XElement node)
705 {
706 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
707 + bool alwaysInstallPrereqs = false;
708 string logoFile = null;
709 string themeFile = null;
710 string localizationFile = null;
@@ -714,6 +716,9 @@ namespace WixToolset.Bal
716 {
717 switch (attrib.Name.LocalName)
718 {
719 + case "AlwaysInstallPrereqs":
720 + alwaysInstallPrereqs = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes;
721 + break;
722 case "LogoFile":
723 logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
724 break;
@@ -792,6 +797,14 @@ namespace WixToolset.Bal
797 }
798
799 this.CreateBARef(section, sourceLineNumbers, node, baId);
800 +
801 + if (alwaysInstallPrereqs)
802 + {
803 + section.AddSymbol(new WixMbaPrereqOptionsSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixMbaPrereqOptions"))
804 + {
805 + AlwaysInstallPrereqs = 1,
806 + });
807 + }
808 }
809 }
810
@@ -802,6 +815,7 @@ namespace WixToolset.Bal
815 private void ParseWixDotNetCoreBootstrapperApplicationHostElement(Intermediate intermediate, IntermediateSection section, XElement node)
816 {
817 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
818 + bool alwaysInstallPrereqs = false;
819 string logoFile = null;
820 string themeFile = null;
821 string localizationFile = null;
@@ -814,6 +828,9 @@ namespace WixToolset.Bal
828 {
829 switch (attrib.Name.LocalName)
830 {
831 + case "AlwaysInstallPrereqs":
832 + alwaysInstallPrereqs = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes;
833 + break;
834 case "LogoFile":
835 logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
836 break;
@@ -903,6 +920,14 @@ namespace WixToolset.Bal
920 }
921
922 this.CreateBARef(section, sourceLineNumbers, node, baId);
923 +
924 + if (alwaysInstallPrereqs)
925 + {
926 + section.AddSymbol(new WixMbaPrereqOptionsSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixMbaPrereqOptions"))
927 + {
928 + AlwaysInstallPrereqs = 1,
929 + });
930 + }
931 }
932 }
933
src/ext/Bal/wixext/Symbols/BalSymbolDefinitions.cs
+5
@@ -16,6 +16,7 @@ namespace WixToolset.Bal
16 WixMbaPrereqInformation,
17 WixStdbaOptions,
18 WixStdbaOverridableVariable,
19 + WixMbaPrereqOptions,
20 }
21
22 public static partial class BalSymbolDefinitions
@@ -60,6 +61,9 @@ namespace WixToolset.Bal
61 case BalSymbolDefinitionType.WixStdbaOverridableVariable:
62 return BalSymbolDefinitions.WixStdbaOverridableVariable;
63
64 + case BalSymbolDefinitionType.WixMbaPrereqOptions:
65 + return BalSymbolDefinitions.WixMbaPrereqOptions;
66 +
67 default:
68 throw new ArgumentOutOfRangeException(nameof(type));
69 }
@@ -75,6 +79,7 @@ namespace WixToolset.Bal
79 WixMbaPrereqInformation.AddTag(BurnConstants.BootstrapperApplicationDataSymbolDefinitionTag);
80 WixStdbaOptions.AddTag(BurnConstants.BootstrapperApplicationDataSymbolDefinitionTag);
81 WixStdbaOverridableVariable.AddTag(BurnConstants.BootstrapperApplicationDataSymbolDefinitionTag);
82 + WixMbaPrereqOptions.AddTag(BurnConstants.BootstrapperApplicationDataSymbolDefinitionTag);
83 }
84 }
85 }
src/ext/Bal/wixext/Symbols/WixMbaPrereqOptionsSymbol.cs new
+47
@@ -0,0 +1,47 @@
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.Bal
4 +{
5 + using WixToolset.Data;
6 + using WixToolset.Bal.Symbols;
7 +
8 + public static partial class BalSymbolDefinitions
9 + {
10 + public static readonly IntermediateSymbolDefinition WixMbaPrereqOptions = new IntermediateSymbolDefinition(
11 + BalSymbolDefinitionType.WixMbaPrereqOptions.ToString(),
12 + new[]
13 + {
14 + new IntermediateFieldDefinition(nameof(WixMbaPrereqOptionsSymbolFields.AlwaysInstallPrereqs), IntermediateFieldType.Number),
15 + },
16 + typeof(WixMbaPrereqOptionsSymbol));
17 + }
18 +}
19 +
20 +namespace WixToolset.Bal.Symbols
21 +{
22 + using WixToolset.Data;
23 +
24 + public enum WixMbaPrereqOptionsSymbolFields
25 + {
26 + AlwaysInstallPrereqs,
27 + }
28 +
29 + public class WixMbaPrereqOptionsSymbol : IntermediateSymbol
30 + {
31 + public WixMbaPrereqOptionsSymbol() : base(BalSymbolDefinitions.WixMbaPrereqOptions, null, null)
32 + {
33 + }
34 +
35 + public WixMbaPrereqOptionsSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(BalSymbolDefinitions.WixMbaPrereqOptions, sourceLineNumber, id)
36 + {
37 + }
38 +
39 + public IntermediateField this[WixMbaPrereqOptionsSymbolFields index] => this.Fields[(int)index];
40 +
41 + public int AlwaysInstallPrereqs
42 + {
43 + get => this.Fields[(int)WixMbaPrereqOptionsSymbolFields.AlwaysInstallPrereqs].AsNumber();
44 + set => this.Set((int)WixMbaPrereqOptionsSymbolFields.AlwaysInstallPrereqs, value);
45 + }
46 + }
47 +}
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+159 -20
@@ -30,6 +30,8 @@ enum WIXSTDBA_STATE
30 WIXSTDBA_STATE_HELP,
31 WIXSTDBA_STATE_DETECTING,
32 WIXSTDBA_STATE_DETECTED,
33 + WIXSTDBA_STATE_PLANNING_PREREQS,
34 + WIXSTDBA_STATE_PLANNED_PREREQS,
35 WIXSTDBA_STATE_PLANNING,
36 WIXSTDBA_STATE_PLANNED,
37 WIXSTDBA_STATE_APPLYING,
@@ -49,6 +51,7 @@ enum WM_WIXSTDBA
51 WM_WIXSTDBA_APPLY_PACKAGES,
52 WM_WIXSTDBA_CHANGE_STATE,
53 WM_WIXSTDBA_SHOW_FAILURE,
54 + WM_WIXSTDBA_PLAN_PREREQS,
55 };
56
57 // This enum must be kept in the same order as the vrgwzPageNames array.
@@ -217,9 +220,11 @@ public: // IBootstrapperApplication
220 : "A restart is required by the prerequisites but the user delayed it. The bootstrapper application will be reloaded after the computer is restarted.");
221 }
222 }
220 - else if (m_fPrereqInstalled)
223 + else if (m_fPrereqInstalled || m_fPrereqSkipped)
224 {
222 - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "The prerequisites were successfully installed. The bootstrapper application will be reloaded.");
225 + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, m_fPrereqInstalled
226 + ? "The prerequisites were successfully installed. The bootstrapper application will be reloaded."
227 + : "The prerequisites were already installed. The bootstrapper application will be reloaded.");
228 *pAction = BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER;
229 m_pPrereqData->fCompleted = TRUE;
230 }
@@ -275,13 +280,16 @@ public: // IBootstrapperApplication
280 hr = S_OK;
281 }
282
278 - // If the UI should be visible, display it now and hide the splash screen
279 - if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display)
283 + if (!m_fPreplanPrereqs)
284 {
281 - ::ShowWindow(m_pTheme->hwndParent, SW_SHOW);
282 - }
285 + // If the UI should be visible, display it now and hide the splash screen
286 + if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display)
287 + {
288 + ::ShowWindow(m_pTheme->hwndParent, SW_SHOW);
289 + }
290
284 - m_pEngine->CloseSplashScreen();
291 + m_pEngine->CloseSplashScreen();
292 + }
293
294 return __super::OnDetectBegin(fCached, registrationType, cPackages, pfCancel);
295 }
@@ -331,24 +339,37 @@ public: // IBootstrapperApplication
339 if (fEvaluateConditions)
340 {
341 hrStatus = EvaluateConditions();
334 -
335 - if (FAILED(hrStatus))
336 - {
337 - fSkipToPlan = FALSE;
338 - }
342 }
343
344 SetState(WIXSTDBA_STATE_DETECTED, hrStatus);
345
343 - if (fSkipToPlan)
346 + if (SUCCEEDED(hrStatus))
347 {
345 - ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action);
348 + if (m_fPreplanPrereqs)
349 + {
350 + ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PREREQS, 0, BOOTSTRAPPER_ACTION_INSTALL);
351 + }
352 + else if (fSkipToPlan)
353 + {
354 + ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action);
355 + }
356 }
357
358 return hr;
359 }
360
361
362 + virtual STDMETHODIMP OnPlanBegin(
363 + __in DWORD cPackages,
364 + __in BOOL* pfCancel
365 + )
366 + {
367 + m_fPrereqPackagePlanned = FALSE;
368 +
369 + return __super::OnPlanBegin(cPackages, pfCancel);
370 + }
371 +
372 +
373 virtual STDMETHODIMP OnPlanRelatedBundleType(
374 __in_z LPCWSTR wzBundleId,
375 __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE recommendedType,
@@ -484,24 +505,97 @@ public: // IBootstrapperApplication
505 }
506
507
508 + virtual STDMETHODIMP OnPlannedPackage(
509 + __in_z LPCWSTR wzPackageId,
510 + __in BOOTSTRAPPER_ACTION_STATE execute,
511 + __in BOOTSTRAPPER_ACTION_STATE rollback,
512 + __in BOOL fPlannedCache,
513 + __in BOOL fPlannedUncache
514 + )
515 + {
516 + if (m_fPrereq && BOOTSTRAPPER_ACTION_STATE_NONE != execute)
517 + {
518 + m_fPrereqPackagePlanned = TRUE;
519 + }
520 +
521 + return __super::OnPlannedPackage(wzPackageId, execute, rollback, fPlannedCache, fPlannedUncache);
522 + }
523 +
524 +
525 virtual STDMETHODIMP OnPlanComplete(
526 __in HRESULT hrStatus
527 )
528 {
529 HRESULT hr = S_OK;
530 + BOOL fPlannedPrereqs = WIXSTDBA_STATE_PLANNING_PREREQS == m_state;
531 + WIXSTDBA_STATE completedState = WIXSTDBA_STATE_PLANNED;
532 + BOOL fApply = TRUE;
533
493 - SetState(WIXSTDBA_STATE_PLANNED, hrStatus);
534 + if (fPlannedPrereqs)
535 + {
536 + if (SUCCEEDED(hrStatus) && !m_fPrereqPackagePlanned)
537 + {
538 + // Nothing to do, so close and let the parent BA take over.
539 + m_fPrereqSkipped = TRUE;
540 + SetState(WIXSTDBA_STATE_APPLIED, S_OK);
541 + ExitFunction();
542 + }
543 + else if (BOOTSTRAPPER_ACTION_HELP == m_command.action)
544 + {
545 + // If prereq packages were planned then the managed BA probably can't be loaded, so show the help from this BA.
546
495 - if (SUCCEEDED(hrStatus))
547 + // Need to force the state change since normally moving backwards is prevented.
548 + ::PostMessageW(m_hWnd, WM_WIXSTDBA_CHANGE_STATE, 0, WIXSTDBA_STATE_HELP);
549 +
550 + ::PostMessageW(m_hWnd, WM_WIXSTDBA_SHOW_HELP, 0, 0);
551 +
552 + ExitFunction();
553 + }
554 +
555 + completedState = WIXSTDBA_STATE_PLANNED_PREREQS;
556 + }
557 +
558 + SetState(completedState, hrStatus);
559 +
560 + if (FAILED(hrStatus))
561 + {
562 + ExitFunction();
563 + }
564 +
565 + if (fPlannedPrereqs)
566 + {
567 + // If the UI should be visible, display it now and hide the splash screen
568 + if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display)
569 + {
570 + ::ShowWindow(m_pTheme->hwndParent, SW_SHOW);
571 + }
572 +
573 + m_pEngine->CloseSplashScreen();
574 +
575 + fApply = BOOTSTRAPPER_DISPLAY_FULL > m_command.display ||
576 + BOOTSTRAPPER_RESUME_TYPE_REBOOT == m_command.resumeType;
577 + }
578 +
579 + if (fApply)
580 {
581 ::PostMessageW(m_hWnd, WM_WIXSTDBA_APPLY_PACKAGES, 0, 0);
582 }
583
584 + LExit:
585 + return hr;
586 + }
587 +
588 +
589 + virtual STDMETHODIMP OnApplyBegin(
590 + __in DWORD dwPhaseCount,
591 + __in BOOL* pfCancel
592 + )
593 + {
594 m_fStartedExecution = FALSE;
595 m_dwCalculatedCacheProgress = 0;
596 m_dwCalculatedExecuteProgress = 0;
597
504 - return hr;
598 + return __super::OnApplyBegin(dwPhaseCount, pfCancel);
599 }
600
601
@@ -2149,6 +2243,7 @@ private:
2243 BOOL fRet = FALSE;
2244 MSG msg = { };
2245 DWORD dwQuit = 0;
2246 + WM_WIXSTDBA firstAction = WM_WIXSTDBA_DETECT_PACKAGES;
2247
2248 // Initialize COM and theme.
2249 hr = ::CoInitialize(NULL);
@@ -2169,15 +2264,21 @@ private:
2264 if (FAILED(pThis->m_hrFinal))
2265 {
2266 pThis->SetState(WIXSTDBA_STATE_FAILED, hr);
2172 - ::PostMessageW(pThis->m_hWnd, WM_WIXSTDBA_SHOW_FAILURE, 0, 0);
2267 + firstAction = WM_WIXSTDBA_SHOW_FAILURE;
2268 }
2269 else
2270 {
2271 // Okay, we're ready for packages now.
2272 pThis->SetState(WIXSTDBA_STATE_INITIALIZED, hr);
2178 - ::PostMessageW(pThis->m_hWnd, BOOTSTRAPPER_ACTION_HELP == pThis->m_command.action ? WM_WIXSTDBA_SHOW_HELP : WM_WIXSTDBA_DETECT_PACKAGES, 0, 0);
2273 +
2274 + if (!pThis->m_fPreplanPrereqs && BOOTSTRAPPER_ACTION_HELP == pThis->m_command.action)
2275 + {
2276 + firstAction = WM_WIXSTDBA_SHOW_HELP;
2277 + }
2278 }
2279
2280 + ::PostMessageW(pThis->m_hWnd, firstAction, 0, 0);
2281 +
2282 // message pump
2283 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
2284 {
@@ -2647,7 +2748,7 @@ private:
2748
2749 // Calculate the window style based on the theme style and command display value.
2750 dwWindowStyle = m_pTheme->dwStyle;
2650 - if (BOOTSTRAPPER_DISPLAY_NONE >= m_command.display)
2751 + if (BOOTSTRAPPER_DISPLAY_NONE >= m_command.display || m_fPreplanPrereqs)
2752 {
2753 dwWindowStyle &= ~WS_VISIBLE;
2754 }
@@ -2850,6 +2951,10 @@ private:
2951 pBA->OnShowFailure();
2952 return 0;
2953
2954 + case WM_WIXSTDBA_PLAN_PREREQS:
2955 + pBA->OnPlanPrereqs(static_cast<BOOTSTRAPPER_ACTION>(lParam));
2956 + return 0;
2957 +
2958 case WM_THMUTIL_CONTROL_WM_COMMAND:
2959 return pBA->OnThemeControlWmCommand(reinterpret_cast<THEME_CONTROLWMCOMMAND_ARGS*>(wParam), reinterpret_cast<THEME_CONTROLWMCOMMAND_RESULTS*>(lParam));
2960
@@ -3140,6 +3245,30 @@ private:
3245 }
3246
3247
3248 + //
3249 + // OnPlanPrereqs - preplan the packages to see if the preqba can be skipped.
3250 + //
3251 + void OnPlanPrereqs(
3252 + __in BOOTSTRAPPER_ACTION action
3253 + )
3254 + {
3255 + HRESULT hr = S_OK;
3256 +
3257 + m_plannedAction = action;
3258 +
3259 + SetState(WIXSTDBA_STATE_PLANNING_PREREQS, hr);
3260 +
3261 + hr = m_pEngine->Plan(action);
3262 + BalExitOnFailure(hr, "Failed to start planning prereq packages.");
3263 +
3264 + LExit:
3265 + if (FAILED(hr))
3266 + {
3267 + SetState(WIXSTDBA_STATE_PLANNING_PREREQS, hr);
3268 + }
3269 + }
3270 +
3271 +
3272 //
3273 // OnApply - apply the packages.
3274 //
@@ -3838,6 +3967,8 @@ LExit:
3967 break;
3968
3969 case WIXSTDBA_STATE_DETECTED: __fallthrough;
3970 + case WIXSTDBA_STATE_PLANNING_PREREQS: __fallthrough;
3971 + case WIXSTDBA_STATE_PLANNED_PREREQS: __fallthrough;
3972 case WIXSTDBA_STATE_PLANNING: __fallthrough;
3973 case WIXSTDBA_STATE_PLANNED: __fallthrough;
3974 case WIXSTDBA_STATE_APPLYING: __fallthrough;
@@ -3874,6 +4005,8 @@ LExit:
4005 break;
4006
4007 case WIXSTDBA_STATE_DETECTED:
4008 + case WIXSTDBA_STATE_PLANNING_PREREQS: __fallthrough;
4009 + case WIXSTDBA_STATE_PLANNED_PREREQS: __fallthrough;
4010 switch (m_command.action)
4011 {
4012 case BOOTSTRAPPER_ACTION_INSTALL:
@@ -4124,7 +4257,10 @@ public:
4257
4258 m_pPrereqData = pPrereqData;
4259 m_fPrereq = NULL != pPrereqData;
4260 + m_fPreplanPrereqs = m_fPrereq && m_pPrereqData->fAlwaysInstallPrereqs;
4261 + m_fPrereqPackagePlanned = FALSE;
4262 m_fPrereqInstalled = FALSE;
4263 + m_fPrereqSkipped = FALSE;
4264
4265 pEngine->AddRef();
4266 m_pEngine = pEngine;
@@ -4405,7 +4541,10 @@ private:
4541
4542 PREQBA_DATA* m_pPrereqData;
4543 BOOL m_fPrereq;
4544 + BOOL m_fPreplanPrereqs;
4545 + BOOL m_fPrereqPackagePlanned;
4546 BOOL m_fPrereqInstalled;
4547 + BOOL m_fPrereqSkipped;
4548
4549 ITaskbarList3* m_pTaskbarList;
4550 UINT m_uTaskbarButtonCreatedMessage;
src/ext/Bal/wixstdba/inc/preqba.h
+1
@@ -5,6 +5,7 @@
5 struct PREQBA_DATA
6 {
7 HRESULT hrHostInitialization;
8 + BOOL fAlwaysInstallPrereqs;
9 BOOL fCompleted;
10 };
11
src/test/burn/TestBA/TestBA.cs
+7
@@ -146,6 +146,13 @@ namespace WixToolset.Test.BA
146
147 this.wait.WaitOne();
148
149 + if (this.action == LaunchAction.Help)
150 + {
151 + this.Log("This is a BA for automated testing");
152 + this.Engine.Quit(0);
153 + return;
154 + }
155 +
156 this.redetectRemaining = redetectCount;
157 for (int i = -1; i < redetectCount; i++)
158 {
src/test/burn/TestData/PrereqBaTests/BundleA/BundleA.wixproj
+4 -1
@@ -13,9 +13,12 @@
13 </ItemGroup>
14 <ItemGroup>
15 <ProjectReference Include="..\PackageA\PackageA.wixproj" />
16 - <ProjectReference Include="..\PackageF\PackageF.wixproj" />
16 + <ProjectReference Include="..\PackageC\PackageC.wixproj" />
17 + <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
18 + <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
19 </ItemGroup>
20 <ItemGroup>
21 <PackageReference Include="WixToolset.Bal.wixext" />
22 + <PackageReference Include="WixToolset.Util.wixext" />
23 </ItemGroup>
24 </Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleA/BundleA.wxs
+10 -2
@@ -1,22 +1,30 @@
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
4 -<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
4 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5 <Fragment>
6 <BootstrapperApplication Id="BrokenDnc">
7 <Payload SourceFile="!(bindpath.dncx86)\TestBA.deps.json" />
8 <Payload SourceFile="!(bindpath.dncx86)\TestBA.dll" bal:BAFactoryAssembly="yes" />
9 + <Payload Name="good.runtimeconfig.json" SourceFile="!(bindpath.dncx86)\TestBA.runtimeconfig.json" />
10 <Payload Name="TestBA.runtimeconfig.json" SourceFile="bad.runtimeconfig.json" />
11 <Payload SourceFile="!(bindpath.dncx86)\mbanative.dll" />
12 <Payload SourceFile="!(bindpath.dncx86)\WixToolset.Mba.Core.dll" />
13 + <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
14 <bal:WixDotNetCoreBootstrapperApplicationHost />
15 </BootstrapperApplication>
16 </Fragment>
17
18 <Fragment>
19 + <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.runtimeconfig.json" Result="exists" />
20 + <Variable Name="CAUSEINFINITELOOP" bal:Overridable="yes" />
21 +
22 <PackageGroup Id="BundlePackages">
23 + <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
24 + Permanent="yes" DetectCondition="NOT GoodConfigPresent OR CAUSEINFINITELOOP = 1"
25 + InstallArguments="&quot;[BARuntimeDirectory]TestBA.runtimeconfig.json&quot; &quot;[BARuntimeDirectory]good.runtimeconfig.json&quot; &quot;[BARuntimeDirectory]bad.runtimeconfig.json&quot;" />
26 <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" bal:PrereqPackage="yes" Permanent="yes" />
19 - <MsiPackage Id="PackageF" SourceFile="$(var.PackageF.TargetPath)" Cache="force" />
27 + <MsiPackage Id="PackageC" SourceFile="$(var.PackageC.TargetPath)" Cache="force" />
28 </PackageGroup>
29 </Fragment>
30 </Wix>
src/test/burn/TestData/PrereqBaTests/BundleB/BundleB.wixproj
+4 -1
@@ -13,9 +13,12 @@
13 </ItemGroup>
14 <ItemGroup>
15 <ProjectReference Include="..\PackageB\PackageB.wixproj" />
16 - <ProjectReference Include="..\PackageF\PackageF.wixproj" />
16 + <ProjectReference Include="..\PackageC\PackageC.wixproj" />
17 + <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
18 + <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
19 </ItemGroup>
20 <ItemGroup>
21 <PackageReference Include="WixToolset.Bal.wixext" />
22 + <PackageReference Include="WixToolset.Util.wixext" />
23 </ItemGroup>
24 </Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleB/BundleB.wxs
+10 -2
@@ -1,21 +1,29 @@
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
4 -<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
4 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5 <Fragment>
6 <BootstrapperApplication Id="BrokenMba">
7 + <Payload Name="good.config" SourceFile="!(bindpath.net2x86)\TestBA.BootstrapperCore.config" />
8 <Payload Name="WixToolset.Mba.Host.config" SourceFile="bad.config" />
9 <Payload SourceFile="!(bindpath.net2x86)\TestBA.dll" />
10 <Payload SourceFile="!(bindpath.net2x86)\mbanative.dll" />
11 <Payload SourceFile="!(bindpath.net2x86)\WixToolset.Mba.Core.dll" />
12 + <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
13 <bal:WixManagedBootstrapperApplicationHost />
14 </BootstrapperApplication>
15 </Fragment>
16
17 <Fragment>
18 + <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.config" Result="exists" />
19 + <Variable Name="CAUSEINFINITELOOP" bal:Overridable="yes" />
20 +
21 <PackageGroup Id="BundlePackages">
22 + <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
23 + Permanent="yes" DetectCondition="NOT GoodConfigPresent OR CAUSEINFINITELOOP = 1"
24 + InstallArguments="&quot;[BARuntimeDirectory]WixToolset.Mba.Host.config&quot; &quot;[BARuntimeDirectory]good.config&quot; &quot;[BARuntimeDirectory]bad.config&quot;" />
25 <MsiPackage Id="PackageB" SourceFile="$(var.PackageB.TargetPath)" bal:PrereqPackage="yes" Permanent="yes" />
18 - <MsiPackage Id="PackageF" SourceFile="$(var.PackageF.TargetPath)" />
26 + <MsiPackage Id="PackageC" SourceFile="$(var.PackageC.TargetPath)" />
27 </PackageGroup>
28 </Fragment>
29 </Wix>
src/test/burn/TestData/PrereqBaTests/BundleC/BundleC.wixproj new
+24
@@ -0,0 +1,24 @@
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 +<Project Sdk="WixToolset.Sdk">
3 + <PropertyGroup>
4 + <OutputType>Bundle</OutputType>
5 + <BA>BrokenDncAlwaysPrereq</BA>
6 + <UpgradeCode>{D2763AB7-979B-485C-AE52-DD03C23CCB93}</UpgradeCode>
7 + </PropertyGroup>
8 + <ItemGroup>
9 + <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10 + </ItemGroup>
11 + <ItemGroup>
12 + <BindInputPaths Include="$(BaseOutputPath)$(Configuration)\net6.0-windows\win-x86" BindName="dncx86" />
13 + </ItemGroup>
14 + <ItemGroup>
15 + <ProjectReference Include="..\PackageA\PackageA.wixproj" />
16 + <ProjectReference Include="..\PackageC\PackageC.wixproj" />
17 + <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
18 + <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
19 + </ItemGroup>
20 + <ItemGroup>
21 + <PackageReference Include="WixToolset.Bal.wixext" />
22 + <PackageReference Include="WixToolset.Util.wixext" />
23 + </ItemGroup>
24 +</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleC/BundleC.wxs new
+30
@@ -0,0 +1,30 @@
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 +
4 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5 + <Fragment>
6 + <BootstrapperApplication Id="BrokenDncAlwaysPrereq">
7 + <Payload SourceFile="!(bindpath.dncx86)\TestBA.deps.json" />
8 + <Payload SourceFile="!(bindpath.dncx86)\TestBA.dll" bal:BAFactoryAssembly="yes" />
9 + <Payload Name="good.runtimeconfig.json" SourceFile="!(bindpath.dncx86)\TestBA.runtimeconfig.json" />
10 + <Payload Name="TestBA.runtimeconfig.json" SourceFile="bad.runtimeconfig.json" />
11 + <Payload SourceFile="!(bindpath.dncx86)\mbanative.dll" />
12 + <Payload SourceFile="!(bindpath.dncx86)\WixToolset.Mba.Core.dll" />
13 + <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
14 + <bal:WixDotNetCoreBootstrapperApplicationHost AlwaysInstallPrereqs="yes" />
15 + </BootstrapperApplication>
16 + </Fragment>
17 +
18 + <Fragment>
19 + <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.runtimeconfig.json" Result="exists" />
20 + <Variable Name="CAUSEINFINITELOOP" bal:Overridable="yes" />
21 +
22 + <PackageGroup Id="BundlePackages">
23 + <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
24 + Permanent="yes" DetectCondition="NOT GoodConfigPresent OR CAUSEINFINITELOOP = 1"
25 + InstallArguments="&quot;[BARuntimeDirectory]TestBA.runtimeconfig.json&quot; &quot;[BARuntimeDirectory]good.runtimeconfig.json&quot; &quot;[BARuntimeDirectory]bad.runtimeconfig.json&quot;" />
26 + <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" bal:PrereqPackage="yes" Permanent="yes" />
27 + <MsiPackage Id="PackageC" SourceFile="$(var.PackageC.TargetPath)" Cache="force" />
28 + </PackageGroup>
29 + </Fragment>
30 +</Wix>
src/test/burn/TestData/PrereqBaTests/BundleC/bad.runtimeconfig.json new
+10
@@ -0,0 +1,10 @@
1 +{
2 + "runtimeOptions": {
3 + "tfm": "net5.5",
4 + "rollForward": "Disable",
5 + "framework": {
6 + "name": "Microsoft.WindowsDesktop.App",
7 + "version": "5.5.0"
8 + }
9 + }
10 +}
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleD/BundleD.wixproj new
+24
@@ -0,0 +1,24 @@
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 +<Project Sdk="WixToolset.Sdk">
3 + <PropertyGroup>
4 + <OutputType>Bundle</OutputType>
5 + <BA>BrokenMbaAlwaysPrereq</BA>
6 + <UpgradeCode>{415CA128-60E1-4D16-ACE8-A1D43E98B997}</UpgradeCode>
7 + </PropertyGroup>
8 + <ItemGroup>
9 + <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10 + </ItemGroup>
11 + <ItemGroup>
12 + <BindInputPaths Include="$(BaseOutputPath)$(Configuration)\net35\win-x86" BindName="net2x86" />
13 + </ItemGroup>
14 + <ItemGroup>
15 + <ProjectReference Include="..\PackageB\PackageB.wixproj" />
16 + <ProjectReference Include="..\PackageC\PackageC.wixproj" />
17 + <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
18 + <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
19 + </ItemGroup>
20 + <ItemGroup>
21 + <PackageReference Include="WixToolset.Bal.wixext" />
22 + <PackageReference Include="WixToolset.Util.wixext" />
23 + </ItemGroup>
24 +</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleD/BundleD.wxs new
+29
@@ -0,0 +1,29 @@
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 +
4 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5 + <Fragment>
6 + <BootstrapperApplication Id="BrokenMbaAlwaysPrereq">
7 + <Payload Name="good.config" SourceFile="!(bindpath.net2x86)\TestBA.BootstrapperCore.config" />
8 + <Payload Name="WixToolset.Mba.Host.config" SourceFile="bad.config" />
9 + <Payload SourceFile="!(bindpath.net2x86)\TestBA.dll" />
10 + <Payload SourceFile="!(bindpath.net2x86)\mbanative.dll" />
11 + <Payload SourceFile="!(bindpath.net2x86)\WixToolset.Mba.Core.dll" />
12 + <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
13 + <bal:WixManagedBootstrapperApplicationHost AlwaysInstallPrereqs="yes" />
14 + </BootstrapperApplication>
15 + </Fragment>
16 +
17 + <Fragment>
18 + <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.config" Result="exists" />
19 + <Variable Name="CAUSEINFINITELOOP" bal:Overridable="yes" />
20 +
21 + <PackageGroup Id="BundlePackages">
22 + <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
23 + Permanent="yes" DetectCondition="NOT GoodConfigPresent OR CAUSEINFINITELOOP = 1"
24 + InstallArguments="&quot;[BARuntimeDirectory]WixToolset.Mba.Host.config&quot; &quot;[BARuntimeDirectory]good.config&quot; &quot;[BARuntimeDirectory]bad.config&quot;" />
25 + <MsiPackage Id="PackageB" SourceFile="$(var.PackageB.TargetPath)" bal:PrereqPackage="yes" Permanent="yes" />
26 + <MsiPackage Id="PackageC" SourceFile="$(var.PackageC.TargetPath)" />
27 + </PackageGroup>
28 + </Fragment>
29 +</Wix>
src/test/burn/TestData/PrereqBaTests/BundleD/bad.config new
+17
@@ -0,0 +1,17 @@
1 +<?xml version="1.0" encoding="utf-8" ?>
2 +<!-- 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. -->
3 +
4 +
5 +<configuration>
6 + <configSections>
7 + <sectionGroup name="wix.bootstrapper" type="WixToolset.Mba.Host.BootstrapperSectionGroup, WixToolset.Mba.Host">
8 + <section name="host" type="WixToolset.Mba.Host.HostSection, WixToolset.Mba.Host" />
9 + </sectionGroup>
10 + </configSections>
11 + <startup>
12 + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v5.8" />
13 + </startup>
14 + <wix.bootstrapper>
15 + <host assemblyName="TestBA" />
16 + </wix.bootstrapper>
17 +</configuration>
src/test/burn/TestData/PrereqBaTests/BundleE/BundleE.wixproj new
+22
@@ -0,0 +1,22 @@
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 +<Project Sdk="WixToolset.Sdk">
3 + <PropertyGroup>
4 + <OutputType>Bundle</OutputType>
5 + <BA>DncAlwaysPrereq</BA>
6 + <UpgradeCode>{2F61ECD8-C28B-4FF9-9609-0E9633716CF9}</UpgradeCode>
7 + </PropertyGroup>
8 + <ItemGroup>
9 + <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10 + </ItemGroup>
11 + <ItemGroup>
12 + <BindInputPaths Include="$(BaseOutputPath)$(Configuration)\net6.0-windows\win-x86" BindName="dncx86" />
13 + </ItemGroup>
14 + <ItemGroup>
15 + <ProjectReference Include="..\PrereqBaf\PrereqBaf.vcxproj" />
16 + <ProjectReference Include="..\ReplaceConfig\ReplaceConfig.vcxproj" />
17 + </ItemGroup>
18 + <ItemGroup>
19 + <PackageReference Include="WixToolset.Bal.wixext" />
20 + <PackageReference Include="WixToolset.Util.wixext" />
21 + </ItemGroup>
22 +</Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/BundleE/BundleE.wxs new
+26
@@ -0,0 +1,26 @@
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 +
4 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5 + <Fragment>
6 + <BootstrapperApplication Id="DncAlwaysPrereq">
7 + <Payload SourceFile="!(bindpath.dncx86)\TestBA.deps.json" />
8 + <Payload SourceFile="!(bindpath.dncx86)\TestBA.dll" bal:BAFactoryAssembly="yes" />
9 + <Payload SourceFile="!(bindpath.dncx86)\TestBA.runtimeconfig.json" />
10 + <Payload SourceFile="!(bindpath.dncx86)\mbanative.dll" />
11 + <Payload SourceFile="!(bindpath.dncx86)\WixToolset.Mba.Core.dll" />
12 + <Payload SourceFile="$(var.PrereqBaf.TargetPath)" bal:BAFunctions="yes" />
13 + <bal:WixDotNetCoreBootstrapperApplicationHost AlwaysInstallPrereqs="yes" />
14 + </BootstrapperApplication>
15 + </Fragment>
16 +
17 + <Fragment>
18 + <util:FileSearch Variable="GoodConfigPresent" Path="[BARuntimeDirectory]\good.runtimeconfig.json" Result="exists" />
19 +
20 + <PackageGroup Id="BundlePackages">
21 + <ExePackage SourceFile="$(var.ReplaceConfig.TargetPath)" bal:PrereqPackage="yes"
22 + Permanent="yes" DetectCondition="NOT GoodConfigPresent"
23 + InstallArguments="&quot;[BARuntimeDirectory]TestBA.runtimeconfig.json&quot; &quot;[BARuntimeDirectory]good.runtimeconfig.json&quot; &quot;[BARuntimeDirectory]bad.runtimeconfig.json&quot;" />
24 + </PackageGroup>
25 + </Fragment>
26 +</Wix>
src/test/burn/TestData/PrereqBaTests/PackageC/PackageC.wixproj renamed
+1 -4
@@ -4,9 +4,6 @@
4 <UpgradeCode>{7DEEE928-CD7F-49AD-8000-2ED6339D8A78}</UpgradeCode>
5 </PropertyGroup>
6 <ItemGroup>
7 - <Compile Include="..\..\Templates\PackageFail.wxs" Link="PackageFail.wxs" />
8 - </ItemGroup>
9 - <ItemGroup>
10 - <PackageReference Include="WixToolset.Util.wixext" />
7 + <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
8 </ItemGroup>
9 </Project>
\ No newline at end of file
src/test/burn/TestData/PrereqBaTests/PrereqBaf/PrereqBaf.cpp new
+79
@@ -0,0 +1,79 @@
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 +#include "precomp.h"
4 +#include "BalBaseBAFunctions.h"
5 +#include "BalBaseBAFunctionsProc.h"
6 +
7 +class CPrereqBaf : public CBalBaseBAFunctions
8 +{
9 +public: // IBAFunctions
10 +
11 +public: //IBootstrapperApplication
12 +
13 + virtual STDMETHODIMP OnDetectBegin(
14 + __in BOOL /*fCached*/,
15 + __in BOOTSTRAPPER_REGISTRATION_TYPE /*registrationType*/,
16 + __in DWORD /*cPackages*/,
17 + __inout BOOL* /*pfCancel*/
18 + )
19 + {
20 + HRESULT hr = S_OK;
21 +
22 + hr = m_pEngine->SetVariableString(L"BARuntimeDirectory", m_command.wzBootstrapperWorkingFolder, FALSE);
23 + ExitOnFailure(hr, "Failed to set BARuntimeDirectory");
24 +
25 + LExit:
26 + return hr;
27 + }
28 +
29 +private:
30 +
31 +public:
32 + //
33 + // Constructor - initialize member variables.
34 + //
35 + CPrereqBaf(
36 + __in HMODULE hModule,
37 + __in IBootstrapperEngine* pEngine,
38 + __in const BA_FUNCTIONS_CREATE_ARGS* pArgs
39 + ) : CBalBaseBAFunctions(hModule, pEngine, pArgs)
40 + {
41 + }
42 +
43 + //
44 + // Destructor - release member variables.
45 + //
46 + ~CPrereqBaf()
47 + {
48 + }
49 +
50 +private:
51 +};
52 +
53 +
54 +HRESULT WINAPI CreateBAFunctions(
55 + __in HMODULE hModule,
56 + __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
57 + __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
58 + )
59 +{
60 + HRESULT hr = S_OK;
61 + CPrereqBaf* pBAFunctions = NULL;
62 + IBootstrapperEngine* pEngine = NULL;
63 +
64 + hr = BalInitializeFromCreateArgs(pArgs->pBootstrapperCreateArgs, &pEngine);
65 + ExitOnFailure(hr, "Failed to initialize Bal.");
66 +
67 + pBAFunctions = new CPrereqBaf(hModule, pEngine, pArgs);
68 + ExitOnNull(pBAFunctions, hr, E_OUTOFMEMORY, "Failed to create new CPrereqBaf object.");
69 +
70 + pResults->pfnBAFunctionsProc = BalBaseBAFunctionsProc;
71 + pResults->pvBAFunctionsProcContext = pBAFunctions;
72 + pBAFunctions = NULL;
73 +
74 +LExit:
75 + ReleaseObject(pBAFunctions);
76 + ReleaseObject(pEngine);
77 +
78 + return hr;
79 +}
src/test/burn/TestData/PrereqBaTests/PrereqBaf/PrereqBaf.def new
+6
@@ -0,0 +1,6 @@
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 +
4 +EXPORTS
5 + BAFunctionsCreate
6 + BAFunctionsDestroy
src/test/burn/TestData/PrereqBaTests/PrereqBaf/PrereqBaf.vcxproj new
+66
@@ -0,0 +1,66 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<!-- 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. -->
3 +
4 +<Project DefaultTargets="Build" Toolsxmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 + <ItemGroup Label="ProjectConfigurations">
6 + <ProjectConfiguration Include="Debug|ARM64">
7 + <Configuration>Debug</Configuration>
8 + <Platform>ARM64</Platform>
9 + </ProjectConfiguration>
10 + <ProjectConfiguration Include="Release|ARM64">
11 + <Configuration>Release</Configuration>
12 + <Platform>ARM64</Platform>
13 + </ProjectConfiguration>
14 + <ProjectConfiguration Include="Debug|Win32">
15 + <Configuration>Debug</Configuration>
16 + <Platform>Win32</Platform>
17 + </ProjectConfiguration>
18 + <ProjectConfiguration Include="Release|Win32">
19 + <Configuration>Release</Configuration>
20 + <Platform>Win32</Platform>
21 + </ProjectConfiguration>
22 + <ProjectConfiguration Include="Debug|x64">
23 + <Configuration>Debug</Configuration>
24 + <Platform>x64</Platform>
25 + </ProjectConfiguration>
26 + <ProjectConfiguration Include="Release|x64">
27 + <Configuration>Release</Configuration>
28 + <Platform>x64</Platform>
29 + </ProjectConfiguration>
30 + </ItemGroup>
31 +
32 + <PropertyGroup Label="Globals">
33 + <ProjectGuid>{C0A11DDB-6CCE-44EC-88FD-93910C2916E3}</ProjectGuid>
34 + <ConfigurationType>DynamicLibrary</ConfigurationType>
35 + <CharacterSet>Unicode</CharacterSet>
36 + <TargetName>PrereqBaf</TargetName>
37 + <ProjectModuleDefinitionFile>PrereqBaf.def</ProjectModuleDefinitionFile>
38 + <IsWixTestProject>true</IsWixTestProject>
39 + </PropertyGroup>
40 +
41 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
42 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
43 +
44 + <PropertyGroup>
45 + <ProjectAdditionalLinkLibraries>comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib;wininet.lib</ProjectAdditionalLinkLibraries>
46 + </PropertyGroup>
47 +
48 + <ItemGroup>
49 + <ClCompile Include="precomp.cpp">
50 + <PrecompiledHeader>Create</PrecompiledHeader>
51 + </ClCompile>
52 + <ClCompile Include="PrereqBaf.cpp" />
53 + </ItemGroup>
54 + <ItemGroup>
55 + <ClInclude Include="precomp.h" />
56 + </ItemGroup>
57 + <ItemGroup>
58 + <None Include="PrereqBaf.def" />
59 + </ItemGroup>
60 +
61 + <ItemGroup>
62 + <PackageReference Include="WixToolset.BalUtil" />
63 + </ItemGroup>
64 +
65 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
66 +</Project>
src/test/burn/TestData/PrereqBaTests/PrereqBaf/precomp.cpp new
+48
@@ -0,0 +1,48 @@
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 +#include "precomp.h"
4 +
5 +static HINSTANCE vhInstance = NULL;
6 +
7 +extern "C" BOOL WINAPI DllMain(
8 + IN HINSTANCE hInstance,
9 + IN DWORD dwReason,
10 + IN LPVOID /* pvReserved */
11 + )
12 +{
13 + switch (dwReason)
14 + {
15 + case DLL_PROCESS_ATTACH:
16 + ::DisableThreadLibraryCalls(hInstance);
17 + vhInstance = hInstance;
18 + break;
19 +
20 + case DLL_PROCESS_DETACH:
21 + vhInstance = NULL;
22 + break;
23 + }
24 +
25 + return TRUE;
26 +}
27 +
28 +extern "C" HRESULT WINAPI BAFunctionsCreate(
29 + __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
30 + __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
31 + )
32 +{
33 + HRESULT hr = S_OK;
34 +
35 + hr = CreateBAFunctions(vhInstance, pArgs, pResults);
36 + BalExitOnFailure(hr, "Failed to create BAFunctions interface.");
37 +
38 +LExit:
39 + return hr;
40 +}
41 +
42 +extern "C" void WINAPI BAFunctionsDestroy(
43 + __in const BA_FUNCTIONS_DESTROY_ARGS* /*pArgs*/,
44 + __inout BA_FUNCTIONS_DESTROY_RESULTS* /*pResults*/
45 + )
46 +{
47 + BalUninitialize();
48 +}
src/test/burn/TestData/PrereqBaTests/PrereqBaf/precomp.h new
+31
@@ -0,0 +1,31 @@
1 +#pragma once
2 +// 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.
3 +
4 +
5 +#include <windows.h>
6 +#include <msiquery.h>
7 +#include <objbase.h>
8 +#include <shlobj.h>
9 +#include <shlwapi.h>
10 +#include <stdlib.h>
11 +#include <strsafe.h>
12 +#include <CommCtrl.h>
13 +
14 +#include "dutil.h"
15 +#include "dictutil.h"
16 +#include "fileutil.h"
17 +#include "locutil.h"
18 +#include "pathutil.h"
19 +#include "strutil.h"
20 +
21 +#include "BalBaseBootstrapperApplication.h"
22 +#include "balutil.h"
23 +
24 +#include "BAFunctions.h"
25 +#include "IBAFunctions.h"
26 +
27 +HRESULT WINAPI CreateBAFunctions(
28 + __in HMODULE hModule,
29 + __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
30 + __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
31 + );
src/test/burn/TestData/PrereqBaTests/ReplaceConfig/ReplaceConfig.cpp new
+33
@@ -0,0 +1,33 @@
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 +#include "precomp.h"
4 +
5 +int __cdecl wmain(
6 + __in int argc,
7 + __in LPWSTR argv[]
8 + )
9 +{
10 + HRESULT hr = S_OK;
11 + DWORD dwExitCode = 0;
12 + LPCWSTR wzDestinationFile = argc > 1 ? argv[1] : NULL;
13 + LPCWSTR wzGoodFile = argc > 2 ? argv[2] : NULL;
14 + LPCWSTR wzBadFile = argc > 3 ? argv[3] : NULL;
15 +
16 + if (argc != 4)
17 + {
18 + ExitWithRootFailure(hr, E_INVALIDARG, "Invalid args");
19 + }
20 +
21 + if (!::MoveFileW(wzDestinationFile, wzBadFile))
22 + {
23 + ExitWithLastError(hr, "Failed to move bad file");
24 + }
25 +
26 + if (!::MoveFileW(wzGoodFile, wzDestinationFile))
27 + {
28 + ExitWithLastError(hr, "Failed to move good file");
29 + }
30 +
31 +LExit:
32 + return FAILED(hr) ? (int)hr : (int)dwExitCode;
33 +}
src/test/burn/TestData/PrereqBaTests/ReplaceConfig/ReplaceConfig.vcxproj new
+63
@@ -0,0 +1,63 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<!-- 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. -->
3 +
4 +<Project DefaultTargets="Build" Toolsxmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 + <ItemGroup Label="ProjectConfigurations">
6 + <ProjectConfiguration Include="Debug|ARM64">
7 + <Configuration>Debug</Configuration>
8 + <Platform>ARM64</Platform>
9 + </ProjectConfiguration>
10 + <ProjectConfiguration Include="Release|ARM64">
11 + <Configuration>Release</Configuration>
12 + <Platform>ARM64</Platform>
13 + </ProjectConfiguration>
14 + <ProjectConfiguration Include="Debug|Win32">
15 + <Configuration>Debug</Configuration>
16 + <Platform>Win32</Platform>
17 + </ProjectConfiguration>
18 + <ProjectConfiguration Include="Release|Win32">
19 + <Configuration>Release</Configuration>
20 + <Platform>Win32</Platform>
21 + </ProjectConfiguration>
22 + <ProjectConfiguration Include="Debug|x64">
23 + <Configuration>Debug</Configuration>
24 + <Platform>x64</Platform>
25 + </ProjectConfiguration>
26 + <ProjectConfiguration Include="Release|x64">
27 + <Configuration>Release</Configuration>
28 + <Platform>x64</Platform>
29 + </ProjectConfiguration>
30 + </ItemGroup>
31 +
32 + <PropertyGroup Label="Globals">
33 + <ProjectGuid>{3D10A07D-3321-4F8E-B884-951F8FB5D636}</ProjectGuid>
34 + <ConfigurationType>Application</ConfigurationType>
35 + <ProjectSubSystem>Console</ProjectSubSystem>
36 + <CharacterSet>Unicode</CharacterSet>
37 + <TargetName>ReplaceConfig</TargetName>
38 + <IsWixTestProject>true</IsWixTestProject>
39 + </PropertyGroup>
40 +
41 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
42 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
43 +
44 + <PropertyGroup>
45 + <ProjectAdditionalLinkLibraries>comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib;wininet.lib</ProjectAdditionalLinkLibraries>
46 + </PropertyGroup>
47 +
48 + <ItemGroup>
49 + <ClCompile Include="precomp.cpp">
50 + <PrecompiledHeader>Create</PrecompiledHeader>
51 + </ClCompile>
52 + <ClCompile Include="ReplaceConfig.cpp" />
53 + </ItemGroup>
54 + <ItemGroup>
55 + <ClInclude Include="precomp.h" />
56 + </ItemGroup>
57 +
58 + <ItemGroup>
59 + <PackageReference Include="WixToolset.DUtil" />
60 + </ItemGroup>
61 +
62 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
63 +</Project>
src/test/burn/TestData/PrereqBaTests/ReplaceConfig/precomp.cpp new
+3
@@ -0,0 +1,3 @@
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 +#include "precomp.h"
src/test/burn/TestData/PrereqBaTests/ReplaceConfig/precomp.h new
+17
@@ -0,0 +1,17 @@
1 +#pragma once
2 +// 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.
3 +
4 +
5 +#include <windows.h>
6 +#include <msiquery.h>
7 +#include <objbase.h>
8 +#include <shlobj.h>
9 +#include <shlwapi.h>
10 +#include <stdlib.h>
11 +#include <strsafe.h>
12 +#include <CommCtrl.h>
13 +
14 +#include "dutil.h"
15 +#include "fileutil.h"
16 +#include "pathutil.h"
17 +#include "strutil.h"
src/test/burn/WixTestTools/BundleInstaller.cs
+14
@@ -26,6 +26,20 @@ namespace WixTestTools
26
27 public string TestName { get; }
28
29 + /// <summary>
30 + /// Runs the bundle asking for help.
31 + /// </summary>
32 + /// <param name="expectedExitCode">Expected exit code, defaults to success.</param>
33 + /// <param name="arguments">Optional arguments to pass to the tool.</param>
34 + /// <returns>Path to the generated log file.</returns>
35 + public string Help(int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments)
36 + {
37 + var newArgumentList = new List<string>();
38 + newArgumentList.Add("-help");
39 + newArgumentList.AddRange(arguments);
40 + return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Custom, newArgumentList.ToArray());
41 + }
42 +
43 /// <summary>
44 /// Installs the bundle with optional arguments.
45 /// </summary>
src/test/burn/WixToolsetTest.BurnE2E/PrereqBaTests.cs
+221 -4
@@ -14,6 +14,39 @@ namespace WixToolsetTest.BurnE2E
14
15 const int E_PREREQBA_INFINITE_LOOP = -2_114_714_646;
16
17 + /// <summary>
18 + /// This bundle purposely provides a .runtimeconfig.json file that requires a version of .NET Core that doesn't exist,
19 + /// with an MSI package to represent the prerequisite package.
20 + /// This verifies that:
21 + /// The preqba doesn't infinitely try to install prereqs.
22 + /// The engine automatically uninstalls the bundle since only permanent packages were installed.
23 + /// </summary>
24 + [RuntimeFact]
25 + public void DncAlwaysPreqBaDetectsInfiniteLoop()
26 + {
27 + var packageA = this.CreatePackageInstaller("PackageA");
28 + var packageC = this.CreatePackageInstaller("PackageC");
29 +
30 + var bundleC = this.CreateBundleInstaller("BundleC");
31 +
32 + var packageASourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs");
33 +
34 + // Source file should *not* be installed
35 + Assert.False(File.Exists(packageASourceCodeInstalled), $"Package A payload should not be there on test start: {packageASourceCodeInstalled}");
36 + packageC.VerifyInstalled(false);
37 +
38 + bundleC.Install(E_PREREQBA_INFINITE_LOOP, "CAUSEINFINITELOOP=1");
39 +
40 + // Part of the test is Install actually completing.
41 +
42 + // Source file should be installed
43 + Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
44 + packageC.VerifyInstalled(false);
45 +
46 + // No non-permanent packages should have ended up installed or cached so it should have unregistered.
47 + bundleC.VerifyUnregisteredAndRemovedFromPackageCache();
48 + }
49 +
50 /// <summary>
51 /// This bundle purposely provides a .runtimeconfig.json file that requires a version of .NET Core that doesn't exist,
52 /// with an MSI package to represent the prerequisite package.
@@ -25,7 +58,7 @@ namespace WixToolsetTest.BurnE2E
58 public void DncPreqBaDetectsInfiniteLoop()
59 {
60 var packageA = this.CreatePackageInstaller("PackageA");
28 - this.CreatePackageInstaller("PackageF");
61 + var packageC = this.CreatePackageInstaller("PackageC");
62
63 var bundleA = this.CreateBundleInstaller("BundleA");
64
@@ -33,18 +66,131 @@ namespace WixToolsetTest.BurnE2E
66
67 // Source file should *not* be installed
68 Assert.False(File.Exists(packageASourceCodeInstalled), $"Package A payload should not be there on test start: {packageASourceCodeInstalled}");
69 + packageC.VerifyInstalled(false);
70
37 - bundleA.Install(E_PREREQBA_INFINITE_LOOP);
71 + bundleA.Install(E_PREREQBA_INFINITE_LOOP, "CAUSEINFINITELOOP=1");
72
73 // Part of the test is Install actually completing.
74
75 // Source file should be installed
76 Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
77 + packageC.VerifyInstalled(false);
78
79 // No non-permanent packages should have ended up installed or cached so it should have unregistered.
80 bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
81 }
82
83 + /// <summary>
84 + /// This bundle purposely provides a .runtimeconfig.json file that requires a version of .NET Core that doesn't exist,
85 + /// with an EXE prereq package to swap it out with a good one.
86 + /// This verifies that:
87 + /// The preqba doesn't infinitely try to install prereqs.
88 + /// The managed BA gets loaded after installing prereqs.
89 + /// </summary>
90 + [RuntimeFact]
91 + public void DncAlwaysPreqBaLoadsManagedBaAfterInstallingPrereqs()
92 + {
93 + var packageA = this.CreatePackageInstaller("PackageA");
94 + var packageC = this.CreatePackageInstaller("PackageC");
95 +
96 + var bundleC = this.CreateBundleInstaller("BundleC");
97 +
98 + var packageASourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs");
99 +
100 + // Source file should *not* be installed
101 + Assert.False(File.Exists(packageASourceCodeInstalled), $"Package A payload should not be there on test start: {packageASourceCodeInstalled}");
102 + packageC.VerifyInstalled(false);
103 +
104 + bundleC.Install();
105 +
106 + // Source file should be installed
107 + Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
108 + packageC.VerifyInstalled(true);
109 +
110 + bundleC.VerifyRegisteredAndInPackageCache();
111 +
112 + bundleC.Uninstall();
113 +
114 + bundleC.VerifyUnregisteredAndRemovedFromPackageCache();
115 + }
116 +
117 + /// <summary>
118 + /// This bundle purposely provides a .runtimeconfig.json file that requires a version of .NET Core that doesn't exist,
119 + /// with an EXE prereq package to swap it out with a good one.
120 + /// This verifies that:
121 + /// The preqba doesn't infinitely reload itself after failing to load the managed BA.
122 + /// The managed BA gets loaded after installing prereqs.
123 + /// </summary>
124 + [RuntimeFact]
125 + public void DncPreqBaLoadsManagedBaAfterInstallingPrereqs()
126 + {
127 + var packageA = this.CreatePackageInstaller("PackageA");
128 + var packageC = this.CreatePackageInstaller("PackageC");
129 +
130 + var bundleA = this.CreateBundleInstaller("BundleA");
131 +
132 + var packageASourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs");
133 +
134 + // Source file should *not* be installed
135 + Assert.False(File.Exists(packageASourceCodeInstalled), $"Package A payload should not be there on test start: {packageASourceCodeInstalled}");
136 + packageC.VerifyInstalled(false);
137 +
138 + bundleA.Install();
139 +
140 + // Source file should be installed
141 + Assert.True(File.Exists(packageASourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageASourceCodeInstalled));
142 + packageC.VerifyInstalled(true);
143 +
144 + bundleA.VerifyRegisteredAndInPackageCache();
145 +
146 + bundleA.Uninstall();
147 +
148 + bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
149 + }
150 +
151 + [RuntimeFact]
152 + public void DncAlwaysPreqBaForwardsHelpToManagedBa()
153 + {
154 + var bundleE = this.CreateBundleInstaller("BundleE");
155 +
156 + var bundleLog = bundleE.Help();
157 +
158 + Assert.True(LogVerifier.MessageInLogFile(bundleLog, "This is a BA for automated testing"));
159 + }
160 +
161 + /// <summary>
162 + /// This bundle purposely provides a WixToolset.Mba.Host.config file that requires a version of .NET Framework that doesn't exist,
163 + /// with an MSI package to represent the prerequisite package.
164 + /// This verifies that:
165 + /// The preqba doesn't infinitely try to install prereqs.
166 + /// The engine automatically uninstalls the bundle since only permanent packages were installed.
167 + /// </summary>
168 + [RuntimeFact]
169 + public void MbaAlwaysPreqBaDetectsInfiniteLoop()
170 + {
171 + var packageB = this.CreatePackageInstaller("PackageB");
172 + var packageC = this.CreatePackageInstaller("PackageC");
173 +
174 + var bundleD = this.CreateBundleInstaller("BundleD");
175 +
176 + var packageBSourceCodeInstalled = packageB.GetInstalledFilePath("Package.wxs");
177 +
178 + // Source file should *not* be installed
179 + Assert.False(File.Exists(packageBSourceCodeInstalled), $"Package B payload should not be there on test start: {packageBSourceCodeInstalled}");
180 + packageC.VerifyInstalled(false);
181 +
182 + bundleD.Install(E_PREREQBA_INFINITE_LOOP, "CAUSEINFINITELOOP=1");
183 +
184 + // Part of the test is Install actually completing.
185 +
186 + // Source file should be installed
187 + Assert.True(File.Exists(packageBSourceCodeInstalled), String.Concat("Should have found Package B payload installed at: ", packageBSourceCodeInstalled));
188 + packageC.VerifyInstalled(false);
189 +
190 + // No non-permanent packages should have ended up installed or cached so it should have unregistered.
191 + bundleD.VerifyUnregisteredAndRemovedFromPackageCache();
192 + }
193 +
194 /// <summary>
195 /// This bundle purposely provides a WixToolset.Mba.Host.config file that requires a version of .NET Framework that doesn't exist,
196 /// with an MSI package to represent the prerequisite package.
@@ -56,7 +202,7 @@ namespace WixToolsetTest.BurnE2E
202 public void MbaPreqBaDetectsInfiniteLoop()
203 {
204 var packageB = this.CreatePackageInstaller("PackageB");
59 - this.CreatePackageInstaller("PackageF");
205 + var packageC = this.CreatePackageInstaller("PackageC");
206
207 var bundleB = this.CreateBundleInstaller("BundleB");
208
@@ -64,13 +210,84 @@ namespace WixToolsetTest.BurnE2E
210
211 // Source file should *not* be installed
212 Assert.False(File.Exists(packageBSourceCodeInstalled), $"Package B payload should not be there on test start: {packageBSourceCodeInstalled}");
213 + packageC.VerifyInstalled(false);
214
68 - bundleB.Install(E_PREREQBA_INFINITE_LOOP);
215 + bundleB.Install(E_PREREQBA_INFINITE_LOOP, "CAUSEINFINITELOOP=1");
216
217 // Part of the test is Install actually completing.
218
219 // Source file should be installed
220 Assert.True(File.Exists(packageBSourceCodeInstalled), String.Concat("Should have found Package B payload installed at: ", packageBSourceCodeInstalled));
221 + packageC.VerifyInstalled(false);
222 +
223 + // No non-permanent packages should have ended up installed or cached so it should have unregistered.
224 + bundleB.VerifyUnregisteredAndRemovedFromPackageCache();
225 + }
226 +
227 + /// <summary>
228 + /// This bundle purposely provides a WixToolset.Mba.Host.config file that requires a version of .NET Framework that doesn't exist,
229 + /// with an EXE prereq package to swap it out with a good one.
230 + /// This verifies that:
231 + /// The preqba doesn't infinitely try to install prereqs.
232 + /// The managed BA gets loaded after installing prereqs.
233 + /// </summary>
234 + [RuntimeFact]
235 + public void MbaAlwaysPreqBaLoadsManagedBaAfterInstallingPrereqs()
236 + {
237 + var packageB = this.CreatePackageInstaller("PackageB");
238 + var packageC = this.CreatePackageInstaller("PackageC");
239 +
240 + var bundleD = this.CreateBundleInstaller("BundleD");
241 +
242 + var packageBSourceCodeInstalled = packageB.GetInstalledFilePath("Package.wxs");
243 +
244 + // Source file should *not* be installed
245 + Assert.False(File.Exists(packageBSourceCodeInstalled), $"Package B payload should not be there on test start: {packageBSourceCodeInstalled}");
246 + packageC.VerifyInstalled(false);
247 +
248 + bundleD.Install();
249 +
250 + // Source file should be installed
251 + Assert.True(File.Exists(packageBSourceCodeInstalled), String.Concat("Should have found Package B payload installed at: ", packageBSourceCodeInstalled));
252 + packageC.VerifyInstalled(true);
253 +
254 + bundleD.VerifyRegisteredAndInPackageCache();
255 +
256 + bundleD.Uninstall();
257 +
258 + bundleD.VerifyUnregisteredAndRemovedFromPackageCache();
259 + }
260 +
261 + /// <summary>
262 + /// This bundle purposely provides a WixToolset.Mba.Host.config file that requires a version of .NET Framework that doesn't exist,
263 + /// with an EXE prereq package to swap it out with a good one.
264 + /// This verifies that:
265 + /// The preqba doesn't infinitely reload itself after failing to load the managed BA.
266 + /// The managed BA gets loaded after installing prereqs.
267 + /// </summary>
268 + [RuntimeFact]
269 + public void MbaPreqBaLoadsManagedBaAfterInstallingPrereqs()
270 + {
271 + var packageB = this.CreatePackageInstaller("PackageB");
272 + var packageC = this.CreatePackageInstaller("PackageC");
273 +
274 + var bundleB = this.CreateBundleInstaller("BundleB");
275 +
276 + var packageBSourceCodeInstalled = packageB.GetInstalledFilePath("Package.wxs");
277 +
278 + // Source file should *not* be installed
279 + Assert.False(File.Exists(packageBSourceCodeInstalled), $"Package B payload should not be there on test start: {packageBSourceCodeInstalled}");
280 + packageC.VerifyInstalled(false);
281 +
282 + bundleB.Install();
283 +
284 + // Source file should be installed
285 + Assert.True(File.Exists(packageBSourceCodeInstalled), String.Concat("Should have found Package B payload installed at: ", packageBSourceCodeInstalled));
286 + packageC.VerifyInstalled(true);
287 +
288 + bundleB.VerifyRegisteredAndInPackageCache();
289 +
290 + bundleB.Uninstall();
291
292 // No non-permanent packages should have ended up installed or cached so it should have unregistered.
293 bundleB.VerifyUnregisteredAndRemovedFromPackageCache();