I have created a Bootstrapper Application project using .NET 8 and I want to use it (as the UI) in my WiX bundle project. The problem I'm facing is that the output of my Bootstrapper Application project consists of many files (and folders), and I don't want to define each file explicitly in the WiX bundle as a Payload because:
Here is a snippet of my current Bundle.wxs file:
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Bundle Name="Setup.Sample.Bootstrapper"
Manufacturer="Sample Ltd."
Version="1.0.0.0"
UpgradeCode="ffffcf4d-b3e4-4eba-a850-ab8ab3fe501d">
<BootstrapperApplication SourceFile="Setup.Sample.Bootstrapper.WPF.exe">
<Payload SourceFile="$(Setup.Sample.Bootstrapper.WPF.TargetDir)*.*" />
</BootstrapperApplication>
<Chain>
<MsiPackage Id="MsiPackage"
DisplayName="MsiPackage"
SourceFile="$(var.Setup.Sample.Msi.TargetPath)"
EnableFeatureSelection="true"
Compressed="yes"
Cache="keep" />
</Chain>
</Bundle>
</Wix>
As you can see, I tried using wildcards, but that didn' work. I also tried setting property PublishSingleFile
to true
in my Bootstrapper Application project, but that didn't work either, because it seems to make a difference only when using dotnet publish
.
I've previously developed a custom Wix Extension, so it might be possible to create one that scans the TargetDir
of the Bootstrapper Application project and generates a list of XML Payload elements for the files.
Yeah, the approach of UPDATE 1 worked. See my answer for full code.
A custom CompilerExtension
does the job. Here's the code of the class:
public class CompilerExtension : BaseCompilerExtension
{
public CompilerExtension()
{
#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif
}
public override XNamespace Namespace => "https://xxx.at/schemas/wxs/extension";
public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
{
if (parentElement.Name.LocalName == "BootstrapperApplication")
{
if (element.Name.LocalName == "GeneratePayloads")
{
GeneratePayloads(parentElement, element, context["Source"]);
return;
}
}
ParseHelper.UnexpectedElement(parentElement, element);
}
private void GeneratePayloads(XElement parentElement, XElement element, string bootstrapperApplicationExeFilePath)
{
const string SourceDirectoryAttributeName = "SourceDirectory";
var sourceDirectoryAttribute = element.Attribute(SourceDirectoryAttributeName);
if (sourceDirectoryAttribute == null)
{
Messaging.Write(ErrorMessages.ExpectedAttribute(ParseHelper.GetSourceLineNumbers(element), element.Name.LocalName, SourceDirectoryAttributeName));
return;
}
var sourceDirectory = sourceDirectoryAttribute.Value;
if (!Directory.Exists(sourceDirectory))
{
Messaging.Write(ErrorMessages.ExpectedDirectory(sourceDirectory));
return;
}
XNamespace wixDefaultNs = parentElement.GetDefaultNamespace();
var payloadElements = Directory
.EnumerateFiles(sourceDirectory, "*", SearchOption.AllDirectories)
.Select(filePath => new
{
FilePath = filePath,
RelativePath = filePath.Substring(sourceDirectory.Length).Trim(Path.DirectorySeparatorChar)
})
.Where(item => !bootstrapperApplicationExeFilePath.Equals(item.FilePath, System.StringComparison.OrdinalIgnoreCase))
.Select(item => new XElement(wixDefaultNs + "Payload", new XAttribute("SourceFile", item.FilePath), new XAttribute("Name", item.RelativePath)));
element.AddAfterSelf(payloadElements);
}
}
And here's how to use it in the Bundle:
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:xxx="https://xxx.at/schemas/wxs/extension">
<Bundle Name="Setup.Sample.Bootstrapper"
Manufacturer="Sample Ltd."
Version="1.0.0.0"
UpgradeCode="ffffcf4d-b3e4-4eba-a850-ab8ab3fe501d">
<BootstrapperApplication SourceFile="$(Setup.Sample.Bootstrapper.WPF.TargetDir)$(Setup.Sample.Bootstrapper.WPF.TargetName).exe">
<xxx:GeneratePayloads SourceDirectory="$(Setup.Sample.Bootstrapper.WPF.TargetDir)" />
</BootstrapperApplication>
<Chain>
<MsiPackage Id="MsiPackage"
DisplayName="MsiPackage"
SourceFile="$(var.Setup.Sample.Msi.TargetPath)"
EnableFeatureSelection="true"
Compressed="yes"
Cache="keep" />
</Chain>
</Bundle>
</Wix>