Skip to content

Commit

Permalink
ExePackage/@DetectVersionVariable: Support providing a version number…
Browse files Browse the repository at this point in the history
… in a variable to test against the package's version.
  • Loading branch information
nirbar committed Dec 5, 2023
1 parent d8bd544 commit e7b14a8
Show file tree
Hide file tree
Showing 13 changed files with 396 additions and 9 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ This repository contains the PanelSwWix4: A custom WiX Toolset codebase
- [6298](https://github.com/wixtoolset/issues/issues/6298): Extract detached containers with "wix burn extract"
- [6252](https://github.com/wixtoolset/issues/issues/6252): Automatically add -norestart flag for any burn ExePackage, BundlePackage, and related bundles
- [7552](https://github.com/wixtoolset/issues/issues/7552): Add burn command line argument to log to console: /clog or /conlog
- [7877](https://github.com/wixtoolset/issues/issues/7877): ArpEntry reads QuietUninstallString or UninstallString, and uses UninstallArguments for the uninstall command line
- [7877](https://github.com/wixtoolset/issues/issues/7877): ArpEntry reads QuietUninstallString or UninstallString, and uses UninstallArguments for the uninstall command line
- ExePackage/@DetectVersionVariable: Support using one of the XxxSearch elements to provide a version number in a variable to test against the package's version.
17 changes: 17 additions & 0 deletions src/api/wix/WixToolset.Data/Symbols/WixBundleExePackageSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public static partial class SymbolDefinitions
new IntermediateFieldDefinition(nameof(WixBundleExePackageSymbolFields.DetectionType), IntermediateFieldType.Number),
new IntermediateFieldDefinition(nameof(WixBundleExePackageSymbolFields.ArpId), IntermediateFieldType.String),
new IntermediateFieldDefinition(nameof(WixBundleExePackageSymbolFields.ArpDisplayVersion), IntermediateFieldType.String),
new IntermediateFieldDefinition(nameof(WixBundleExePackageSymbolFields.DetectVersionVariable), IntermediateFieldType.String),
new IntermediateFieldDefinition(nameof(WixBundleExePackageSymbolFields.Version), IntermediateFieldType.String),
},
typeof(WixBundleExePackageSymbol));
}
Expand All @@ -39,6 +41,8 @@ public enum WixBundleExePackageSymbolFields
DetectionType,
ArpId,
ArpDisplayVersion,
DetectVersionVariable,
Version,
}

/// <summary>
Expand All @@ -49,6 +53,7 @@ public enum WixBundleExePackageDetectionType
None,
Condition,
Arp,
VersionVariable,
}

[Flags]
Expand Down Expand Up @@ -160,5 +165,17 @@ public bool ArpWin64
public bool Repairable => this.RepairCommand != null;

public bool Uninstallable => this.UninstallCommand != null;

public string DetectVersionVariable
{
get => (string)this.Fields[(int)WixBundleExePackageSymbolFields.DetectVersionVariable];
set => this.Set((int)WixBundleExePackageSymbolFields.DetectVersionVariable, value);
}

public string Version
{
get => (string)this.Fields[(int)WixBundleExePackageSymbolFields.Version];
set => this.Set((int)WixBundleExePackageSymbolFields.Version, value);
}
}
}
90 changes: 90 additions & 0 deletions src/burn/engine/exeengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ static HRESULT DetectArpEntry(
__out BOOTSTRAPPER_PACKAGE_STATE* pPackageState,
__out_opt LPWSTR* psczQuietUninstallString
);
static HRESULT DetectByVersionVariable(
__in BURN_VARIABLES* pVariables,
__in const BURN_PACKAGE* pPackage,
__out BOOTSTRAPPER_PACKAGE_STATE* pPackageState
);

// function definitions

