@joebigelow / wix-1 / commits / 933d4fc3

Support "inscribing" Burn bundles

Rob Mensching committed Jan 10, 2022 at 14:19 UTC 933d4fc340f989239b77bfef4212f80d0a4a65f2
17 files changed +639 -54
src/api/wix/WixToolset.Data/WarningMessages.cs
+1 -1
@@ -279,7 +279,7 @@ namespace WixToolset.Data
279
280 public static Message ExternalCabsAreNotSigned(string databaseFile)
281 {
282 - return Message(null, Ids.ExternalCabsAreNotSigned, "The installer database '{0}' has external cabs, but at least one of them is not signed. Please ensure that all external cabs are signed, if you mean to sign them. If you don't mean to sign them, there is no need to run the insignia tool as part of your build.", databaseFile);
282 + return Message(null, Ids.ExternalCabsAreNotSigned, "The installer database '{0}' has external cabs, but at least one of them is not signed. Please ensure that all external cabs are signed, if you mean to sign them. If you don't mean to sign them, there is no need to inscribe the MSI as part of your build.", databaseFile);
283 }
284
285 public static Message FailedToDeleteTempDir(string directory)
src/wix/WixToolset.BuildTasks/DetachBundleEngineForSigning.cs new
+64
@@ -0,0 +1,64 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.BuildTasks
4 +{
5 + using Microsoft.Build.Framework;
6 +
7 + /// <summary>
8 + /// An MSBuild task to run WiX to detach bundle engine to be signed.
9 + /// </summary>
10 + public sealed partial class DetachBundleEngineForSigning : WixExeBaseTask
11 + {
12 + /// <summary>
13 + /// The bundle from which to detach the bundle engine.
14 + /// </summary>
15 + [Required]
16 + public ITaskItem BundleFile { get; set; }
17 +
18 + /// <summary>
19 + /// Gets or sets the intermedidate folder to use.
20 + /// </summary>
21 + public ITaskItem IntermediateDirectory { get; set; }
22 +
23 + /// <summary>
24 + /// Gets or sets the path to the output detached bundle.
25 + /// </summary>
26 + [Required]
27 + public ITaskItem OutputFile { get; set; }
28 +
29 + /// <summary>
30 + /// Gets or sets the output. Only set if the task does work.
31 + /// </summary>
32 + [Output]
33 + public ITaskItem Output { get; set; }
34 +
35 + protected override void BuildCommandLine(WixCommandLineBuilder commandLineBuilder)
36 + {
37 + commandLineBuilder.AppendTextUnquoted("burn detach");
38 +
39 + commandLineBuilder.AppendFileNameIfNotNull(this.BundleFile);
40 + commandLineBuilder.AppendSwitchIfNotNull("-engine ", this.OutputFile);
41 + commandLineBuilder.AppendSwitchIfNotNull("-intermediatefolder ", this.IntermediateDirectory);
42 +
43 + base.BuildCommandLine(commandLineBuilder);
44 +
45 + commandLineBuilder.AppendTextIfNotWhitespace(this.AdditionalOptions);
46 + }
47 +
48 + protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
49 + {
50 + var exitCode = base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands);
51 +
52 + if (exitCode == 0) // successfully did work.
53 + {
54 + this.Output = this.OutputFile;
55 + }
56 + else if (exitCode == -1000) // no work done.
57 + {
58 + exitCode = 0;
59 + }
60 +
61 + return exitCode;
62 + }
63 + }
64 +}
src/wix/WixToolset.BuildTasks/ReattachSignedBundleEngine.cs new
+71
@@ -0,0 +1,71 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.BuildTasks
4 +{
5 + using Microsoft.Build.Framework;
6 +
7 + /// <summary>
8 + /// An MSBuild task to run WiX to reattach a (signed) bundle engine to its bundle.
9 + /// </summary>
10 + public sealed partial class ReattachSignedBundleEngine : WixExeBaseTask
11 + {
12 + /// <summary>
13 + /// The bundle to which to attach the bundle engine.
14 + /// </summary>
15 + [Required]
16 + public ITaskItem BundleFile { get; set; }
17 +
18 + /// <summary>
19 + /// The bundle engine file to reattach to the bundle.
20 + /// </summary>
21 + [Required]
22 + public ITaskItem BundleEngineFile { get; set; }
23 +
24 + /// <summary>
25 + /// Gets or sets the intermedidate folder to use.
26 + /// </summary>
27 + public ITaskItem IntermediateDirectory { get; set; }
28 +
29 + /// <summary>
30 + /// Gets or sets the path to the output detached bundle.
31 + /// </summary>
32 + [Required]
33 + public ITaskItem OutputFile { get; set; }
34 +
35 + /// <summary>
36 + /// Gets or sets the output. Only set if the task does work.
37 + /// </summary>
38 + [Output]
39 + public ITaskItem Output { get; set; }
40 +
41 + protected override void BuildCommandLine(WixCommandLineBuilder commandLineBuilder)
42 + {
43 + commandLineBuilder.AppendTextUnquoted("burn reattach");
44 +
45 + commandLineBuilder.AppendFileNameIfNotNull(this.BundleFile);
46 + commandLineBuilder.AppendSwitchIfNotNull("-engine ", this.BundleEngineFile);
47 + commandLineBuilder.AppendSwitchIfNotNull("-out ", this.OutputFile);
48 + commandLineBuilder.AppendSwitchIfNotNull("-intermediatefolder ", this.IntermediateDirectory);
49 +
50 + base.BuildCommandLine(commandLineBuilder);
51 +
52 + commandLineBuilder.AppendTextIfNotWhitespace(this.AdditionalOptions);
53 + }
54 +
55 + protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
56 + {
57 + var exitCode = base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands);
58 +
59 + if (exitCode == 0) // successfully did work.
60 + {
61 + this.Output = this.OutputFile;
62 + }
63 + else if (exitCode == -1000) // no work done.
64 + {
65 + exitCode = 0;
66 + }
67 +
68 + return exitCode;
69 + }
70 + }
71 +}
src/wix/WixToolset.BuildTasks/ToolsetTask_InProc.cs
+1 -1
@@ -15,7 +15,7 @@ namespace WixToolset.BuildTasks
15
16 public partial class ToolsetTask
17 {
18 - protected sealed override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
18 + protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
19 {
20 if (this.RunAsSeparateProcess)
21 {
src/wix/WixToolset.Core.Burn/BundleBackend.cs
+1 -10
@@ -47,16 +47,7 @@ namespace WixToolset.Core.Burn
47
48 public bool Inscribe(IInscribeContext context)
49 {
50 - if (String.IsNullOrEmpty(context.SignedEngineFile))
51 - {
52 - var command = new InscribeBundleCommand(context);
53 - return command.Execute();
54 - }
55 - else
56 - {
57 - var command = new InscribeBundleEngineCommand(context);
58 - return command.Execute();
59 - }
50 + return false;
51 }
52
53 public Intermediate Unbind(IUnbindContext context)
src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
-5
@@ -45,11 +45,6 @@ namespace WixToolset.Core.Burn.Bundles
45 /// </summary>
46 public Stream Stream => this.binaryReader?.BaseStream;
47
48 - internal static BurnReader Open(object inputFilePath)
49 - {
50 - throw new NotImplementedException();
51 - }
52 -
48 /// <summary>
49 /// Opens a Burn reader.
50 /// </summary>
src/wix/WixToolset.Core.Burn/BurnCommand.cs new
+78
@@ -0,0 +1,78 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn
4 +{
5 + using System;
6 + using System.Threading;
7 + using System.Threading.Tasks;
8 + using WixToolset.Extensibility.Data;
9 + using WixToolset.Extensibility.Services;
10 +
11 + /// <summary>
12 + /// Burn specialized command.
13 + /// </summary>
14 + internal class BurnCommand : ICommandLineCommand
15 + {
16 + public BurnCommand(IServiceProvider serviceProvider)
17 + {
18 + this.ServiceProvider = serviceProvider;
19 + }
20 +
21 + public bool ShowHelp { get; set; }
22 +
23 + public bool ShowLogo { get; set; }
24 +
25 + public bool StopParsing { get; set; }
26 +
27 + private IServiceProvider ServiceProvider { get; }
28 +
29 + private BurnSubcommandBase Subcommand { get; set; }
30 +
31 + public Task<int> ExecuteAsync(CancellationToken cancellationToken)
32 + {
33 + if (this.ShowHelp || this.Subcommand is null)
34 + {
35 + DisplayHelp();
36 + return Task.FromResult(1);
37 + }
38 +
39 + return this.Subcommand.ExecuteAsync(cancellationToken);
40 + }
41 +
42 + public bool TryParseArgument(ICommandLineParser parser, string argument)
43 + {
44 + if (this.Subcommand is null)
45 + {
46 + switch (argument.ToLowerInvariant())
47 + {
48 + case "detach":
49 + this.Subcommand = new DetachSubcommand(this.ServiceProvider);
50 + return true;
51 +
52 + case "reattach":
53 + this.Subcommand = new ReattachSubcommand(this.ServiceProvider);
54 + return true;
55 + }
56 +
57 + return false;
58 + }
59 +
60 + return this.Subcommand.TryParseArgument(parser, argument);
61 + }
62 +
63 + private static void DisplayHelp()
64 + {
65 + Console.WriteLine();
66 + Console.WriteLine("Usage: wix burn detach|reattach bundle.exe -out engine.exe");
67 + Console.WriteLine();
68 + Console.WriteLine("Options:");
69 + Console.WriteLine(" -h|--help Show command line help.");
70 + Console.WriteLine(" --nologo Suppress displaying the logo information.");
71 + Console.WriteLine();
72 + Console.WriteLine("Commands:");
73 + Console.WriteLine();
74 + Console.WriteLine(" detach Detaches the burn engine from a bundle so it can be signed.");
75 + Console.WriteLine(" reattach Reattaches a signed burn engine to a bundle.");
76 + }
77 + }
78 +}
src/wix/WixToolset.Core.Burn/BurnExtensionCommandLine.cs new
+41
@@ -0,0 +1,41 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using WixToolset.Extensibility;
8 + using WixToolset.Extensibility.Data;
9 + using WixToolset.Extensibility.Services;
10 +
11 + /// <summary>
12 + /// Parses the "msi" command-line command. See <c>WindowsInstallerCommand</c>
13 + /// for the bulk of the command-line processing.
14 + /// </summary>
15 + internal class BurnExtensionCommandLine : BaseExtensionCommandLine
16 + {
17 + public BurnExtensionCommandLine(IServiceProvider serviceProvider)
18 + {
19 + this.ServiceProvider = serviceProvider;
20 + }
21 +
22 + private IServiceProvider ServiceProvider { get; }
23 +
24 + public override IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[]
25 + {
26 + new ExtensionCommandLineSwitch { Switch = "burn", Description = "Burn specialized operations." },
27 + };
28 +
29 + public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command)
30 + {
31 + command = null;
32 +
33 + if ("burn".Equals(argument, StringComparison.OrdinalIgnoreCase))
34 + {
35 + command = new BurnCommand(this.ServiceProvider);
36 + }
37 +
38 + return command != null;
39 + }
40 + }
41 +}
src/wix/WixToolset.Core.Burn/BurnExtensionFactory.cs
+12 -1
@@ -1,4 +1,4 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.Burn
4 {
@@ -7,10 +7,21 @@ namespace WixToolset.Core.Burn
7
8 internal class BurnExtensionFactory : IExtensionFactory
9 {
10 + public BurnExtensionFactory(IServiceProvider serviceProvider)
11 + {
12 + this.ServiceProvider = serviceProvider;
13 + }
14 +
15 + private IServiceProvider ServiceProvider { get; }
16 +
17 public bool TryCreateExtension(Type extensionType, out object extension)
18 {
19 extension = null;
20
21 + if (extensionType == typeof(IExtensionCommandLine))
22 + {
23 + extension = new BurnExtensionCommandLine(this.ServiceProvider);
24 + }
25 if (extensionType == typeof(IBackendFactory))
26 {
27 extension = new BurnBackendFactory();
src/wix/WixToolset.Core.Burn/BurnSubcommandBase.cs new
+15
@@ -0,0 +1,15 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn
4 +{
5 + using System.Threading;
6 + using System.Threading.Tasks;
7 + using WixToolset.Extensibility.Services;
8 +
9 + internal abstract class BurnSubcommandBase
10 + {
11 + public abstract bool TryParseArgument(ICommandLineParser parser, string argument);
12 +
13 + public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken);
14 + }
15 +}
src/wix/WixToolset.Core.Burn/DetachSubcommand.cs new
+80
@@ -0,0 +1,80 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn
4 +{
5 + using System;
6 + using System.IO;
7 + using System.Threading;
8 + using System.Threading.Tasks;
9 + using WixToolset.Core.Burn.Inscribe;
10 + using WixToolset.Extensibility.Services;
11 +
12 + internal class DetachSubcommand : BurnSubcommandBase
13 + {
14 + public DetachSubcommand(IServiceProvider serviceProvider)
15 + {
16 + this.ServiceProvider = serviceProvider;
17 + this.Messaging = serviceProvider.GetService<IMessaging>();
18 + }
19 +
20 + private IServiceProvider ServiceProvider { get; }
21 +
22 + private IMessaging Messaging { get; }
23 +
24 + private string InputPath { get; set; }
25 +
26 + private string IntermediateFolder { get; set; }
27 +
28 + private string EngineOutputPath { get; set; }
29 +
30 + public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
31 + {
32 + if (String.IsNullOrEmpty(this.InputPath))
33 + {
34 + Console.Error.WriteLine("Path to input bundle is required");
35 + return Task.FromResult(-1);
36 + }
37 +
38 + if (String.IsNullOrEmpty(this.EngineOutputPath))
39 + {
40 + Console.Error.WriteLine("Path to output the bundle engine is required");
41 + return Task.FromResult(-1);
42 + }
43 +
44 + if (String.IsNullOrEmpty(this.IntermediateFolder))
45 + {
46 + this.IntermediateFolder = Path.GetTempPath();
47 + }
48 +
49 + var command = new InscribeBundleEngineCommand(this.ServiceProvider, this.InputPath, this.EngineOutputPath, this.IntermediateFolder);
50 + command.Execute();
51 +
52 + return Task.FromResult(this.Messaging.LastErrorNumber);
53 + }
54 +
55 + public override bool TryParseArgument(ICommandLineParser parser, string argument)
56 + {
57 + if (parser.IsSwitch(argument))
58 + {
59 + var parameter = argument.Substring(1);
60 + switch (parameter.ToLowerInvariant())
61 + {
62 + case "intermediatefolder":
63 + this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument);
64 + return true;
65 +
66 + case "engine":
67 + this.EngineOutputPath = parser.GetNextArgumentAsFilePathOrError(argument);
68 + return true;
69 + }
70 + }
71 + else if (String.IsNullOrEmpty(this.InputPath))
72 + {
73 + this.InputPath = argument;
74 + return true;
75 + }
76 +
77 + return false;
78 + }
79 + }
80 +}
src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+23 -14
@@ -2,42 +2,51 @@
2
3 namespace WixToolset.Core.Burn.Inscribe
4 {
5 + using System;
6 using System.IO;
7 using WixToolset.Core.Burn.Bundles;
8 using WixToolset.Core.Native;
8 - using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services;
10
11 internal class InscribeBundleCommand
12 {
13 - public InscribeBundleCommand(IInscribeContext context)
13 + public InscribeBundleCommand(IServiceProvider serviceProvider, string inputPath, string signedEngineFile, string outputPath, string intermediateFolder)
14 {
15 - this.Context = context;
16 -
17 - this.Messaging = context.ServiceProvider.GetService<IMessaging>();
15 + this.Messaging = serviceProvider.GetService<IMessaging>();
16 + this.IntermediateFolder = intermediateFolder;
17 + this.InputFilePath = inputPath;
18 + this.SignedEngineFile = signedEngineFile;
19 + this.OutputFile = outputPath;
20 }
19 -
20 - private IInscribeContext Context { get; }
21
22 - public IMessaging Messaging { get; }
22 + private IMessaging Messaging { get; }
23 +
24 + private string IntermediateFolder { get; }
25 +
26 + private string InputFilePath { get; }
27 +
28 + private string SignedEngineFile { get; }
29 +
30 + private string OutputFile { get; }
31
32 public bool Execute()
33 {
34 var inscribed = false;
27 - var tempFile = Path.Combine(this.Context.IntermediateFolder, "bundle_engine_signed.exe");
35 + var tempFile = Path.Combine(this.IntermediateFolder, "~bundle_engine_signed.exe");
36
29 - using (var reader = BurnReader.Open(this.Context.InputFilePath))
37 + using (var reader = BurnReader.Open(this.Messaging, this.InputFilePath))
38 {
31 - FileSystem.CopyFile(this.Context.SignedEngineFile, tempFile, allowHardlink: false);
32 - using (BurnWriter writer = BurnWriter.Open(this.Messaging, tempFile))
39 + FileSystem.CopyFile(this.SignedEngineFile, tempFile, allowHardlink: false);
40 +
41 + using (var writer = BurnWriter.Open(this.Messaging, tempFile))
42 {
43 inscribed = writer.ReattachContainers(reader);
44 }
45 }
46
38 - Directory.CreateDirectory(Path.GetDirectoryName(this.Context.OutputFile));
47 + Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile));
48
40 - FileSystem.MoveFile(tempFile, this.Context.OutputFile);
49 + FileSystem.MoveFile(tempFile, this.OutputFile);
50
51 return inscribed;
52 }
src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
+10 -9
@@ -6,28 +6,31 @@ namespace WixToolset.Core.Burn.Inscribe
6 using System.IO;
7 using WixToolset.Core.Burn.Bundles;
8 using WixToolset.Core.Native;
9 - using WixToolset.Extensibility.Data;
9 + using WixToolset.Extensibility.Services;
10
11 internal class InscribeBundleEngineCommand
12 {
13 - public InscribeBundleEngineCommand(IInscribeContext context)
13 + public InscribeBundleEngineCommand(IServiceProvider serviceProvider, string inputPath, string outputPath, string intermediateFolder)
14 {
15 - this.IntermediateFolder = context.IntermediateFolder;
16 - this.InputFilePath = context.InputFilePath;
17 - this.OutputFile = context.OutputFile;
15 + this.Messaging = serviceProvider.GetService<IMessaging>();
16 + this.IntermediateFolder = intermediateFolder;
17 + this.InputFilePath = inputPath;
18 + this.OutputFile = outputPath;
19 }
20
21 + private IMessaging Messaging { get; }
22 +
23 private string IntermediateFolder { get; }
24
25 private string InputFilePath { get; }
26
27 private string OutputFile { get; }
28
26 - public bool Execute()
29 + public void Execute()
30 {
31 var tempFile = Path.Combine(this.IntermediateFolder, "bundle_engine_unsigned.exe");
32
30 - using (var reader = BurnReader.Open(this.InputFilePath))
33 + using (var reader = BurnReader.Open(this.Messaging, this.InputFilePath))
34 using (var writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
35 {
36 reader.Stream.Seek(0, SeekOrigin.Begin);
@@ -56,8 +59,6 @@ namespace WixToolset.Core.Burn.Inscribe
59 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile));
60
61 FileSystem.MoveFile(tempFile, this.OutputFile);
59 -
60 - return true;
62 }
63 }
64 }
src/wix/WixToolset.Core.Burn/ReattachSubcommand.cs new
+101
@@ -0,0 +1,101 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn
4 +{
5 + using System;
6 + using System.IO;
7 + using System.Threading;
8 + using System.Threading.Tasks;
9 + using WixToolset.Core.Burn.Inscribe;
10 + using WixToolset.Extensibility.Services;
11 +
12 + internal class ReattachSubcommand : BurnSubcommandBase
13 + {
14 + public ReattachSubcommand(IServiceProvider serviceProvider)
15 + {
16 + this.ServiceProvider = serviceProvider;
17 + this.Messaging = serviceProvider.GetService<IMessaging>();
18 + }
19 +
20 + private IServiceProvider ServiceProvider { get; }
21 +
22 + private IMessaging Messaging { get; }
23 +
24 + private string InputPath { get; set; }
25 +
26 + private string SignedEnginePath { get; set; }
27 +
28 + private string IntermediateFolder { get; set; }
29 +
30 + private string OutputPath { get; set; }
31 +
32 + public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
33 + {
34 + if (String.IsNullOrEmpty(this.InputPath))
35 + {
36 + Console.Error.WriteLine("Path to input bundle is required");
37 + return Task.FromResult(-1);
38 + }
39 +
40 + if (String.IsNullOrEmpty(this.SignedEnginePath))
41 + {
42 + Console.Error.WriteLine("Path to detached signed bundle engine is required");
43 + return Task.FromResult(-1);
44 + }
45 +
46 + if (String.IsNullOrEmpty(this.IntermediateFolder))
47 + {
48 + this.IntermediateFolder = Path.GetTempPath();
49 + }
50 +
51 + if (String.IsNullOrEmpty(this.OutputPath))
52 + {
53 + this.OutputPath = this.InputPath;
54 + }
55 +
56 + var command = new InscribeBundleCommand(this.ServiceProvider, this.InputPath, this.SignedEnginePath, this.OutputPath, this.IntermediateFolder);
57 + var didWork = command.Execute();
58 +
59 + // If the detach subcommand did not encounter an error but did no work
60 + // then return the special exit code that indicates no work was done (-1000).
61 + var exitCode = this.Messaging.LastErrorNumber;
62 +
63 + if (!didWork && exitCode == 0)
64 + {
65 + exitCode = -1000;
66 + }
67 +
68 + return Task.FromResult(exitCode);
69 + }
70 +
71 + public override bool TryParseArgument(ICommandLineParser parser, string argument)
72 + {
73 + if (parser.IsSwitch(argument))
74 + {
75 + var parameter = argument.Substring(1);
76 + switch (parameter.ToLowerInvariant())
77 + {
78 + case "engine":
79 + this.SignedEnginePath = parser.GetNextArgumentAsFilePathOrError(argument);
80 + return true;
81 +
82 + case "intermediatefolder":
83 + this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument);
84 + return true;
85 +
86 + case "o":
87 + case "out":
88 + this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument);
89 + return true;
90 + }
91 + }
92 + else if (String.IsNullOrEmpty(this.InputPath))
93 + {
94 + this.InputPath = argument;
95 + return true;
96 + }
97 +
98 + return false;
99 + }
100 + }
101 +}
src/wix/WixToolset.Sdk/tools/wix.signing.targets
+29 -13
@@ -20,6 +20,14 @@
20 <UsingTask TaskName="InscribeMsiWithCabinetSignatures" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath)" Architecture="x86" />
21 <UsingTask TaskName="InscribeMsiWithCabinetSignatures" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath64)" Architecture="x64" />
22
23 + <UsingTask TaskName="DetachBundleEngineForSigning" Condition=" '$(WixTasksPath64)' == '' " AssemblyFile="$(WixTasksPath)" />
24 + <UsingTask TaskName="DetachBundleEngineForSigning" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath)" Architecture="x86" />
25 + <UsingTask TaskName="DetachBundleEngineForSigning" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath64)" Architecture="x64" />
26 +
27 + <UsingTask TaskName="ReattachSignedBundleEngine" Condition=" '$(WixTasksPath64)' == '' " AssemblyFile="$(WixTasksPath)" />
28 + <UsingTask TaskName="ReattachSignedBundleEngine" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath)" Architecture="x86" />
29 + <UsingTask TaskName="ReattachSignedBundleEngine" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath64)" Architecture="x64" />
30 +
31 <!-- Default Inscribe properties. -->
32 <PropertyGroup>
33 <InscribeNoLogo Condition=" '$(InscribeNoLogo)' == '' ">$(NoLogo)</InscribeNoLogo>
@@ -230,22 +238,26 @@
238 Inputs="@(SignTargetPath)"
239 Outputs="$(SignedFilePath)">
240
233 - <Insignia
241 + <DetachBundleEngineForSigning
242 BundleFile="@(SignTargetPath)"
243 OutputFile="$(IntermediateOutputPath)%(SignTargetPath.Filename)%(SignTargetPath.Extension)"
236 - ToolPath="$(WixToolPath)"
244 + IntermediateDirectory="%(SignTargetPath.RootDir)%(SignTargetPath.Directory)"
245 +
246 NoLogo="$(InscribeNoLogo)"
238 - RunAsSeparateProcess="$(RunWixToolsOutOfProc)"
247 SuppressAllWarnings="$(InscribeSuppressAllWarnings)"
248 SuppressSpecificWarnings="$(InscribeSuppressSpecificWarnings)"
249 TreatWarningsAsErrors="$(InscribeTreatWarningsAsErrors)"
250 TreatSpecificWarningsAsErrors="$(InscribeTreatSpecificWarningsAsErrors)"
251 VerboseOutput="$(InscribeVerboseOutput)"
244 - AdditionalOptions="$(InscribeAdditionalOptions)">
252 + AdditionalOptions="$(InscribeAdditionalOptions)"
253 +
254 + RunAsSeparateProcess="$(RunWixToolsOutOfProc)"
255 + ToolExe="$(WixToolExe)"
256 + ToolPath="$(WixToolDir)">
257 <Output TaskParameter="Output" ItemName="SignBundleEngine" />
246 - </Insignia>
258 + </DetachBundleEngineForSigning>
259
248 - <!-- Explicitly add output to FileWrites to ensure they are included even when the target is up to date. -->
260 + <!-- Explicitly add output to FileWrites to ensure it is included even when the target is up to date. -->
261 <ItemGroup>
262 <FileWrites Include="$(IntermediateOutputPath)%(SignTargetPath.Filename)%(SignTargetPath.Extension)" />
263 </ItemGroup>
@@ -274,22 +286,26 @@
286 Inputs="@(SignTargetPath)"
287 Outputs="$(SignedFilePath)">
288
277 - <Insignia
278 - BundleFile="@(SignBundleEngine)"
279 - OriginalBundleFile="@(SignTargetPath)"
289 + <ReattachSignedBundleEngine
290 + BundleFile="@(SignTargetPath)"
291 + BundleEngineFile="@(SignBundleEngine)"
292 OutputFile="@(SignTargetPath)"
281 - ToolPath="$(WixToolPath)"
293 + IntermediateDirectory="%(SignTargetPath.RootDir)%(SignTargetPath.Directory)"
294 +
295 NoLogo="$(InscribeNoLogo)"
283 - RunAsSeparateProcess="$(RunWixToolsOutOfProc)"
296 SuppressAllWarnings="$(InscribeSuppressAllWarnings)"
297 SuppressSpecificWarnings="$(InscribeSuppressSpecificWarnings)"
298 TreatWarningsAsErrors="$(InscribeTreatWarningsAsErrors)"
299 TreatSpecificWarningsAsErrors="$(InscribeTreatSpecificWarningsAsErrors)"
300 VerboseOutput="$(InscribeVerboseOutput)"
289 - AdditionalOptions="$(InscribeAdditionalOptions)">
301 + AdditionalOptions="$(InscribeAdditionalOptions)"
302 +
303 + RunAsSeparateProcess="$(RunWixToolsOutOfProc)"
304 + ToolExe="$(WixToolExe)"
305 + ToolPath="$(WixToolDir)">
306 <Output TaskParameter="Output" ItemName="SignBundle" />
307 <Output TaskParameter="Output" ItemName="FileWrites" />
292 - </Insignia>
308 + </ReattachSignedBundleEngine>
309 </Target>
310
311 <!--
src/wix/test/WixToolsetTest.CoreIntegration/SigningFixture.cs
+112
@@ -62,5 +62,117 @@ namespace WixToolsetTest.CoreIntegration
62 }, rows);
63 }
64 }
65 +
66 + [Fact]
67 + public void CanInscribeBundle()
68 + {
69 + var folder = TestData.Get(@"TestData", "SimpleBundle");
70 + var signedFolder = TestData.Get(@"TestData", ".Data");
71 +
72 + using (var fs = new DisposableFileSystem())
73 + {
74 + var baseFolder = fs.GetFolder();
75 + var intermediateFolder = Path.Combine(baseFolder, "obj");
76 + var exePath = Path.Combine(baseFolder, @"bin\test.exe");
77 + var signedExe = Path.Combine(intermediateFolder, @"signed.exe");
78 + var reattachedExe = Path.Combine(baseFolder, @"bin\final.exe");
79 +
80 + var result = WixRunner.Execute(new[]
81 + {
82 + "build",
83 + Path.Combine(folder, "Bundle.wxs"),
84 + "-loc", Path.Combine(folder, "Bundle.en-us.wxl"),
85 + "-bindpath", Path.Combine(folder, "data"),
86 + "-bindpath", signedFolder,
87 + "-intermediateFolder", intermediateFolder,
88 + "-o", exePath,
89 + });
90 +
91 + result.AssertSuccess();
92 +
93 + result = WixRunner.Execute(new[]
94 +{
95 + "burn",
96 + "detach",
97 + exePath,
98 + "-engine", signedExe
99 + });
100 +
101 + result.AssertSuccess();
102 +
103 + // Swap in a pre-signed executable since signing during the unit test
104 + // is a challenge. The exe isn't an exact match but that's okay for
105 + // these testing purposes.
106 + File.Copy(Path.Combine(signedFolder, "signed_bundle_engine.exe"), signedExe, true);
107 +
108 + result = WixRunner.Execute(new[]
109 +{
110 + "burn",
111 + "reattach",
112 + exePath,
113 + "-engine", signedExe,
114 + "-o", reattachedExe
115 + });
116 +
117 + result.AssertSuccess();
118 + Assert.True(File.Exists(reattachedExe));
119 + }
120 + }
121 +
122 + [Fact]
123 + public void CanInscribeUncompressedBundle()
124 + {
125 + var folder = TestData.Get(@"TestData", "BundleUncompressed");
126 + var bindPath = TestData.Get(@"TestData", "SimpleBundle", "data");
127 + var signedFolder = TestData.Get(@"TestData", ".Data");
128 +
129 + using (var fs = new DisposableFileSystem())
130 + {
131 + var baseFolder = fs.GetFolder();
132 + var intermediateFolder = Path.Combine(baseFolder, "obj");
133 + var exePath = Path.Combine(baseFolder, @"bin\test.exe");
134 + var signedExe = Path.Combine(intermediateFolder, @"signed.exe");
135 + var reattachedExe = Path.Combine(baseFolder, @"bin\final.exe");
136 +
137 + var result = WixRunner.Execute(new[]
138 + {
139 + "build",
140 + Path.Combine(folder, "UncompressedBundle.wxs"),
141 + "-bindpath", bindPath,
142 + "-bindpath", signedFolder,
143 + "-intermediateFolder", intermediateFolder,
144 + "-o", exePath,
145 + });
146 +
147 + result.AssertSuccess();
148 +
149 + result = WixRunner.Execute(new[]
150 +{
151 + "burn",
152 + "detach",
153 + exePath,
154 + "-engine", signedExe
155 + });
156 +
157 + result.AssertSuccess();
158 +
159 + // Swap in a pre-signed executable since signing during the unit test
160 + // is a challenge. The exe isn't an exact match but that's okay for
161 + // these testing purposes.
162 + File.Copy(Path.Combine(signedFolder, "signed_bundle_engine.exe"), signedExe, true);
163 +
164 + result = WixRunner.Execute(new[]
165 +{
166 + "burn",
167 + "reattach",
168 + exePath,
169 + "-engine", signedExe,
170 + "-o", reattachedExe
171 + });
172 +
173 + Assert.True(File.Exists(reattachedExe));
174 + Assert.Equal(-1000, result.ExitCode);
175 + }
176 + }
177 }
178 }
src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/signed_bundle_engine.exe
Binary files /dev/null and b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/signed_bundle_engine.exe differ