Support remote Payloads
Closes 5601 Closes 6802
Rob Mensching committed
Jul 14, 2022 at 06:05 UTC
f46ca6a9dce91607ffc9855270dd6998216e1a8b
10 files changed
+164
-22
src/api/wix/WixToolset.Data/WarningMessages.cs
+1
-1
@@ -650,7 +650,7 @@ namespace WixToolset.Data
650
651
public static Message UxPayloadsOnlySupportEmbedding(SourceLineNumber sourceLineNumbers, string sourceFile)
652
{
653
- return Message(sourceLineNumbers, Ids.UxPayloadsOnlySupportEmbedding, "A bootstrapper application payload ('{0}') was marked for something other than embedded packaging, possibly because it included a @DownloadUrl attribute. At present, bootstrapper application payloads must be embedded in the bundle, so the requested packaging is being ignored.", sourceFile);
653
+ return Message(sourceLineNumbers, Ids.UxPayloadsOnlySupportEmbedding, "A bootstrapper application or bundle extension payload ('{0}') was marked for something other than embedded packaging, possibly because it included a @DownloadUrl attribute. Bootstrapper application and bundle extension payloads must be embedded in the bundle, so the requested packaging is being ignored and the file is being embedded anyway.", sourceFile);
654
}
655
656
public static Message ValidationFailedDueToSystemPolicy()
src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+5
-1
@@ -300,7 +300,11 @@ namespace WixToolset.Core.Burn
300
// downloaded. The current engine requires the UX to be fully present before any downloading starts,
301
// so that rules out downloading. Also, the burn engine does not currently copy external UX payloads
302
// into the temporary UX directory correctly, so we don't allow external either.
303
- if (PackagingType.Embedded != payload.Packaging)
303
+ if (payload.SourceFile is null)
304
+ {
305
+ this.Messaging.Write(BurnBackendErrors.BAContainerCannotContainRemotePayload(payload.SourceLineNumbers, payload.Name));
306
+ }
307
+ else if (PackagingType.Embedded != payload.Packaging)
308
{
309
this.Messaging.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.SourceFile.Path));
310
payload.Packaging = PackagingType.Embedded;
src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
+6
@@ -95,6 +95,11 @@ namespace WixToolset.Core.Burn
95
return Message(sourceLineNumbers, Ids.InvalidBundleManifest, "Unable to read bundle executable '{0}'. Its manifest is invalid. {1}", bundleExecutable, reason);
96
}
97
98
+ public static Message BAContainerCannotContainRemotePayload(SourceLineNumber sourceLineNumbers, string payloadName)
99
+ {
100
+ return Message(sourceLineNumbers, Ids.BAContainerCannotContainRemotePayload, "Bootstrapper application and bundle extension payloads must be embedded in the bundle. The payload '{0}' is remote thus cannot be found for embedding. Provide a full path to the payload via the Payload/@SourceFile attribute.", payloadName);
101
+ }
102
+
103
private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
104
{
105
return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
@@ -116,6 +121,7 @@ namespace WixToolset.Core.Burn
121
FailedToAddIconOrSplashScreenToBundle = 8011,
122
InvalidBundleManifest = 8012,
123
BundleMultipleProviders = 8013,
124
+ BAContainerCannotContainRemotePayload = 8014,
125
} // last available is 8499. 8500 is BurnBackendWarnings.
126
}
127
}
src/wix/WixToolset.Core/Compile/CompilerPayload.cs
+8
-5
@@ -247,13 +247,12 @@ namespace WixToolset.Core
247
{
248
this.CalculateAndVerifyFields();
249
this.GenerateIdFromFilename();
250
- this.GenerateIdFromPrefix("ppy");
250
}
251
253
- public void FinishCompilingPayload()
252
+ public void FinishCompilingPayload(string parentId)
253
{
254
this.CalculateAndVerifyFields();
256
- this.GenerateIdFromPrefix("pay");
255
+ this.GenerateIdFromPrefix("pay", parentId);
256
}
257
258
private void GenerateIdFromFilename()
@@ -268,14 +267,18 @@ namespace WixToolset.Core
267
{
268
this.Id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(this.SourceFile));
269
}
270
+ else // if Name and SourceFile were not specified an error was already reported.
271
+ {
272
+ this.Id = Identifier.Invalid;
273
+ }
274
}
275
}
276
274
- private void GenerateIdFromPrefix(string prefix)
277
+ private void GenerateIdFromPrefix(string prefix, string parentId)
278
{
279
if (this.Id == null)
280
{
278
- this.Id = this.Core.CreateIdentifier(prefix, this.SourceFile?.ToUpperInvariant() ?? String.Empty);
281
+ this.Id = this.Core.CreateIdentifier(prefix, parentId, this.SourceFile?.ToUpperInvariant() ?? this.Name?.ToUpperInvariant());
282
}
283
}
284
src/wix/WixToolset.Core/Compiler_Bundle.cs
+26
-10
@@ -698,7 +698,7 @@ namespace WixToolset.Core
698
this.ParseBootstrapperApplicationDllElement(child, id);
699
break;
700
case "Payload":
701
- this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
701
+ this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, isRemoteAllowed: false);
702
break;
703
case "PayloadGroupRef":
704
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
@@ -788,7 +788,7 @@ namespace WixToolset.Core
788
}
789
}
790
791
- compilerPayload.FinishCompilingPayload();
791
+ compilerPayload.FinishCompilingPayload(Compiler.BurnUXContainerId.Id);
792
793
// Now that the Id is known, we can parse the extension attributes.
794
var context = new Dictionary<string, string>
@@ -872,7 +872,7 @@ namespace WixToolset.Core
872
switch (child.Name.LocalName)
873
{
874
case "Payload":
875
- this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
875
+ this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, isRemoteAllowed: false);
876
break;
877
case "PayloadGroupRef":
878
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
@@ -1237,7 +1237,7 @@ namespace WixToolset.Core
1237
}
1238
}
1239
1240
- compilerPayload.FinishCompilingPayload();
1240
+ compilerPayload.FinishCompilingPayload(Compiler.BurnUXContainerId.Id);
1241
1242
// Now that the Id is known, we can parse the extension attributes.
1243
var context = new Dictionary<string, string>
@@ -1259,7 +1259,7 @@ namespace WixToolset.Core
1259
switch (child.Name.LocalName)
1260
{
1261
case "Payload":
1262
- this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
1262
+ this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, isRemoteAllowed: false);
1263
break;
1264
case "PayloadGroupRef":
1265
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
@@ -1393,12 +1393,16 @@ namespace WixToolset.Core
1393
/// <param name="node">Element to parse</param>
1394
/// <param name="parentType">ComplexReferenceParentType of parent element. (BA or PayloadGroup)</param>
1395
/// <param name="parentId">Identifier of parent element.</param>
1396
- private Identifier ParsePayloadElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId)
1396
+ /// <param name="isRemoteAllowed">Indicates if the Payload element can be remote or not.</param>
1397
+ private Identifier ParsePayloadElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId, bool isRemoteAllowed)
1398
{
1399
Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
1400
1401
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1401
- var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node);
1402
+ var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
1403
+ {
1404
+ IsRemoteAllowed = isRemoteAllowed
1405
+ };
1406
1407
// This list lets us evaluate extension attributes *after* all core attributes
1408
// have been parsed and dealt with, regardless of authoring order.
@@ -1414,12 +1418,24 @@ namespace WixToolset.Core
1418
case "Id":
1419
compilerPayload.ParseId(attrib);
1420
break;
1421
+ case "CertificatePublicKey":
1422
+ compilerPayload.ParseCertificatePublicKey(attrib);
1423
+ break;
1424
+ case "CertificateThumbprint":
1425
+ compilerPayload.ParseCertificateThumbprint(attrib);
1426
+ break;
1427
case "Compressed":
1428
compilerPayload.ParseCompressed(attrib);
1429
break;
1430
+ case "Hash":
1431
+ compilerPayload.ParseHash(attrib);
1432
+ break;
1433
case "Name":
1434
compilerPayload.ParseName(attrib);
1435
break;
1436
+ case "Size":
1437
+ compilerPayload.ParseSize(attrib);
1438
+ break;
1439
case "SourceFile":
1440
compilerPayload.ParseSourceFile(attrib);
1441
break;
@@ -1442,7 +1458,7 @@ namespace WixToolset.Core
1458
}
1459
}
1460
1445
- compilerPayload.FinishCompilingPayload();
1461
+ compilerPayload.FinishCompilingPayload(parentId?.Id);
1462
1463
// Now that the PayloadId is known, we can parse the extension attributes.
1464
var context = new Dictionary<string, string>
@@ -1539,7 +1555,7 @@ namespace WixToolset.Core
1555
packageType = WixBundlePackageType.Msu;
1556
break;
1557
case "Payload":
1542
- this.ParsePayloadElement(child, ComplexReferenceParentType.PayloadGroup, id);
1558
+ this.ParsePayloadElement(child, ComplexReferenceParentType.PayloadGroup, id, isRemoteAllowed: true);
1559
break;
1560
case "PayloadGroupRef":
1561
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.PayloadGroup, id);
@@ -2263,7 +2279,7 @@ namespace WixToolset.Core
2279
}
2280
break;
2281
case "Payload":
2266
- this.ParsePayloadElement(child, ComplexReferenceParentType.Package, id);
2282
+ this.ParsePayloadElement(child, ComplexReferenceParentType.Package, id, isRemoteAllowed: true);
2283
break;
2284
case "PayloadGroupRef":
2285
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id);
src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
+2
-2
@@ -103,7 +103,7 @@ namespace WixToolsetTest.CoreIntegration
103
"<Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' />" +
104
"<RelatedBundle Id='{B94478B1-E1F3-4700-9CE8-6AA090854AEC}' Action='Upgrade' />" +
105
"<PayloadRef Id='chain.exe' />" +
106
- "<PayloadRef Id='payP6wZpeHEAZbDUQPEKeCpQ_9bN.4' />" +
106
+ "<PayloadRef Id='paydfjdGCAZtFxTeTjs0nIscHI86SY' />" +
107
"</BundlePackage>",
108
}, bundlePackages);
109
@@ -175,7 +175,7 @@ namespace WixToolsetTest.CoreIntegration
175
var payloads = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Payload", ignoreAttributesByElementName);
176
WixAssert.CompareLineByLine(new[]
177
{
178
- "<Payload Id='payP6wZpeHEAZbDUQPEKeCpQ_9bN.4' FilePath='signed_cab1.cab' FileSize='*' Hash='*' Packaging='external' SourcePath='signed_cab1.cab' />",
178
+ "<Payload Id='paydfjdGCAZtFxTeTjs0nIscHI86SY' FilePath='signed_cab1.cab' FileSize='*' Hash='*' Packaging='external' SourcePath='signed_cab1.cab' />",
179
"<Payload Id='chain.exe' FilePath='chain.exe' FileSize='*' Hash='*' Packaging='external' SourcePath='chain.exe' />",
180
}, payloads);
181
src/wix/test/WixToolsetTest.CoreIntegration/BurnRemotePayloadSubcommandFixture.cs
+1
-3
@@ -398,7 +398,7 @@ namespace WixToolsetTest.CoreIntegration
398
}
399
}
400
401
- [Fact(Skip = "Blocked by https://github.com/wixtoolset/issues/issues/5601 - Support RemotePayload for Payload elements")]
401
+ [Fact]
402
public void CanGetRemotePayloadWithCertificate()
403
{
404
var folder = TestData.Get(@"TestData");
@@ -441,9 +441,7 @@ namespace WixToolsetTest.CoreIntegration
441
var remotePayloadSourceText = "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>" +
442
" <Fragment>" +
443
" <PackageGroup Id='BundlePackages'>" +
444
- " <ExePackage CacheId='xyz'>" +
444
String.Join(Environment.NewLine, elements) +
446
- " </ExePackage>" +
445
" </PackageGroup>" +
446
" </Fragment>" +
447
"</Wix>";
src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
+81
@@ -201,5 +201,86 @@ namespace WixToolsetTest.CoreIntegration
201
}, containers);
202
}
203
}
204
+
205
+ [Fact]
206
+ public void CanBuildBundleWithRemotePackagePaylod()
207
+ {
208
+ var folder = TestData.Get(@"TestData");
209
+
210
+ using (var fs = new DisposableFileSystem())
211
+ {
212
+ var baseFolder = fs.GetFolder();
213
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
214
+ var bundlePath = Path.Combine(baseFolder, @"bin\test.exe");
215
+ var baFolderPath = Path.Combine(baseFolder, "ba");
216
+ var extractFolderPath = Path.Combine(baseFolder, "extract");
217
+
218
+ var result = WixRunner.Execute(false, new[]
219
+ {
220
+ "build",
221
+ Path.Combine(folder, "Payload", "RemotePayloadInPackage.wxs"),
222
+ Path.Combine(folder, "BundleWithPackageGroupRef", "Bundle.wxs"),
223
+ "-bindpath", Path.Combine(folder, ".Data"),
224
+ "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
225
+ "-intermediateFolder", intermediateFolder,
226
+ "-o", bundlePath
227
+ });
228
+
229
+ result.AssertSuccess();
230
+
231
+ Assert.True(File.Exists(bundlePath));
232
+
233
+ var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
234
+ extractResult.AssertSuccess();
235
+
236
+ var payloadElements = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Payload");
237
+ WixAssert.CompareLineByLine(new[]
238
+ {
239
+ "<Payload Id='payU3zeF5QWZvsWVEfgPVoFiuo65qQ' FilePath='reallybig.dat' FileSize='100000000' Hash='4312abcef' DownloadUrl='example.com/reallybig.dat' Packaging='external' SourcePath='reallybig.dat' />",
240
+ "<Payload Id='burn.exe' FilePath='burn.exe' FileSize='463360' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />",
241
+ "<Payload Id='payIswsNTCI1qS7UYl_lydLSgHt2Aw' FilePath='fake.txt' FileSize='1' Hash='bcadef' DownloadUrl='example.com/fake.txt' Packaging='external' SourcePath='fake.txt' />",
242
+ "<Payload Id='RemotePayloadExe' FilePath='fake.exe' FileSize='1' Hash='a' DownloadUrl='example.com' Packaging='external' SourcePath='fake.exe' />",
243
+ }, payloadElements);
244
+
245
+ var payloadRefElements = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Chain/burn:ExePackage/burn:PayloadRef");
246
+ WixAssert.CompareLineByLine(new[]
247
+ {
248
+ "<PayloadRef Id='burn.exe' />",
249
+ "<PayloadRef Id='payU3zeF5QWZvsWVEfgPVoFiuo65qQ' />",
250
+ "<PayloadRef Id='RemotePayloadExe' />",
251
+ "<PayloadRef Id='payIswsNTCI1qS7UYl_lydLSgHt2Aw' />"
252
+ }, payloadRefElements);
253
+ }
254
+ }
255
+
256
+ [Fact]
257
+ public void CannotBuildRemotePayloadInBootstrapperApplication()
258
+ {
259
+ var folder = TestData.Get(@"TestData");
260
+
261
+ using (var fs = new DisposableFileSystem())
262
+ {
263
+ var baseFolder = fs.GetFolder();
264
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
265
+ var bundlePath = Path.Combine(baseFolder, "bin", "test.exe");
266
+
267
+ var result = WixRunner.Execute(false, new[]
268
+ {
269
+ "build",
270
+ Path.Combine(folder, "Payload", "RemotePayloadInBootstrapperApplication.wxs"),
271
+ Path.Combine(folder, "SimpleBundle", "MultiFileBootstrapperApplication.wxs"),
272
+ "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
273
+ "-bindpath", Path.Combine(folder, ".Data"),
274
+ "-intermediateFolder", intermediateFolder,
275
+ "-o", bundlePath,
276
+ });
277
+
278
+ WixAssert.CompareLineByLine(new[]
279
+ {
280
+ "The Payload 'RemotePayload' is being added to Container 'WixUXContainer', overriding its Compressed value of 'no'.",
281
+ "Bootstrapper application and bundle extension payloads must be embedded in the bundle. The payload 'someremotefile.txt' is remote thus cannot be found for embedding. Provide a full path to the payload via the Payload/@SourceFile attribute."
282
+ }, result.Messages.Select(m => m.ToString()).ToArray());
283
+ }
284
+ }
285
}
286
}
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Payload/RemotePayloadInBootstrapperApplication.wxs
new
+17
@@ -0,0 +1,17 @@
1
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2
+ <Bundle Name="RemotePayloadInBootstrapperApplication" Version="1.0.0.0" Manufacturer="test" UpgradeCode="{B04C20B8-70C3-4DE1-8D91-4F11C7C68DED}">
3
+ <BootstrapperApplicationRef Id="fakeba">
4
+ <PayloadGroupRef Id="RemotePayload" />
5
+ </BootstrapperApplicationRef>
6
+
7
+ <Chain>
8
+ <MsiPackage SourceFile="test.msi" DownloadUrl="http://example.com/{0}id/{1}/{2}" Compressed="no" />
9
+ </Chain>
10
+ </Bundle>
11
+
12
+ <Fragment>
13
+ <PayloadGroup Id="RemotePayload">
14
+ <Payload Id="RemotePayload" Name="someremotefile.txt" DownloadUrl="http://example.com/{0}id/{1}/{2}" Hash="abc" Size="123" />
15
+ </PayloadGroup>
16
+ </Fragment>
17
+</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Payload/RemotePayloadInPackage.wxs
new
+17
@@ -0,0 +1,17 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3
+ <Fragment>
4
+ <PackageGroup Id="BundlePackages">
5
+ <ExePackage DetectCondition="ForTestPurposesOnly" SourceFile="burn.exe" Permanent="yes">
6
+ <Payload Hash="4312abcef" Size="100000000" Name="reallybig.dat" DownloadUrl="example.com/reallybig.dat" />
7
+ </ExePackage>
8
+
9
+ <ExePackage Id="RemotePayloadExe" DetectCondition="ForTestPurposesOnly" Description="Override RemotePayload description"
10
+ DisplayName="Override RemotePayload display name" Permanent="yes">
11
+ <ExePackagePayload Description="RemotePayload description" Hash="a" ProductName="RemotePayload product name"
12
+ Size="1" Version="1.0.0.0" Name="fake.exe" DownloadUrl="example.com" />
13
+ <Payload Hash="bcadef" Size="1" Name="fake.txt" DownloadUrl="example.com/fake.txt" />
14
+ </ExePackage>
15
+ </PackageGroup>
16
+ </Fragment>
17
+</Wix>