Expand All @@ -33,6 +38,10 @@ extern "C" HRESULT ExeEngineParsePackageFromXml(
{
pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_ARP;
}
else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
{
pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_VERSION;
}
else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1))
{
pPackage->Exe.detectionType = BURN_EXE_DETECTION_TYPE_NONE;
Expand Down Expand Up @@ -87,6 +96,30 @@ extern "C" HRESULT ExeEngineParsePackageFromXml(

pPackage->Exe.fUninstallable = TRUE;
}
else if (BURN_EXE_DETECTION_TYPE_VERSION == pPackage->Exe.detectionType)
{
// @DetectVersionVariable
hr = XmlGetAttributeEx(pixnExePackage, L"DetectVersionVariable", &pPackage->Exe.sczDetectVersionVariable);
ExitOnRequiredXmlQueryFailure(hr, "Failed to get @DetectVersionVariable.");

// @PackageVersion
hr = XmlGetAttributeEx(pixnExePackage, L"PackageVersion", &scz);
ExitOnRequiredXmlQueryFailure(hr, "Failed to get @PackageVersion.");

hr = VerParseVersion(scz, 0, FALSE, &pPackage->Exe.pExePackageVersion);
ExitOnFailure(hr, "Failed to parse @PackageVersion: %ls", scz);

if (pPackage->Exe.pExePackageVersion->fInvalid)
{
LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
}

// @UninstallArguments
hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UninstallArguments.");

pPackage->Exe.fUninstallable = TRUE;
}

// @InstallArguments
hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments);
Expand Down Expand Up @@ -151,7 +184,9 @@ extern "C" void ExeEnginePackageUninitialize(
ReleaseStr(pPackage->Exe.sczRepairArguments);
ReleaseStr(pPackage->Exe.sczUninstallArguments);
ReleaseStr(pPackage->Exe.sczArpKeyPath);
ReleaseStr(pPackage->Exe.sczDetectVersionVariable);
ReleaseVerutilVersion(pPackage->Exe.pArpDisplayVersion);
ReleaseVerutilVersion(pPackage->Exe.pExePackageVersion);
ReleaseMem(pPackage->Exe.rgExitCodes);

// free command-line arguments
Expand Down Expand Up @@ -208,6 +243,11 @@ extern "C" HRESULT ExeEngineDetectPackage(
hr = DetectArpEntry(pPackage, &pPackage->currentState, NULL);
ExitOnFailure(hr, "Failed to detect EXE package by ArpEntry.");

break;
case BURN_EXE_DETECTION_TYPE_VERSION:
hr = DetectByVersionVariable(pVariables, pPackage, &pPackage->currentState);
ExitOnFailure(hr, "Failed to detect EXE package by version variable.");

break;
default:
ExitWithRootFailure(hr, E_INVALIDARG, "Unknown EXE package detection type: %d.", pPackage->Exe.detectionType);
Expand Down Expand Up @@ -1073,6 +1113,56 @@ extern "C" HRESULT ExeEngineHandleExitCode(
return hr;
}

static HRESULT DetectByVersionVariable(
__in BURN_VARIABLES* pVariables,
__in const BURN_PACKAGE* pPackage,
__out BOOTSTRAPPER_PACKAGE_STATE* pPackageState
)
{
HRESULT hr = S_OK;
VERUTIL_VERSION* pDetectedVersion = NULL;
int nCompareResult = 0;

*pPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;

ExitOnNull((pPackage->Exe.sczDetectVersionVariable && *pPackage->Exe.sczDetectVersionVariable), hr, E_INVALIDSTATE, "DetectVersionVariable is null.");
ExitOnNull(pPackage->Exe.pExePackageVersion, hr, E_INVALIDSTATE, "ExeVersion is null.");

hr = VariableGetVersion(pVariables, pPackage->Exe.sczDetectVersionVariable, &pDetectedVersion);
if (hr == E_NOTFOUND)
{
hr = S_OK;
ExitFunction();
}
ExitOnFailure(hr, "Failed to parse detected version variable.");

if (pDetectedVersion->fInvalid)
{
LogId(REPORT_WARNING, MSG_DETECTED_EXE_PACKAGE_INVALID_VERSION, pPackage->Exe.sczDetectVersionVariable, pDetectedVersion->sczVersion);
}

hr = VerCompareParsedVersions(pPackage->Exe.pExePackageVersion, pDetectedVersion, &nCompareResult);
ExitOnFailure(hr, "Failed to compare versions.");

if (nCompareResult < 0)
{
*pPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
}
else if (nCompareResult > 0)
{
*pPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
}
else
{
*pPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
}

LExit:
ReleaseVerutilVersion(pDetectedVersion);

return hr;
}

static HRESULT DetectArpEntry(
__in const BURN_PACKAGE* pPackage,
__out BOOTSTRAPPER_PACKAGE_STATE* pPackageState,
Expand Down
4 changes: 4 additions & 0 deletions src/burn/engine/package.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enum BURN_EXE_DETECTION_TYPE
BURN_EXE_DETECTION_TYPE_NONE,
BURN_EXE_DETECTION_TYPE_CONDITION,
BURN_EXE_DETECTION_TYPE_ARP,
BURN_EXE_DETECTION_TYPE_VERSION,
};

enum BURN_EXE_EXIT_CODE_TYPE
Expand Down Expand Up @@ -372,6 +373,9 @@ typedef struct _BURN_PACKAGE
LPWSTR sczArpKeyPath;
VERUTIL_VERSION* pArpDisplayVersion;

LPWSTR sczDetectVersionVariable;
VERUTIL_VERSION* pExePackageVersion;

LPWSTR sczDetectCondition;
LPWSTR sczInstallArguments;
LPWSTR sczRepairArguments;
Expand Down
10 changes: 10 additions & 0 deletions src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,16 @@ public void Execute()
writer.WriteAttributeString("ArpWin64", "yes");
}

if (!String.IsNullOrEmpty(exePackage.UninstallCommand))
{
writer.WriteAttributeString("UninstallArguments", exePackage.UninstallCommand);
}
break;
case WixBundleExePackageDetectionType.VersionVariable:
writer.WriteAttributeString("DetectionType", "version");
writer.WriteAttributeString("DetectVersionVariable", exePackage.DetectVersionVariable);
writer.WriteAttributeString("PackageVersion", exePackage.Version);

if (!String.IsNullOrEmpty(exePackage.UninstallCommand))
{
writer.WriteAttributeString("UninstallArguments", exePackage.UninstallCommand);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ private void ValidateExePackage(WixBundleExePackageSymbol symbol, WixBundlePacka
this.Messaging.Write(WarningMessages.InvalidWixVersion(symbol.SourceLineNumbers, symbol.ArpDisplayVersion, "ArpEntry", "Version"));
}
}
else if (symbol.DetectionType == WixBundleExePackageDetectionType.VersionVariable)
{
if (!this.BackendHelper.IsValidWixVersion(symbol.Version))
{
this.Messaging.Write(WarningMessages.InvalidWixVersion(symbol.SourceLineNumbers, symbol.Version, "ExePackage", "Version"));
}
}
}
}

