Support bind variables in Package and Bundle versions
Closes 6779
Rob Mensching committed
Jul 7, 2022 at 17:52 UTC
36a8f464f6c9b7e553c94b29ee3ca836638eef4d
22 files changed
+380
-72
src/api/wix/WixToolset.Data/ErrorMessages.cs
+1
-6
@@ -1326,12 +1326,7 @@ namespace WixToolset.Data
1326
1327
public static Message InvalidProductVersion(SourceLineNumber sourceLineNumbers, string version)
1328
{
1329
- return Message(sourceLineNumbers, Ids.InvalidProductVersion, "Invalid product version '{0}'. Product version must have a major version less than 256, a minor version less than 256, and a build version less than 65536.", version);
1330
- }
1331
-
1332
- public static Message InvalidProductVersion(SourceLineNumber sourceLineNumbers, string version, string packagePath)
1333
- {
1334
- return Message(sourceLineNumbers, Ids.InvalidProductVersion, "Invalid product version '{0}' in package '{1}'. When included in a bundle, all product version fields in an MSI package must be less than 65536.", version, packagePath);
1329
+ return Message(sourceLineNumbers, Ids.InvalidProductVersion, "Invalid product version '{0}'. MSI product versions must have a major version less than 256, a minor version less than 256, and a build version less than 65536. The revision value is ignored but version labels and metadata are not allowed.", version);
1330
}
1331
1332
public static Message InvalidRemoveComponent(SourceLineNumber sourceLineNumbers, string component, string feature, string transformPath)
src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs
+18
-1
@@ -93,7 +93,7 @@ namespace WixToolset.Extensibility.Services
93
/// <param name="value">The Filename value.</param>
94
/// <param name="source">true to get a source name; false to get a target name</param>
95
/// <param name="longName">true to get a long name; false to get a short name</param>
96
- /// <returns>The name.</returns>
96
+ /// <returns>The requesed file name.</returns>
97
string GetMsiFileName(string value, bool source, bool longName);
98
99
/// <summary>
@@ -165,5 +165,22 @@ namespace WixToolset.Extensibility.Services
165
/// Thus the returned array will always be of length 4.
166
/// </remarks>
167
string[] SplitMsiFileName(string value);
168
+
169
+ /// <summary>
170
+ /// Tries to parse a version from the provided version string.
171
+ /// </summary>
172
+ /// <param name="version">The version to verify and parse.</param>
173
+ /// <param name="parsedVersion">The parsed result if possible, otherwise null.</param>
174
+ /// <returns>True if the version was able to parsed, otherwise false.</returns>
175
+ bool TryParseFourPartVersion(string version, out string parsedVersion);
176
+
177
+ /// <summary>
178
+ /// Tries to parse an MSI product version from the provided version string.
179
+ /// </summary>
180
+ /// <param name="version">The version to verify and parse.</param>
181
+ /// <param name="strict">Indicates whether to return a strict (255.255.65535) product version or any valid product version (255.255.65535.*).</param>
182
+ /// <param name="parsedVersion">The parsed result if possible, otherwise null.</param>
183
+ /// <returns>True if the version was able to parsed as an product version, otherwise false.</returns>
184
+ bool TryParseMsiProductVersion(string version, bool strict, out string parsedVersion);
185
}
186
}
src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+26
-6
@@ -227,6 +227,16 @@ namespace WixToolset.Core.Burn
227
return;
228
}
229
230
+ // Resolve any delayed fields now that the variable cache is populated with package information.
231
+ if (this.DelayedFields.Any())
232
+ {
233
+ this.BackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache);
234
+ }
235
+
236
+ // Now that delayed variables are resolved the bundle version must be valid so ensure
237
+ // it is correct.
238
+ this.ProcessBundleVersion(bundleSymbol);
239
+
240
// Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later)
241
// are present.
242
payloadSymbols = section.Symbols.OfType<WixBundlePayloadSymbol>().ToDictionary(t => t.Id.Id);
@@ -344,12 +354,6 @@ namespace WixToolset.Core.Burn
354
boundaries = command.UsedRollbackBoundaries;
355
}
356
347
- // Resolve any delayed fields before generating the manifest.
348
- if (this.DelayedFields.Any())
349
- {
350
- this.BackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache);
351
- }
352
-
357
{
358
var command = new ProcessDependencyProvidersCommand(this.ServiceProvider, section, facades);
359
command.Execute();
@@ -535,6 +539,22 @@ namespace WixToolset.Core.Burn
539
bundleSymbol.UpgradeCode = this.NormalizeBundleRelatedBundleId(bundleSymbol.SourceLineNumbers, bundleSymbol.UpgradeCode, null, null);
540
}
541
542
+ private void ProcessBundleVersion(WixBundleSymbol bundleSymbol)
543
+ {
544
+ if (WixVersion.TryParse(bundleSymbol.Version, out var wixVersion))
545
+ {
546
+ // Trim the prefix from the version if it is there.
547
+ if (wixVersion.Prefix.HasValue)
548
+ {
549
+ bundleSymbol.Version = bundleSymbol.Version.Substring(1);
550
+ }
551
+ }
552
+ else
553
+ {
554
+ this.Messaging.Write(ErrorMessages.IllegalVersionValue(bundleSymbol.SourceLineNumbers, "Bundle", "Version", bundleSymbol.Version));
555
+ }
556
+ }
557
+
558
private string NormalizeBundleRelatedBundleId(SourceLineNumber sourceLineNumber, string relatedBundleId, string elementName, string attributeName)
559
{
560
if (Guid.TryParse(relatedBundleId, out var guid))
src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
+1
-1
@@ -92,7 +92,7 @@ namespace WixToolset.Core.Burn
92
93
public static Message InvalidBundleManifest(SourceLineNumber sourceLineNumbers, string bundleExecutable, string reason)
94
{
95
- return Message(sourceLineNumbers, Ids.InvalidBundleManifest, "Unable to harvest bundle executable '{0}'. The manifest was invalid. {1}", bundleExecutable, reason);
95
+ return Message(sourceLineNumbers, Ids.InvalidBundleManifest, "Unable to read bundle executable '{0}'. Its manifest is invalid. {1}", bundleExecutable, reason);
96
}
97
98
private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
+10
@@ -134,6 +134,16 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
134
return this.backendHelper.SplitMsiFileName(value);
135
}
136
137
+ public bool TryParseFourPartVersion(string version, out string parsedVersion)
138
+ {
139
+ return this.backendHelper.TryParseFourPartVersion(version, out parsedVersion);
140
+ }
141
+
142
+ public bool TryParseMsiProductVersion(string version, bool strict, out string parsedVersion)
143
+ {
144
+ return this.backendHelper.TryParseMsiProductVersion(version, strict, out parsedVersion);
145
+ }
146
+
147
public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null)
148
{
149
return this.backendHelper.TrackFile(path, type, sourceLineNumbers);
src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+20
-11
@@ -127,6 +127,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
127
tableDefinitions = command.TableDefinitions;
128
}
129
130
+ if (section.Type == SectionType.Product)
131
+ {
132
+ this.ProcessProductVersion(packageSymbol, section, validate: false);
133
+ }
134
+
135
// Calculate codepage
136
var codepage = this.CalculateCodepage(packageSymbol, moduleSymbol, patchSymbol);
137
@@ -298,8 +303,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
303
command.Execute();
304
}
305
301
- // Now that delayed fields are processed, validate the package/module version.
302
- this.VerifyVersion(packageSymbol, moduleSymbol);
306
+ // Now that delayed fields are processed, fixup the package version (if needed) and validate it
307
+ // which will short circuit duplicate errors later if the ProductVersion is invalid.
308
+ if (SectionType.Product == section.Type)
309
+ {
310
+ this.ProcessProductVersion(packageSymbol, section, validate: true);
311
+ }
312
313
// Process dependency references.
314
if (SectionType.Product == section.Type || SectionType.Module == section.Type)
@@ -551,21 +560,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
560
return result;
561
}
562
554
- private void VerifyVersion(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol)
563
+ private void ProcessProductVersion(WixPackageSymbol packageSymbol, IntermediateSection section, bool validate)
564
{
556
- if (packageSymbol != null)
565
+ if (this.WindowsInstallerBackendHelper.TryParseMsiProductVersion(packageSymbol.Version, strict: false, out var version))
566
{
558
- if (!this.WindowsInstallerBackendHelper.IsValidMsiProductVersion(packageSymbol.Version))
567
+ if (packageSymbol.Version != version)
568
{
560
- this.Messaging.Write(ErrorMessages.InvalidProductVersion(packageSymbol.SourceLineNumbers, packageSymbol.Version));
569
+ packageSymbol.Version = version;
570
+
571
+ var productVersionProperty = section.Symbols.OfType<PropertySymbol>().FirstOrDefault(p => p.Id.Id == "ProductVersion");
572
+ productVersionProperty.Value = version;
573
}
574
}
563
- else if (moduleSymbol != null)
575
+ else if (validate)
576
{
565
- if (!this.WindowsInstallerBackendHelper.IsValidFourPartVersion(moduleSymbol.Version))
566
- {
567
- this.Messaging.Write(WindowsInstallerBackendErrors.InvalidModuleVersion(moduleSymbol.SourceLineNumbers, moduleSymbol.Version));
568
- }
577
+ this.Messaging.Write(ErrorMessages.InvalidProductVersion(packageSymbol.SourceLineNumbers, packageSymbol.Version));
578
}
579
}
580
src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
+47
-4
@@ -224,6 +224,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
224
this.AddWixEnsureTableSymbol((WixEnsureTableSymbol)symbol);
225
break;
226
227
+ case SymbolDefinitionType.WixModule:
228
+ this.AddWixModuleSymbol((WixModuleSymbol)symbol);
229
+ break;
230
+
231
case SymbolDefinitionType.WixPackage:
232
this.AddWixPackageSymbol((WixPackageSymbol)symbol);
233
break;
@@ -1033,14 +1037,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1037
1038
private void AddUpgradeSymbol(UpgradeSymbol symbol)
1039
{
1036
- if (!String.IsNullOrEmpty(symbol.VersionMin) && !this.BackendHelper.IsValidMsiProductVersion(symbol.VersionMin))
1040
+ if (this.CheckUpgradeVersion(symbol, symbol.VersionMin, out var changedVersion))
1041
{
1038
- this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, symbol.VersionMin));
1042
+ symbol.VersionMin = changedVersion;
1043
}
1044
1041
- if (!String.IsNullOrEmpty(symbol.VersionMax) && !this.BackendHelper.IsValidMsiProductVersion(symbol.VersionMax))
1045
+ if (this.CheckUpgradeVersion(symbol, symbol.VersionMax, out changedVersion))
1046
{
1043
- this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, symbol.VersionMax));
1047
+ symbol.VersionMax = changedVersion;
1048
}
1049
1050
var row = (UpgradeRow)this.CreateRow(symbol, "Upgrade");
@@ -1248,6 +1252,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1252
}
1253
}
1254
1255
+ private void AddWixModuleSymbol(WixModuleSymbol symbol)
1256
+ {
1257
+ if (!String.IsNullOrEmpty(symbol.Version) && this.BackendHelper.TryParseFourPartVersion(symbol.Version, out var version))
1258
+ {
1259
+ var row = this.CreateRow(symbol, "ModuleSignature");
1260
+ row[0] = symbol.ModuleId;
1261
+ row[1] = symbol.Language;
1262
+ row[2] = version;
1263
+ }
1264
+ else
1265
+ {
1266
+ this.Messaging.Write(WindowsInstallerBackendErrors.InvalidModuleVersion(symbol.SourceLineNumbers, symbol.Version));
1267
+ }
1268
+ }
1269
+
1270
private void AddWixPackageSymbol(WixPackageSymbol symbol)
1271
{
1272
// TODO: Remove the following from the compiler and do it here instead.
@@ -1584,6 +1603,30 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1603
return this.BackendHelper.CreateRow(this.Section, symbol, this.Data, tableDefinition);
1604
}
1605
1606
+ private bool CheckUpgradeVersion(UpgradeSymbol symbol, string version, out string changedVersion)
1607
+ {
1608
+ if (String.IsNullOrEmpty(version))
1609
+ {
1610
+ // Null is allowed.
1611
+ }
1612
+ else if (this.BackendHelper.TryParseMsiProductVersion(version, strict: false, out var parsedVersionMin))
1613
+ {
1614
+ // If the strictly parsed value is different, update the symbol.
1615
+ if (version != parsedVersionMin)
1616
+ {
1617
+ changedVersion = parsedVersionMin;
1618
+ return true;
1619
+ }
1620
+ }
1621
+ else
1622
+ {
1623
+ this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, version));
1624
+ }
1625
+
1626
+ changedVersion = null;
1627
+ return false;
1628
+ }
1629
+
1630
private string CreateShortName(string longName, bool keepExtension, params string[] args)
1631
{
1632
longName = longName.ToLowerInvariant();
src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
+25
-25
@@ -36,32 +36,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
36
{
37
var trackedFiles = new List<ITrackedFile>();
38
39
- string productName = null;
40
- string productVersion = null;
41
- string manufacturer = null;
42
- string upgradeCode = null;
43
-
39
var summaryInfo = this.Section.Symbols.OfType<SummaryInformationSymbol>().FirstOrDefault(s => s.PropertyId == SummaryInformationType.PackageCode);
40
var packageCode = NormalizeGuid(summaryInfo?.Value);
41
47
- foreach (var property in this.Section.Symbols.OfType<PropertySymbol>())
48
- {
49
- switch (property.Id.Id)
50
- {
51
- case "ProductName":
52
- productName = property.Value;
53
- break;
54
- case "ProductVersion":
55
- productVersion = property.Value;
56
- break;
57
- case "Manufacturer":
58
- manufacturer = property.Value;
59
- break;
60
- case "UpgradeCode":
61
- upgradeCode = NormalizeGuid(property.Value);
62
- break;
63
- }
64
- }
42
+ var packageSymbol = this.Section.Symbols.OfType<WixPackageSymbol>().First();
43
+ var upgradeCode = NormalizeGuid(packageSymbol.UpgradeCode);
44
45
var fileSymbolsById = this.Section.Symbols.OfType<FileSymbol>().Where(f => f.Id != null).ToDictionary(f => f.Id.Id);
46
@@ -83,7 +62,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
62
63
using (var fs = new FileStream(fileSymbol.Source.Path, FileMode.Create))
64
{
86
- CreateTagFile(fs, uniqueId, productName, productVersion, tagRow.Regid, manufacturer, persistentId);
65
+ CreateTagFile(fs, uniqueId, packageSymbol.Name, packageSymbol.Version, tagRow.Regid, packageSymbol.Manufacturer, persistentId);
66
}
67
68
// Ensure the matching "SoftwareIdentificationTag" row exists and
@@ -113,7 +92,28 @@ namespace WixToolset.Core.WindowsInstaller.Bind
92
93
private static void CreateTagFile(Stream stream, string uniqueId, string name, string version, string regid, string manufacturer, string persistendId)
94
{
116
- var versionScheme = Version.TryParse(version, out _) ? "multipartnumeric" : "alphanumeric";
95
+ var versionScheme = "alphanumeric";
96
+
97
+ if (WixVersion.TryParse(version, out var parsedVersion))
98
+ {
99
+ if (parsedVersion.Prefix.HasValue)
100
+ {
101
+ version = version.Substring(1);
102
+ }
103
+
104
+ if (Version.TryParse(version, out _))
105
+ {
106
+ versionScheme = "multipartnumeric";
107
+ }
108
+ else if (!parsedVersion.HasRevision)
109
+ {
110
+ versionScheme = "semver";
111
+ }
112
+ else
113
+ {
114
+ versionScheme = "multipartnumeric+suffix";
115
+ }
116
+ }
117
118
using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true }))
119
{
src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs
+10
@@ -123,6 +123,16 @@ namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices
123
return this.backendHelper.SplitMsiFileName(value);
124
}
125
126
+ public bool TryParseFourPartVersion(string version, out string parsedVersion)
127
+ {
128
+ return this.backendHelper.TryParseFourPartVersion(version, out parsedVersion);
129
+ }
130
+
131
+ public bool TryParseMsiProductVersion(string version, bool strict, out string parsedVersion)
132
+ {
133
+ return this.backendHelper.TryParseMsiProductVersion(version, strict, out parsedVersion);
134
+ }
135
+
136
public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null)
137
{
138
return this.backendHelper.TrackFile(path, type, sourceLineNumbers);
src/wix/WixToolset.Core/Common.cs
+1
-1
@@ -134,7 +134,7 @@ namespace WixToolset.Core
134
135
public static bool IsValidMsiProductVersion(string version)
136
{
137
- return Version.TryParse(version, out var ver) && ver.Major < 256 && ver.Minor < 256 && ver.Build < 65536;
137
+ return WixVersion.TryParse(version, out var wixVersion) && wixVersion.HasMajor && wixVersion.Major < 256 && wixVersion.Minor < 256 && wixVersion.Patch < 65536 && wixVersion.Labels == null && String.IsNullOrEmpty(wixVersion.Metadata);
138
}
139
140
public static bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
src/wix/WixToolset.Core/Compiler.cs
-1
@@ -5910,7 +5910,6 @@ namespace WixToolset.Core
5910
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Message"));
5911
}
5912
5913
-
5913
this.Core.ParseForExtensionElements(node);
5914
5915
if (!this.Core.EncounteredError)
src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
+35
@@ -119,5 +119,40 @@ namespace WixToolset.Core.ExtensibilityServices
119
{
120
return Common.IsValidShortFilename(filename, allowWildcards);
121
}
122
+
123
+ public bool TryParseFourPartVersion(string version, out string parsedVersion)
124
+ {
125
+ if (WixVersion.TryParse(version, out var wixVersion) && wixVersion.HasMajor && wixVersion.Major < 65536 && wixVersion.Minor < 65536 && wixVersion.Patch < 65536 && wixVersion.Revision < 65536)
126
+ {
127
+ parsedVersion = $"{wixVersion.Major}.{wixVersion.Minor}.{wixVersion.Patch}.{wixVersion.Revision}";
128
+ return true;
129
+ }
130
+
131
+ parsedVersion = null;
132
+ return false;
133
+ }
134
+
135
+ public bool TryParseMsiProductVersion(string version, bool strict, out string parsedVersion)
136
+ {
137
+ if (WixVersion.TryParse(version, out var wixVersion) && wixVersion.HasMajor && wixVersion.Major < 256 && wixVersion.Minor < 256 && wixVersion.Patch < 65536 && wixVersion.Labels == null && String.IsNullOrEmpty(wixVersion.Metadata))
138
+ {
139
+ parsedVersion = $"{wixVersion.Major}.{wixVersion.Minor}";
140
+
141
+ if (strict || wixVersion.HasPatch)
142
+ {
143
+ parsedVersion += $".{wixVersion.Patch}";
144
+ }
145
+
146
+ if (!strict && wixVersion.HasRevision)
147
+ {
148
+ parsedVersion += $".{wixVersion.Revision}";
149
+ }
150
+
151
+ return true;
152
+ }
153
+
154
+ parsedVersion = null;
155
+ return false;
156
+ }
157
}
158
}
src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
+2
-3
@@ -552,10 +552,9 @@ namespace WixToolset.Core.ExtensibilityServices
552
553
if (!String.IsNullOrEmpty(value))
554
{
555
- if (WixVersion.TryParse(value, out var version))
555
+ if (WixVersion.TryParse(value, out var _))
556
{
557
- // Return the attribute value sans-prefix, if present.
558
- return version.Prefix.HasValue ? value.Substring(1) : value;
557
+ return value;
558
}
559
560
// Allow versions to contain binder variables.
src/wix/test/WixToolsetTest.Converters/TestData/PackageSummaryInformation/TypicalV3.wxs
+1
-1
@@ -34,4 +34,4 @@
34
</Component>
35
</ComponentGroup>
36
</Product>
37
-</Wix>
\ No newline at end of file
37
+</Wix>
src/wix/test/WixToolsetTest.Core/ParserHelperFixture.cs
+1
-1
@@ -78,7 +78,7 @@ namespace WixToolsetTest.Core
78
var attribute = CreateAttribute("v1.2.3.4");
79
var result = helper.GetAttributeVersionValue(null, attribute);
80
81
- WixAssert.StringEqual("1.2.3.4", result);
81
+ WixAssert.StringEqual("v1.2.3.4", result);
82
}
83
84
src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+27
-1
@@ -8,7 +8,6 @@ namespace WixToolsetTest.CoreIntegration
8
using System.Linq;
9
using System.Text;
10
using System.Xml;
11
- using System.Xml.Linq;
11
using Example.Extension;
12
using WixBuildTools.TestSupport;
13
using WixToolset.Core.Burn;
@@ -21,6 +20,33 @@ namespace WixToolsetTest.CoreIntegration
20
21
public class BundleFixture
22
{
23
+ [Fact]
24
+ public void CanBuildBundleWithBindVariableVersion()
25
+ {
26
+ var folder = TestData.Get(@"TestData");
27
+
28
+ using (var fs = new DisposableFileSystem())
29
+ {
30
+ var baseFolder = fs.GetFolder();
31
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
32
+ var exePath = Path.Combine(baseFolder, @"bin\test.exe");
33
+
34
+ var result = WixRunner.Execute(new[]
35
+ {
36
+ "build",
37
+ Path.Combine(folder, "BundleBindVariables", "BindVarBundleVersion.wxs"),
38
+ "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
39
+ "-bindpath", Path.Combine(folder, ".Data"),
40
+ "-intermediateFolder", intermediateFolder,
41
+ "-o", exePath,
42
+ });
43
+
44
+ result.AssertSuccess();
45
+
46
+ Assert.True(File.Exists(exePath));
47
+ }
48
+ }
49
+
50
[Fact]
51
public void CanBuildMultiFileBundle()
52
{
src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundleBindVariables/BindVarBundleVersion.wxs
new
+14
@@ -0,0 +1,14 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3
+ <Bundle Name="BindVarBundleVersion" Version="!(wix.BundleVersion)" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4
+ <BootstrapperApplication Id="fakeba">
5
+ <BootstrapperApplicationDll SourceFile="fakeba.dll" />
6
+ </BootstrapperApplication>
7
+
8
+ <Chain>
9
+ <MsiPackage SourceFile="test.msi" />
10
+ </Chain>
11
+
12
+ <WixVariable Id="BundleVersion" Value="v8.7.6.5" />
13
+ </Bundle>
14
+</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/PackageWithBindVariableVersion.wxs
new
+33
@@ -0,0 +1,33 @@
1
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2
+ <Package Version="!(wix.Version)"
3
+ Name="MsiPackage"
4
+ Manufacturer="Example Corporation"
5
+ UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a"
6
+ Scope="perUser">
7
+ <MajorUpgrade DowngradeErrorMessage="Downgrade not allowed" />
8
+ <MediaTemplate EmbedCab="true" />
9
+
10
+ <Feature Id="ProductFeature" Title="Feature title">
11
+ <ComponentGroupRef Id="ProductComponents" />
12
+ </Feature>
13
+
14
+ <WixVariable Id="Version" Value="v9.8.7.6" />
15
+ </Package>
16
+
17
+ <Fragment>
18
+ <StandardDirectory Id="DesktopFolder">
19
+ <Directory Id="INSTALLFOLDER" Name="MsiPackage !(wix.Version) and !(bind.property.ProductVersion)" />
20
+ </StandardDirectory>
21
+ </Fragment>
22
+
23
+ <Fragment>
24
+ <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
25
+ <Component>
26
+ <File Source="test.txt" />
27
+ </Component>
28
+ <Component Id="Shared.dll" Shared="yes">
29
+ <File Name="Shared.dll" Source="test.txt" />
30
+ </Component>
31
+ </ComponentGroup>
32
+ </Fragment>
33
+</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/PackageWithReplaceableVersion.wxs
renamed
src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs
+1
-1
@@ -39,7 +39,7 @@ namespace WixToolsetTest.CoreIntegration
39
.ToArray();
40
WixAssert.CompareLineByLine(new[]
41
{
42
- "Invalid product version '1.256.0'. Product version must have a major version less than 256, a minor version less than 256, and a build version less than 65536.",
42
+ "Invalid product version '1.256.0'. MSI product versions must have a major version less than 256, a minor version less than 256, and a build version less than 65536. The revision value is ignored but version labels and metadata are not allowed.",
43
}, errorMessages);
44
Assert.Equal(242, result.ExitCode);
45
}
src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs
+107
-5
@@ -12,6 +12,101 @@ namespace WixToolsetTest.CoreIntegration
12
13
public class VersionFixture
14
{
15
+ [Fact]
16
+ public void CanBuildMsiWithPrefixedVersion()
17
+ {
18
+ var folder = TestData.Get(@"TestData");
19
+
20
+ using (var fs = new DisposableFileSystem())
21
+ {
22
+ var baseFolder = fs.GetFolder();
23
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
24
+ var msiPath = Path.Combine(baseFolder, "bin", "test1.msi");
25
+
26
+ var result = WixRunner.Execute(new[]
27
+ {
28
+ "build",
29
+ Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"),
30
+ "-bindpath", Path.Combine(folder, "SingleFile", "data"),
31
+ "-intermediateFolder", intermediateFolder,
32
+ "-d", "Version=v4.3.2.1",
33
+ "-o", msiPath
34
+ });
35
+
36
+ result.AssertSuccess();
37
+
38
+ var productVersion = GetProductVersionFromMsi(msiPath);
39
+ Assert.Equal("4.3.2.1", productVersion);
40
+ }
41
+ }
42
+
43
+ [Fact]
44
+ public void CanBuildMsiWithPrefixedVersionBindVariable()
45
+ {
46
+ var folder = TestData.Get(@"TestData");
47
+
48
+ using (var fs = new DisposableFileSystem())
49
+ {
50
+ var baseFolder = fs.GetFolder();
51
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
52
+ var msiPath = Path.Combine(baseFolder, "bin", "test1.msi");
53
+
54
+ var result = WixRunner.Execute(new[]
55
+ {
56
+ "build",
57
+ Path.Combine(folder, "Version", "PackageWithBindVariableVersion.wxs"),
58
+ "-bindpath", Path.Combine(folder, "SingleFile", "data"),
59
+ "-intermediateFolder", intermediateFolder,
60
+ "-o", msiPath
61
+ });
62
+
63
+ result.AssertSuccess();
64
+
65
+ var productVersion = GetProductVersionFromMsi(msiPath);
66
+ Assert.Equal("9.8.7.6", productVersion);
67
+
68
+ var directoryTable = Query.QueryDatabase(msiPath, new[] { "Directory" }).OrderBy(s => s).ToArray();
69
+ WixAssert.CompareLineByLine(new[]
70
+ {
71
+ "Directory:DesktopFolder\tTARGETDIR\tDesktop",
72
+ "Directory:INSTALLFOLDER\tDesktopFolder\tpja2bznq|MsiPackage v9.8.7.6 and 9.8.7.6",
73
+ "Directory:TARGETDIR\t\tSourceDir"
74
+ }, directoryTable);
75
+ }
76
+ }
77
+
78
+ [Fact]
79
+ public void CannotBuildMsiWithExtendedVersion()
80
+ {
81
+ var folder = TestData.Get(@"TestData");
82
+
83
+ using (var fs = new DisposableFileSystem())
84
+ {
85
+ var baseFolder = fs.GetFolder();
86
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
87
+ var msiPath = Path.Combine(baseFolder, "bin", "test1.msi");
88
+
89
+ var result = WixRunner.Execute(new[]
90
+ {
91
+ "build",
92
+ Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"),
93
+ "-bindpath", Path.Combine(folder, "SingleFile", "data"),
94
+ "-intermediateFolder", intermediateFolder,
95
+ "-d", "Version=v4.3.2-preview.1",
96
+ "-o", msiPath
97
+ });
98
+
99
+ var errorMessages = result.Messages.Where(m => m.Level == MessageLevel.Error)
100
+ .Select(m => m.ToString())
101
+ .ToArray();
102
+ WixAssert.CompareLineByLine(new[]
103
+ {
104
+ "Invalid product version 'v4.3.2-preview.1'. MSI product versions must have a major version less than 256, a minor version less than 256, and a build version less than 65536. The revision value is ignored but version labels and metadata are not allowed.",
105
+ }, errorMessages);
106
+ Assert.Equal(242, result.ExitCode);
107
+ }
108
+ }
109
+
110
[Fact]
111
public void CannotBuildMsiWithInvalidMajorVersion()
112
{
@@ -26,7 +121,7 @@ namespace WixToolsetTest.CoreIntegration
121
var result = WixRunner.Execute(new[]
122
{
123
"build",
29
- Path.Combine(folder, "Version", "Package.wxs"),
124
+ Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"),
125
"-bindpath", Path.Combine(folder, "SingleFile", "data"),
126
"-intermediateFolder", intermediateFolder,
127
"-d", "Version=257.0.0",
@@ -38,7 +133,7 @@ namespace WixToolsetTest.CoreIntegration
133
.ToArray();
134
WixAssert.CompareLineByLine(new[]
135
{
41
- "Invalid product version '257.0.0'. Product version must have a major version less than 256, a minor version less than 256, and a build version less than 65536.",
136
+ "Invalid product version '257.0.0'. MSI product versions must have a major version less than 256, a minor version less than 256, and a build version less than 65536. The revision value is ignored but version labels and metadata are not allowed.",
137
}, errorMessages);
138
Assert.Equal(242, result.ExitCode);
139
}
@@ -60,7 +155,7 @@ namespace WixToolsetTest.CoreIntegration
155
var result = WixRunner.Execute(new[]
156
{
157
"build",
63
- Path.Combine(folder, "Version", "Package.wxs"),
158
+ Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"),
159
"-bindpath", Path.Combine(folder, "SingleFile", "data"),
160
"-intermediateFolder", intermediateFolder,
161
"-d", "Version=255.255.65535",
@@ -82,8 +177,7 @@ namespace WixToolsetTest.CoreIntegration
177
178
result3.AssertSuccess();
179
85
- var propertyTable = Query.QueryDatabase(msiPath, new[] { "Property" }).Select(r => r.Split('\t')).ToDictionary(r => r[0].Substring("Property:".Length), r => r[1]);
86
- Assert.True(propertyTable.TryGetValue("ProductVersion", out var productVersion));
180
+ var productVersion = GetProductVersionFromMsi(msiPath);
181
WixAssert.StringEqual("255.255.65535", productVersion);
182
183
var extractResult = BundleExtractor.ExtractAllContainers(null, bundlePath, Path.Combine(baseFolder, "ba"), Path.Combine(baseFolder, "attached"), Path.Combine(baseFolder, "extract"));
@@ -95,5 +189,13 @@ namespace WixToolsetTest.CoreIntegration
189
WixAssert.StringEqual("2022.3.9-preview.0-build.5+0987654321abcdef1234567890", bundleVersion.Value);
190
}
191
}
192
+
193
+ private static string GetProductVersionFromMsi(string msiPath)
194
+ {
195
+ var propertyTable = Query.QueryDatabase(msiPath, new[] { "Property" }).Select(r => r.Split('\t')).ToDictionary(r => r[0].Substring("Property:".Length), r => r[1]);
196
+ Assert.True(propertyTable.TryGetValue("ProductVersion", out var productVersion));
197
+
198
+ return productVersion;
199
+ }
200
}
201
}
src/wix/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
-4
@@ -14,10 +14,6 @@
14
<Content Include="TestData\**" CopyToOutputDirectory="PreserveNewest" />
15
</ItemGroup>
16
17
- <ItemGroup>
18
- <None Remove="TestData\BundleWithInvalid\BundleWithInvalidLocVariableNames.wxs" />
19
- </ItemGroup>
20
-
17
<ItemGroup>
18
<ProjectReference Include="..\..\WixToolset.Core\WixToolset.Core.csproj" />
19
<ProjectReference Include="..\..\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj" />