WiX Toolset

Multi-arch BalExtension BAs

User stories

  • As a Setup developer I can author my bundle using the built-in BootstrapperApplications in BalExtension such that the correct architecture is automatically used.

Proposal - Bal.wixext

In v3.x, the built-in BootstrapperApplications in BalExtension were used by creating a BootstrapperApplicationRef with a documented Id. They were customized with child bal:WixStandardBootstrapperApplication and bal:WixManagedBootstrapperApplicationHost elements. In order to allow the BalExtension compiler to reference the correct BootstrapperApplication based on the target architecture, these child elements will be the supported way of using the BA's instead of a special Id on BootstrapperApplicationRef.

The bal:WixStandardBootstrapperApplication element will get a new Theme attribute to specify which flavor to use:

<xs:attribute name="Theme" use="required">
    <xs:annotation>
        <xs:documentation>The built-in theme to use.</xs:documentation>
    </xs:annotation>
    <xs:simpleType>
        <xs:restriction base="xs:NMTOKEN">
            <xs:enumeration value="hyperlinkLargeLicense" />
            <xs:enumeration value="hyperlinkLicense" />
            <xs:enumeration value="hyperlinkSidebarLicense" />
            <xs:enumeration value="none" />
            <xs:enumeration value="rtfLargeLicense" />
            <xs:enumeration value="rtfLicense" />
        </xs:restriction>
    </xs:simpleType>
</xs:attribute>

The bal:WixManagedBootstrapperApplicationHost element will also get a new Theme attribute to specify which flavor to use:

<xs:attribute name="Theme" default="standard">
    <xs:annotation>
        <xs:documentation>The built-in theme to use.</xs:documentation>
    </xs:annotation>
    <xs:simpleType>
        <xs:restriction base="xs:NMTOKEN">
            <xs:enumeration value="none" />
            <xs:enumeration value="standard" />
        </xs:restriction>
    </xs:simpleType>
</xs:attribute>

Proposal - Core

This is how the WiX BA should have been added to the bundle in v3:

<Bundle>
  <BootstrapperApplicationRef Id='ManagedBootstrapperApplicationHost' bal:UseUILanguages='yes'>
    <bal:WixManagedBootstrapperApplicationHost LicenseFile='..\..\..\License.txt' />
    <Payload Name='BootstrapperCore.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload SourceFile='WixBA.dll' />
  </BootstrapperApplicationRef>
</Bundle>

Under the above proposal, this is how it would be in v4:

<Bundle>
  <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt'>
    <Payload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload SourceFile='WixBA.dll' />
  </bal:WixManagedBootstrapperApplicationHost>
</Bundle>