Expand Down
53 changes: 45 additions & 8 deletions src/wix/WixToolset.Core/Compiler_Bundle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2127,6 +2127,8 @@ private string ParseChainPackage(XElement node, WixBundlePackageType packageType
string uninstallArguments = null;
var perMachine = YesNoDefaultType.NotSet;
string detectCondition = null;
string detectVersionVariable = null;
string version = null;
string protocol = null;
long? installSize = null;
var enableFeatureSelection = YesNoType.NotSet;
Expand All @@ -2135,7 +2137,7 @@ private string ParseChainPackage(XElement node, WixBundlePackageType packageType
var bundle = YesNoType.NotSet;
var slipstream = YesNoType.NotSet;
var hasPayloadInfo = false;
WixBundleExePackageDetectionType? exeDetectionType = WixBundleExePackageDetectionType.None;
WixBundleExePackageDetectionType exeDetectionType = WixBundleExePackageDetectionType.None;
string arpId = null;
string arpDisplayVersion = null;
var arpWin64 = YesNoType.NotSet;
Expand Down Expand Up @@ -2255,6 +2257,26 @@ private string ParseChainPackage(XElement node, WixBundlePackageType packageType
case "DetectCondition":
detectCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu);

if (exeDetectionType != WixBundleExePackageDetectionType.None)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DetectVersionVariable"));
}
exeDetectionType = WixBundleExePackageDetectionType.Condition;
break;
case "DetectVersionVariable":
detectVersionVariable = this.Core.GetAttributeBundleVariableNameValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Exe);

