From 72e20f682c0d64102e86439ba5527dd0d71932ae Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 22 Apr 2022 16:55:29 -0500 Subject: Add RepairCondition. Fixes 6758 --- .../inc/BootstrapperApplication.h | 1 + .../WixToolset.Mba.Core/BootstrapperApplication.cs | 4 +- src/api/burn/WixToolset.Mba.Core/EventArgs.cs | 8 +- .../IBootstrapperApplication.cs | 1 + src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs | 7 +- src/api/burn/WixToolset.Mba.Core/PackageInfo.cs | 5 ++ src/api/burn/balutil/balinfo.cpp | 7 ++ src/api/burn/balutil/inc/BalBaseBAFunctions.h | 1 + .../balutil/inc/BalBaseBootstrapperApplication.h | 1 + .../inc/BalBaseBootstrapperApplicationProc.h | 2 +- .../burn/balutil/inc/IBootstrapperApplication.h | 1 + src/api/burn/balutil/inc/balinfo.h | 1 + .../Symbols/WixBundlePackageSymbol.cs | 8 ++ src/burn/engine/package.cpp | 5 ++ src/burn/engine/package.h | 1 + src/burn/engine/plan.cpp | 24 ++++-- src/burn/engine/plan.h | 1 + src/burn/engine/userexperience.cpp | 2 + src/burn/engine/userexperience.h | 1 + src/burn/test/BurnUnitTest/PlanTest.cpp | 95 ++++++++++++++++++++++ .../PlanTest/BundlePackage_Multiple_manifest.xml | 2 +- src/burn/test/BurnUnitTest/VariableTest.cpp | 1 + .../WixStandardBootstrapperApplication.cpp | 3 +- ...CreateBootstrapperApplicationManifestCommand.cs | 5 ++ .../Bundles/CreateBurnManifestCommand.cs | 5 ++ .../PerformBundleBackendValidationCommand.cs | 5 ++ src/wix/WixToolset.Core/Compiler_Bundle.cs | 11 +++ .../BundleManifestFixture.cs | 2 +- .../BundlePackageFixture.cs | 4 +- .../ExePackageFixture.cs | 24 ++++++ .../TestData/BundlePackage/V3BundlePackage.wxs | 2 +- .../CustomPackageDescription.wxs | 2 +- .../RepairConditionWithoutRepairArguments.wxs | 12 +++ 33 files changed, 237 insertions(+), 17 deletions(-) create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExePackage/RepairConditionWithoutRepairArguments.wxs (limited to 'src') diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index fbbd10ee..f0b5dad4 100644 --- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -1243,6 +1243,7 @@ struct BA_ONPLANPACKAGEBEGIN_ARGS BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition; BOOTSTRAPPER_REQUEST_STATE recommendedState; BOOTSTRAPPER_CACHE_TYPE recommendedCacheType; + BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition; }; struct BA_ONPLANPACKAGEBEGIN_RESULTS diff --git a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs index 41738bf6..6738a4a6 100644 --- a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs +++ b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs @@ -1598,9 +1598,9 @@ namespace WixToolset.Mba.Core return args.HResult; } - int IBootstrapperApplication.OnPlanPackageBegin(string wzPackageId, PackageState state, bool fCached, BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, RequestState recommendedState, BOOTSTRAPPER_CACHE_TYPE recommendedCacheType, ref RequestState pRequestedState, ref BOOTSTRAPPER_CACHE_TYPE pRequestedCacheType, ref bool fCancel) + int IBootstrapperApplication.OnPlanPackageBegin(string wzPackageId, PackageState state, bool fCached, BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, RequestState recommendedState, BOOTSTRAPPER_CACHE_TYPE recommendedCacheType, ref RequestState pRequestedState, ref BOOTSTRAPPER_CACHE_TYPE pRequestedCacheType, ref bool fCancel) { - PlanPackageBeginEventArgs args = new PlanPackageBeginEventArgs(wzPackageId, state, fCached, installCondition, recommendedState, recommendedCacheType, pRequestedState, pRequestedCacheType, fCancel); + PlanPackageBeginEventArgs args = new PlanPackageBeginEventArgs(wzPackageId, state, fCached, installCondition, repairCondition, recommendedState, recommendedCacheType, pRequestedState, pRequestedCacheType, fCancel); this.OnPlanPackageBegin(args); pRequestedState = args.State; diff --git a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs index 07b14b3d..d79ac402 100644 --- a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs +++ b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs @@ -783,13 +783,14 @@ namespace WixToolset.Mba.Core public class PlanPackageBeginEventArgs : CancellableHResultEventArgs { /// - public PlanPackageBeginEventArgs(string packageId, PackageState currentState, bool cached, BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, RequestState recommendedState, BOOTSTRAPPER_CACHE_TYPE recommendedCacheType, RequestState state, BOOTSTRAPPER_CACHE_TYPE cacheType, bool cancelRecommendation) + public PlanPackageBeginEventArgs(string packageId, PackageState currentState, bool cached, BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, RequestState recommendedState, BOOTSTRAPPER_CACHE_TYPE recommendedCacheType, RequestState state, BOOTSTRAPPER_CACHE_TYPE cacheType, bool cancelRecommendation) : base(cancelRecommendation) { this.PackageId = packageId; this.CurrentState = currentState; this.Cached = cached; this.InstallCondition = installCondition; + this.RepairCondition = repairCondition; this.RecommendedState = recommendedState; this.RecommendedCacheType = recommendedCacheType; this.State = state; @@ -816,6 +817,11 @@ namespace WixToolset.Mba.Core /// public BOOTSTRAPPER_PACKAGE_CONDITION_RESULT InstallCondition { get; private set; } + /// + /// Gets the evaluated result of the package's repair condition. + /// + public BOOTSTRAPPER_PACKAGE_CONDITION_RESULT RepairCondition { get; private set; } + /// /// Gets the recommended requested state for the package. /// diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs index ae642474..2877d4de 100644 --- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs +++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs @@ -314,6 +314,7 @@ namespace WixToolset.Mba.Core [MarshalAs(UnmanagedType.U4)] PackageState state, [MarshalAs(UnmanagedType.Bool)] bool fCached, [MarshalAs(UnmanagedType.U4)] BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + [MarshalAs(UnmanagedType.U4)] BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, [MarshalAs(UnmanagedType.U4)] RequestState recommendedState, [MarshalAs(UnmanagedType.U4)] BOOTSTRAPPER_CACHE_TYPE recommendedCacheType, [MarshalAs(UnmanagedType.U4)] ref RequestState pRequestedState, diff --git a/src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs b/src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs index a1d99b10..ee3be820 100644 --- a/src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs +++ b/src/api/burn/WixToolset.Mba.Core/IPackageInfo.cs @@ -42,6 +42,11 @@ namespace WixToolset.Mba.Core /// string InstallCondition { get; } + /// + /// + /// + string RepairCondition { get; } + /// /// /// @@ -87,4 +92,4 @@ namespace WixToolset.Mba.Core /// bool Vital { get; } } -} \ No newline at end of file +} diff --git a/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs b/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs index 39fe4d73..e400cbe4 100644 --- a/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs +++ b/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs @@ -101,6 +101,9 @@ namespace WixToolset.Mba.Core /// public string InstallCondition { get; internal set; } + /// + public string RepairCondition { get; internal set; } + /// public BOOTSTRAPPER_CACHE_TYPE CacheType { get; internal set; } @@ -174,6 +177,8 @@ namespace WixToolset.Mba.Core package.InstallCondition = BootstrapperApplicationData.GetAttribute(node, "InstallCondition"); + package.RepairCondition = BootstrapperApplicationData.GetAttribute(node, "RepairCondition"); + packagesById.Add(package.Id, package); } diff --git a/src/api/burn/balutil/balinfo.cpp b/src/api/burn/balutil/balinfo.cpp index f0eb9904..29e453f6 100644 --- a/src/api/burn/balutil/balinfo.cpp +++ b/src/api/burn/balutil/balinfo.cpp @@ -304,6 +304,7 @@ DAPI_(void) BalInfoUninitialize( ReleaseStr(pBundle->packages.rgPackages[i].sczUpgradeCode); ReleaseStr(pBundle->packages.rgPackages[i].sczVersion); ReleaseStr(pBundle->packages.rgPackages[i].sczInstallCondition); + ReleaseStr(pBundle->packages.rgPackages[i].sczRepairCondition); ReleaseStr(pBundle->packages.rgPackages[i].sczPrereqLicenseFile); ReleaseStr(pBundle->packages.rgPackages[i].sczPrereqLicenseUrl); } @@ -484,6 +485,12 @@ static HRESULT ParsePackagesFromXml( ExitOnFailure(hr, "Failed to get install condition for package."); } + hr = XmlGetAttributeEx(pNode, L"RepairCondition", &prgPackages[iPackage].sczRepairCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get repair condition for package."); + } + hr = XmlGetAttributeEx(pNode, L"Cache", &scz); ExitOnFailure(hr, "Failed to get cache type for package."); diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h index 6bde05d2..9ff58d2b 100644 --- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h +++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h @@ -276,6 +276,7 @@ public: // IBootstrapperApplication __in BOOTSTRAPPER_PACKAGE_STATE /*state*/, __in BOOL /*fCached*/, __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT /*installCondition*/, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT /*repairCondition*/, __in BOOTSTRAPPER_REQUEST_STATE /*recommendedState*/, __in BOOTSTRAPPER_CACHE_TYPE /*recommendedCacheType*/, __inout BOOTSTRAPPER_REQUEST_STATE* /*pRequestState*/, diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h index b661c7c9..2e848df7 100644 --- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h +++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h @@ -290,6 +290,7 @@ public: // IBootstrapperApplication __in BOOTSTRAPPER_PACKAGE_STATE /*state*/, __in BOOL /*fCached*/, __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT /*installCondition*/, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT /*repairCondition*/, __in BOOTSTRAPPER_REQUEST_STATE /*recommendedState*/, __in BOOTSTRAPPER_CACHE_TYPE /*recommendedCacheType*/, __inout BOOTSTRAPPER_REQUEST_STATE* /*pRequestState*/, diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h index 4ef7bac5..e35678ad 100644 --- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h +++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h @@ -195,7 +195,7 @@ static HRESULT BalBaseBAProcOnPlanPackageBegin( __inout BA_ONPLANPACKAGEBEGIN_RESULTS* pResults ) { - return pBA->OnPlanPackageBegin(pArgs->wzPackageId, pArgs->state, pArgs->fCached, pArgs->installCondition, pArgs->recommendedState, pArgs->recommendedCacheType, &pResults->requestedState, &pResults->requestedCacheType, &pResults->fCancel); + return pBA->OnPlanPackageBegin(pArgs->wzPackageId, pArgs->state, pArgs->fCached, pArgs->installCondition, pArgs->repairCondition, pArgs->recommendedState, pArgs->recommendedCacheType, &pResults->requestedState, &pResults->requestedCacheType, &pResults->fCancel); } static HRESULT BalBaseBAProcOnPlanCompatibleMsiPackageBegin( diff --git a/src/api/burn/balutil/inc/IBootstrapperApplication.h b/src/api/burn/balutil/inc/IBootstrapperApplication.h index a4840228..6eca90fa 100644 --- a/src/api/burn/balutil/inc/IBootstrapperApplication.h +++ b/src/api/burn/balutil/inc/IBootstrapperApplication.h @@ -182,6 +182,7 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A __in BOOTSTRAPPER_PACKAGE_STATE state, __in BOOL fCached, __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, __in BOOTSTRAPPER_REQUEST_STATE recommendedState, __in BOOTSTRAPPER_CACHE_TYPE recommendedCacheType, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, diff --git a/src/api/burn/balutil/inc/balinfo.h b/src/api/burn/balutil/inc/balinfo.h index 0c7a5b93..cd61f34d 100644 --- a/src/api/burn/balutil/inc/balinfo.h +++ b/src/api/burn/balutil/inc/balinfo.h @@ -49,6 +49,7 @@ typedef struct _BAL_INFO_PACKAGE LPWSTR sczUpgradeCode; LPWSTR sczVersion; LPWSTR sczInstallCondition; + LPWSTR sczRepairCondition; BOOTSTRAPPER_CACHE_TYPE cacheType; BOOL fPrereqPackage; LPWSTR sczPrereqLicenseFile; diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs index 6afe657e..8d625664 100644 --- a/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs +++ b/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageSymbol.cs @@ -14,6 +14,7 @@ namespace WixToolset.Data new IntermediateFieldDefinition(nameof(WixBundlePackageSymbolFields.PayloadRef), IntermediateFieldType.String), new IntermediateFieldDefinition(nameof(WixBundlePackageSymbolFields.Attributes), IntermediateFieldType.Number), new IntermediateFieldDefinition(nameof(WixBundlePackageSymbolFields.InstallCondition), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixBundlePackageSymbolFields.RepairCondition), IntermediateFieldType.String), new IntermediateFieldDefinition(nameof(WixBundlePackageSymbolFields.Cache), IntermediateFieldType.String), new IntermediateFieldDefinition(nameof(WixBundlePackageSymbolFields.CacheId), IntermediateFieldType.String), new IntermediateFieldDefinition(nameof(WixBundlePackageSymbolFields.PerMachine), IntermediateFieldType.Bool), @@ -42,6 +43,7 @@ namespace WixToolset.Data.Symbols PayloadRef, Attributes, InstallCondition, + RepairCondition, Cache, CacheId, PerMachine, @@ -124,6 +126,12 @@ namespace WixToolset.Data.Symbols set => this.Set((int)WixBundlePackageSymbolFields.InstallCondition, value); } + public string RepairCondition + { + get => (string)this.Fields[(int)WixBundlePackageSymbolFields.RepairCondition]; + set => this.Set((int)WixBundlePackageSymbolFields.RepairCondition, value); + } + public BundleCacheType? Cache { get => Enum.TryParse((string)this.Fields[(int)WixBundlePackageSymbolFields.Cache], true, out BundleCacheType value) ? value : (BundleCacheType?)null; diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp index 89203ada..88e7cb9d 100644 --- a/src/burn/engine/package.cpp +++ b/src/burn/engine/package.cpp @@ -178,6 +178,10 @@ extern "C" HRESULT PackagesParseFromXml( hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallCondition."); + // @RepairCondition + hr = XmlGetAttributeEx(pixnNode, L"RepairCondition", &pPackage->sczRepairCondition); + ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairCondition."); + // @RollbackBoundaryForward hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RollbackBoundaryForward."); @@ -362,6 +366,7 @@ extern "C" void PackageUninitialize( ReleaseStr(pPackage->sczLogPathVariable); ReleaseStr(pPackage->sczRollbackLogPathVariable); ReleaseStr(pPackage->sczInstallCondition); + ReleaseStr(pPackage->sczRepairCondition); ReleaseStr(pPackage->sczCacheId); if (pPackage->rgDependencyProviders) diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h index eb8e7543..3ec77baf 100644 --- a/src/burn/engine/package.h +++ b/src/burn/engine/package.h @@ -250,6 +250,7 @@ typedef struct _BURN_PACKAGE LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path. LPWSTR sczInstallCondition; + LPWSTR sczRepairCondition; BOOL fPerMachine; BOOL fPermanent; BOOL fVital; diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index 2c267415..47da22c0 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp @@ -347,6 +347,7 @@ extern "C" HRESULT PlanDefaultPackageRequestState( __in BOOTSTRAPPER_PACKAGE_STATE currentState, __in BOOTSTRAPPER_ACTION action, __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, __in BOOTSTRAPPER_RELATION_TYPE relationType, __out BOOTSTRAPPER_REQUEST_STATE* pRequestState ) @@ -408,6 +409,10 @@ extern "C" HRESULT PlanDefaultPackageRequestState( { defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT <= defaultRequestState ? BOOTSTRAPPER_REQUEST_STATE_NONE : defaultRequestState; } + else if (BOOTSTRAPPER_ACTION_REPAIR == action && BOOTSTRAPPER_PACKAGE_CONDITION_FALSE == repairCondition) + { + defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } } *pRequestState = defaultRequestState; @@ -905,7 +910,8 @@ static HRESULT InitializePackage( { HRESULT hr = S_OK; BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition = BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT; - BOOL fInstallCondition = FALSE; + BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition = BOOTSTRAPPER_PACKAGE_CONDITION_DEFAULT; + BOOL fEvaluatedCondition = FALSE; BOOL fBeginCalled = FALSE; BOOTSTRAPPER_RELATION_TYPE relationType = pPlan->pCommand->relationType; @@ -927,20 +933,28 @@ static HRESULT InitializePackage( if (pPackage->sczInstallCondition && *pPackage->sczInstallCondition) { - hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fInstallCondition); + hr = ConditionEvaluate(pVariables, pPackage->sczInstallCondition, &fEvaluatedCondition); ExitOnFailure(hr, "Failed to evaluate install condition."); - installCondition = fInstallCondition ? BOOTSTRAPPER_PACKAGE_CONDITION_TRUE : BOOTSTRAPPER_PACKAGE_CONDITION_FALSE; + installCondition = fEvaluatedCondition ? BOOTSTRAPPER_PACKAGE_CONDITION_TRUE : BOOTSTRAPPER_PACKAGE_CONDITION_FALSE; + } + + if (pPackage->sczRepairCondition && *pPackage->sczRepairCondition) + { + hr = ConditionEvaluate(pVariables, pPackage->sczRepairCondition, &fEvaluatedCondition); + ExitOnFailure(hr, "Failed to evaluate repair condition."); + + repairCondition = fEvaluatedCondition ? BOOTSTRAPPER_PACKAGE_CONDITION_TRUE : BOOTSTRAPPER_PACKAGE_CONDITION_FALSE; } // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. - hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, pPlan->action, installCondition, relationType, &pPackage->defaultRequested); + hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, pPlan->action, installCondition, repairCondition, relationType, &pPackage->defaultRequested); ExitOnFailure(hr, "Failed to set default package state."); pPackage->requested = pPackage->defaultRequested; fBeginCalled = TRUE; - hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, pPackage->fCached, installCondition, &pPackage->requested, &pPackage->cacheType); + hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, pPackage->currentState, pPackage->fCached, installCondition, repairCondition, &pPackage->requested, &pPackage->cacheType); ExitOnRootFailure(hr, "BA aborted plan package begin."); if (BURN_PACKAGE_TYPE_MSI == pPackage->type) diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h index 87214cc8..fbb7a5a1 100644 --- a/src/burn/engine/plan.h +++ b/src/burn/engine/plan.h @@ -342,6 +342,7 @@ HRESULT PlanDefaultPackageRequestState( __in BOOTSTRAPPER_PACKAGE_STATE currentState, __in BOOTSTRAPPER_ACTION action, __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, __in BOOTSTRAPPER_RELATION_TYPE relationType, __out BOOTSTRAPPER_REQUEST_STATE* pRequestState ); diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp index a1a010d2..a3cbcd4a 100644 --- a/src/burn/engine/userexperience.cpp +++ b/src/burn/engine/userexperience.cpp @@ -2169,6 +2169,7 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( __in BOOTSTRAPPER_PACKAGE_STATE state, __in BOOL fCached, __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType ) @@ -2182,6 +2183,7 @@ EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( args.state = state; args.fCached = fCached; args.installCondition = installCondition; + args.repairCondition = repairCondition; args.recommendedState = *pRequestedState; args.recommendedCacheType = *pRequestedCacheType; diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h index 90a047ed..2059b521 100644 --- a/src/burn/engine/userexperience.h +++ b/src/burn/engine/userexperience.h @@ -501,6 +501,7 @@ BAAPI UserExperienceOnPlanPackageBegin( __in BOOTSTRAPPER_PACKAGE_STATE state, __in BOOL fCached, __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState, __inout BOOTSTRAPPER_CACHE_TYPE* pRequestedCacheType ); diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp index 87bbbffd..f69606a0 100644 --- a/src/burn/test/BurnUnitTest/PlanTest.cpp +++ b/src/burn/test/BurnUnitTest/PlanTest.cpp @@ -458,6 +458,101 @@ namespace Bootstrapper ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); } + [Fact] + void MultipleBundlePackageRepairTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzMultipleBundlePackageManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_REPAIR); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_REPAIR, pPlan->action); + NativeAssert::StringEqual(L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", pPlan->wzBundleId); + NativeAssert::StringEqual(L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", pPlan->wzBundleProviderKey); + Assert::Equal(FALSE, pPlan->fEnabledForwardCompatibleBundle); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(TRUE, pPlan->fCanAffectMachineState); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); + Assert::Equal(BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_PROVIDER_KEY, pPlan->dwRegistrationOperations); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRegistrationActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions); + + fRollback = FALSE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(18575450ull, pPlan->qwEstimatedSize); + Assert::Equal(52254105ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_REPAIR, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(1ul, pPlan->cExecutePackagesTotal); + Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"NetFx48Web"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + [Fact] void OrphanCompatiblePackageTest() { diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml index 0b521faa..b02c2056 100644 --- a/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml @@ -1 +1 @@ - + diff --git a/src/burn/test/BurnUnitTest/VariableTest.cpp b/src/burn/test/BurnUnitTest/VariableTest.cpp index d0361c0e..1d5fc6f3 100644 --- a/src/burn/test/BurnUnitTest/VariableTest.cpp +++ b/src/burn/test/BurnUnitTest/VariableTest.cpp @@ -388,6 +388,7 @@ namespace Bootstrapper Assert::True(EvaluateFailureConditionHelper(&variables, L"1A")); Assert::True(EvaluateFailureConditionHelper(&variables, L"*")); + Assert::False(EvaluateFailureConditionHelper(&variables, L"0")); Assert::True(EvaluateFailureConditionHelper(&variables, L"1 == 1")); } finally diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp index 969aca9d..e93dbe90 100644 --- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp +++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp @@ -425,6 +425,7 @@ public: // IBootstrapperApplication __in BOOTSTRAPPER_PACKAGE_STATE state, __in BOOL fCached, __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT installCondition, + __in BOOTSTRAPPER_PACKAGE_CONDITION_RESULT repairCondition, __in BOOTSTRAPPER_REQUEST_STATE recommendedState, __in BOOTSTRAPPER_CACHE_TYPE recommendedCacheType, __inout BOOTSTRAPPER_REQUEST_STATE* pRequestState, @@ -485,7 +486,7 @@ public: // IBootstrapperApplication } } - return CBalBaseBootstrapperApplication::OnPlanPackageBegin(wzPackageId, state, fCached, installCondition, recommendedState, recommendedCacheType, pRequestState, pRequestedCacheType, pfCancel); + return CBalBaseBootstrapperApplication::OnPlanPackageBegin(wzPackageId, state, fCached, installCondition, repairCondition, recommendedState, recommendedCacheType, pRequestState, pRequestedCacheType, pfCancel); } diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs index 39347d19..3167ccd3 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs @@ -206,6 +206,11 @@ namespace WixToolset.Core.Burn.Bundles writer.WriteAttributeString("InstallCondition", package.PackageSymbol.InstallCondition); } + if (!String.IsNullOrEmpty(package.PackageSymbol.RepairCondition)) + { + writer.WriteAttributeString("RepairCondition", package.PackageSymbol.RepairCondition); + } + switch (package.PackageSymbol.Cache) { case BundleCacheType.Remove: diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs index 68f5002f..78e02534 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs @@ -389,6 +389,11 @@ namespace WixToolset.Core.Burn.Bundles writer.WriteAttributeString("InstallCondition", package.PackageSymbol.InstallCondition); } + if (!String.IsNullOrEmpty(package.PackageSymbol.RepairCondition)) + { + writer.WriteAttributeString("RepairCondition", package.PackageSymbol.RepairCondition); + } + if (package.SpecificPackageSymbol is WixBundleBundlePackageSymbol bundlePackage) // BUNDLE { writer.WriteAttributeString("BundleId", bundlePackage.BundleId); diff --git a/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs index 3ffb8df4..5d9acb4a 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs @@ -91,6 +91,11 @@ namespace WixToolset.Core.Burn.Bundles { this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, elementName, "InstallCondition", symbol.InstallCondition, BundleConditionPhase.Plan); } + + if (!String.IsNullOrEmpty(symbol.RepairCondition)) + { + this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, elementName, "RepairCondition", symbol.RepairCondition, BundleConditionPhase.Plan); + } } private void ValidateBundlePackage(WixBundleBundlePackageSymbol symbol, WixBundlePackageSymbol packageSymbol) diff --git a/src/wix/WixToolset.Core/Compiler_Bundle.cs b/src/wix/WixToolset.Core/Compiler_Bundle.cs index 4cbfe58a..bd1d70f3 100644 --- a/src/wix/WixToolset.Core/Compiler_Bundle.cs +++ b/src/wix/WixToolset.Core/Compiler_Bundle.cs @@ -2003,6 +2003,7 @@ namespace WixToolset.Core }; string after = null; string installCondition = null; + string repairCondition = null; var cache = BundleCacheType.Keep; // the default is to cache everything in tradeoff for stability over disk space. string cacheId = null; string description = null; @@ -2061,6 +2062,10 @@ namespace WixToolset.Core case "InstallCondition": installCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; + case "RepairCondition": + repairCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + allowed = (packageType != WixBundlePackageType.Msu); + break; case "Cache": var value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (value) @@ -2262,6 +2267,11 @@ namespace WixToolset.Core } } + if (repairArguments == null && repairCondition != null) + { + this.Core.Write(ErrorMessages.ExpectedAttributeWithValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "RepairArguments", "RepairCondition")); + } + // Validate the protocol if provided. if (!String.IsNullOrEmpty(protocol)) { @@ -2429,6 +2439,7 @@ namespace WixToolset.Core Type = packageType, Attributes = attributes, InstallCondition = installCondition, + RepairCondition = repairCondition, Cache = cache, CacheId = cacheId, Description = description, diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs index 08ffea33..368cd961 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs @@ -88,7 +88,7 @@ namespace WixToolsetTest.CoreIntegration { "WixPackageProperties", new List { "DownloadSize", "PackageSize", "InstalledSize", "Version" } }, }; Assert.Equal(3, packageElements.Count); - Assert.Equal("", packageElements[0].GetTestXml(ignoreAttributesByElementName)); + Assert.Equal("", packageElements[0].GetTestXml(ignoreAttributesByElementName)); Assert.Equal("", packageElements[1].GetTestXml()); Assert.Equal("", packageElements[2].GetTestXml(ignoreAttributesByElementName)); } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs index 621d0871..63659936 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs @@ -354,7 +354,7 @@ namespace WixToolsetTest.CoreIntegration .ToArray(); WixAssert.CompareLineByLine(new string[] { - $"" + + $"" + "" + "" + "" + @@ -382,7 +382,7 @@ namespace WixToolsetTest.CoreIntegration .ToArray(); WixAssert.CompareLineByLine(new string[] { - "", + "", }, packageElements); } } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/ExePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/ExePackageFixture.cs index 15885b94..67f295e8 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/ExePackageFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/ExePackageFixture.cs @@ -35,6 +35,30 @@ namespace WixToolsetTest.CoreIntegration } } + [Fact] + public void ErrorWhenRepairConditionWithoutRepairArguments() + { + var folder = TestData.Get(@"TestData", "ExePackage"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "RepairConditionWithoutRepairArguments.wxs"), + "-o", Path.Combine(baseFolder, "test.wixlib") + }); + + WixAssert.CompareLineByLine(new[] + { + "The ExePackage/@RepairArguments attribute is required to have a value when attribute RepairCondition is present.", + }, result.Messages.Select(m => m.ToString()).ToArray()); + Assert.Equal(401, result.ExitCode); + } + } + [Fact] public void ErrorWhenRequireDetectCondition() { diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/V3BundlePackage.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/V3BundlePackage.wxs index 192bb6d7..36a36618 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/V3BundlePackage.wxs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/V3BundlePackage.wxs @@ -4,7 +4,7 @@ - + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomPackageDescription/CustomPackageDescription.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomPackageDescription/CustomPackageDescription.wxs index 10c4f91f..47284d1f 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomPackageDescription/CustomPackageDescription.wxs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomPackageDescription/CustomPackageDescription.wxs @@ -2,7 +2,7 @@ - + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExePackage/RepairConditionWithoutRepairArguments.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExePackage/RepairConditionWithoutRepairArguments.wxs new file mode 100644 index 00000000..764e7a5a --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExePackage/RepairConditionWithoutRepairArguments.wxs @@ -0,0 +1,12 @@ + + + + + + + + -- cgit v1.2.3-55-g6feb