The problem with this is that the Bal extension is now required to parse Core elements (Payload and PayloadGroupRef). There is no other way for the Setup developer to add payloads to the UX container - currently that requires adding them under BootstrapperApplication or BootstrapperApplicationRef. The BootstrapperApplication is defined in Bal.wixext so can't be used by the Setup developer. BootstrapperApplicationRef can't be used by the Setup developer either, because the Id changes based on the target architecture (the issue we're solving here). Requiring extensions to parse Core elements is not acceptable because that parsing logic is in Core and is subject to change.

In order for the Setup Developer to include their own payloads in the UXContainer, the core WiX schema needs to be enhanced. The basic idea is to remove the payload information from BootstrapperApplication and require the new BootstrapperApplicationDll element to declare the actual BA.

Remove Name and SourceFile attributes from BootstrapperApplication. Allow BootstrapperApplication and BootstrapperApplicationRef to be specified any number of times under Bundle. All child payloads from those elements will go into the UX container.

Add BootstrapperApplicationDll element. A bundle must have exactly one BootstrapperApplicationDll.

<xs:element name="BootstrapperApplicationDll">
  <xs:annotation>
    <xs:documentation>Describes the entry point to the bootstrapper application.</xs:documentation>
    <xs:appinfo>
      <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="BootstrapperApplication" />
    </xs:appinfo>
  </xs:annotation>
  <xs:complexType>
    <xs:attribute name="Id" type="xs:string">
      <xs:annotation>
        <xs:documentation>The identifier of BootstrapperApplicationDll element.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="SourceFile" type="xs:string" use="required">
      <xs:annotation>
        <xs:documentation>Location of the source file.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="Name" type="xs:string">
      <xs:annotation>
        <xs:documentation>The destination path and file name for this payload. The default is the source file name. The use of '..' directories is not allowed.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>
</xs:element>

The Bal.wixext currently has this authoring for MBA:

<Fragment>
  <BootstrapperApplication Id='ManagedBootstrapperApplicationHost$(var.Suffix)' SourceFile='!(bindpath.$(var.platform))\mbahost.dll'>
    <PayloadGroupRef Id='Mba' />
    <PayloadGroupRef Id='MbaPreqStandard' />
  </BootstrapperApplication>
</Fragment>

This will change to:

<Fragment>
  <BootstrapperApplication Id='ManagedBootstrapperApplicationHost$(var.Suffix)'>
    <BootstrapperApplicationDll SourceFile='!(bindpath.$(var.platform))\mbahost.dll' />
    <PayloadGroupRef Id='Mba' />
    <PayloadGroupRef Id='MbaPreqStandard' />
  </BootstrapperApplication>
</Fragment>

The bundle authoring will look like:

<Bundle>
  <BootstrapperApplication>
    <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
    <Payload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload SourceFile='WixBA.dll' />
    <Payload SourceFile='mbanative.dll' />
  </BootstrapperApplication>
</Bundle>

Considerations

In WiX v3.x Setup developers were able to assume that no matter what architecture they picked to build the bundle, the BA would be x86 due to v3 always picking the x86 stub. Since some of their BA payloads may be architecture specific (bafunctions.dll, the MBA, or any of the MBA's dependencies), automatically converting the old elements could lead to an unusable bundle.

These were the options considered for enhancing the language:

Option 1. Create BootstrapperApplicationContainer element, which can be specified any number of times. It can have an Id to be referenced from a new BootstrapperApplicationContainerRef element. This would look like:

<Bundle>
  <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
  <BootstrapperApplicationContainer>
    <Payload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload SourceFile='WixBA.dll' />
  </BootstrapperApplicationContainer>
</Bundle>

or

<Bundle>
  <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
  <BootstrapperApplicationContainerRef Id='WixBA'>
    <Payload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
  </BootstrapperApplicationContainerRef>
</Bundle>
<Fragment>
  <BootstrapperApplicationContainer Id='WixBA'>
    <Payload SourceFile='WixBA.dll' />
  </BootstrapperApplicationContainer>
</Fragment>

Option 2. Same as option 1, but also remove the ability to specify child Payload or PayloadGroup elements under BootstrapperApplication. Require the payload information on the BootstrapperApplication element. Remove the BootstrapperApplicationRef element entirely. Require the parent of BootstrapperApplication to be BootstrapperApplicationContainer or BootstrapperApplicationContainerRef. Change the parent of the Bal BA elements to also be either BootstrapperApplicationContainer or BootstrapperApplicationContainerRef. The Bal.wixext currently has this authoring for MBA:

<Fragment>
  <BootstrapperApplication Id='ManagedBootstrapperApplicationHost$(var.Suffix)' SourceFile='!(bindpath.$(var.platform))\mbahost.dll'>
    <PayloadGroupRef Id='Mba' />
    <PayloadGroupRef Id='MbaPreqStandard' />
  </BootstrapperApplication>
</Fragment>

This would change to:

<Fragment>
  <BootstrapperApplicationContainer Id='ManagedBootstrapperApplicationHost$(var.Suffix)'>
    <BootstrapperApplication SourceFile='!(bindpath.$(var.platform))\mbahost.dll' />
    <PayloadGroupRef Id='Mba' />
    <PayloadGroupRef Id='MbaPreqStandard' />
  </BootstrapperApplication>
</Fragment>

The bundle authoring would look like:

<Bundle>
  <BootstrapperApplicationContainer>
    <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
    <Payload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload SourceFile='WixBA.dll' />
  </BootstrapperApplicationContainer>
</Bundle>

Option 3. Allow BootstrapperApplicationRef without an Id. This would look like:

<Bundle>
  <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
  <BootstrapperApplicationRef>
    <Payload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload SourceFile='WixBA.dll' />
  </BootstrapperApplicationRef>
</Bundle>

Option 4. Create BootstrapperApplicationContainerRef which has no attributes and can have children Payload and PayloadGroupRef elements. This would look like:

<Bundle>
  <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
  <BootstrapperApplicationContainerRef>
    <Payload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload SourceFile='WixBA.dll' />
  </BootstrapperApplicationContainerRef>
</Bundle>

Option 5. Create BootstrapperApplicationPayload, BootstrapperApplicationPayloadGroup, and BootstrapperApplicationPayloadGroupRef elements. This would look like:

<Bundle>
  <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
  <BootstrapperApplicationPayloadGroup Id='WixBA'>
    <Payload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload SourceFile='WixBA.dll' />
  </BootstrapperApplicationPayloadGroup>
</Bundle>

or

<Bundle>
  <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
  <PayloadGroup Id='WixBA'>
    <BootstrapperApplicationPayload Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <BootstrapperApplicationPayload SourceFile='WixBA.dll' />
  </PayloadGroup>
</Bundle>

Option 6. Add BootstrapperApplication=yes to Payload elements. This would look like:

<Bundle>
  <bal:WixManagedBootstrapperApplicationHost Theme='standard' LicenseFile='..\..\..\License.txt' />
  <PayloadGroup Id='WixBA'>
    <Payload BootstrapperApplication='yes' Name='WixToolset.Mba.Core.config' SourceFile='WixBA.BootstrapperCore.config' />
    <Payload BootstrapperApplication='yes' SourceFile='WixBA.dll' />
  </PayloadGroup>
</Bundle>

See Also