if (exeDetectionType != WixBundleExePackageDetectionType.None)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DetectCondition"));
}
exeDetectionType = WixBundleExePackageDetectionType.VersionVariable;
break;
case "Version":
version = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Exe);
break;
case "Protocol":
protocol = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
Expand Down Expand Up @@ -2332,9 +2354,9 @@ private string ParseChainPackage(XElement node, WixBundlePackageType packageType
allowed = packageType == WixBundlePackageType.Exe;
if (allowed)
{
if (exeDetectionType.Value != WixBundleExePackageDetectionType.None)
if (exeDetectionType != WixBundleExePackageDetectionType.None)
{
throw new WixException($"Unexpected WixBundleExePackageDetectionType: {exeDetectionType}");
this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, (exeDetectionType == WixBundleExePackageDetectionType.VersionVariable) ? "DetectVersionVariable" : "DetectCondition"));
}

exeDetectionType = WixBundleExePackageDetectionType.Arp;
Expand Down Expand Up @@ -2403,7 +2425,7 @@ private string ParseChainPackage(XElement node, WixBundlePackageType packageType
}
}

if (packageType == WixBundlePackageType.Exe && exeDetectionType.Value == WixBundleExePackageDetectionType.None && (detectCondition != null || uninstallArguments != null))
if (packageType == WixBundlePackageType.Exe && exeDetectionType == WixBundleExePackageDetectionType.None && (detectCondition != null || uninstallArguments != null))
{
exeDetectionType = WixBundleExePackageDetectionType.Condition;
}
Expand Down Expand Up @@ -2438,11 +2460,24 @@ private string ParseChainPackage(XElement node, WixBundlePackageType packageType
perMachine = YesNoDefaultType.Default;
}

if (version != null && exeDetectionType != WixBundleExePackageDetectionType.VersionVariable)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Version", "DetectVersionVariable"));
}

if (exeDetectionType == WixBundleExePackageDetectionType.Arp)
{
if (!String.IsNullOrEmpty(detectCondition))
// Missing attributes are reported when parsing the element.
}
else if (exeDetectionType == WixBundleExePackageDetectionType.VersionVariable)
{
if (String.IsNullOrEmpty(detectVersionVariable))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DetectVersionVariable"));
}
if (String.IsNullOrEmpty(version))
{
this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "ArpEntry", "DetectCondition"));
version = $"!(bind.packageVersion.{id.Id})";
}
}
else if (exeDetectionType == WixBundleExePackageDetectionType.Condition)
Expand Down Expand Up @@ -2631,9 +2666,11 @@ private string ParseChainPackage(XElement node, WixBundlePackageType packageType
RepairCommand = repairArguments,
UninstallCommand = uninstallArguments,
ExeProtocol = protocol,
DetectionType = exeDetectionType.Value,
DetectionType = exeDetectionType,
ArpId = arpId,
ArpDisplayVersion = arpDisplayVersion,
DetectVersionVariable = detectVersionVariable,
Version = version,
});
break;

Expand Down Expand Up @@ -3406,7 +3443,7 @@ private void CreateRollbackBoundary(SourceLineNumber sourceLineNumbers, Identifi
{
this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));

var rollbackBoundary = this.Core.AddSymbol(new WixBundleRollbackBoundarySymbol(sourceLineNumbers, id)
this.Core.AddSymbol(new WixBundleRollbackBoundarySymbol(sourceLineNumbers, id)
{
Vital = vital == YesNoType.Yes,
});
Expand Down
Loading

0 comments on commit e7b14a8

Please sign in to comment.