Beginner's guide to MSI creation

msitools provides a compiler called wixl that is able to construct MSI file from a wxs file, written in XML.

The wxs format is shared with WiX (http://wixtoolset.org), so you can follow a tutorial to learn how to edit such file: http://wix.tramontana.co.hu/ .

Here is a short wxs file:

<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
  <Product Name='Foobar 1.0' Id='ABCDDCBA-86C7-4D14-AEC0-86416A69ABDE' UpgradeCode='ABCDDCBA-7349-453F-94F6-BCB5110BA4FD'
    Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='Acme Ltd.'>

    <Package Id='*' Keywords='Installer' Description="Acme's Foobar 1.0 Installer"
      Comments='Foobar is a registered trademark of Acme Ltd.' Manufacturer='Acme Ltd.'
      InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />

    <Media Id='1' Cabinet='Sample.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
    <Property Id='DiskPrompt' Value="Acme's Foobar 1.0 Installation [1]" />

    <Directory Id='TARGETDIR' Name='SourceDir'>
      <Directory Id='ProgramFilesFolder' Name='PFiles'>
        <Directory Id='Acme' Name='Acme'>
          <Directory Id='INSTALLDIR' Name='Foobar 1.0'>

            <Component Id='MainExecutable' Guid='ABCDDCBA-83F1-4F22-985B-FDB3C8ABD471'>
              <File Id='FoobarEXE' Name='FoobarAppl10.exe' DiskId='1' Source='FoobarAppl10.exe' KeyPath='yes'/>
            </Component>

          </Directory>
        </Directory>
      </Directory>
    </Directory>

    <Feature Id='Complete' Level='1'>
      <ComponentRef Id='MainExecutable' />
    </Feature>

  </Product>
</Wix>

wixl uses a single command to build. (WiX uses "heat" and "candle", see also this diagram).

Let's build the MSI:

# create a fake file
$ touch FoobarAppl10.exe

# build
$ wixl -v sample.wxs
Loading sample.wxs...
Building sample.msi...
Writing sample.msi...

You can then inspect MSI files with msidump and msiextract.

$ msiextract -l sample.msi
Program Files/Acme/Foobar 1.0/FoobarAppl10.exe

$ msidump sample.msi 
Exporting table _SummaryInformation...
Exporting table _ForceCodepage...
Exporting table AdminExecuteSequence...
Exporting table AdvtExecuteSequence...
Exporting table Component...
Exporting table FeatureComponents...
Exporting table CreateFolder...
Exporting table AppSearch...
Exporting table Property...
Exporting table File...
Exporting table ServiceInstall...
Exporting table Error...
Exporting table InstallExecuteSequence...
Exporting table InstallUISequence...
Exporting table RegLocator...
Exporting table Feature...
Exporting table CustomAction...
Exporting table Icon...
Exporting table Upgrade...
Exporting table RemoveFile...
Exporting table Shortcut...
Exporting table Directory...
Exporting table AdminUISequence...
Exporting table Registry...
Exporting table LaunchCondition...
Exporting table Media...
Exporting table Binary...
Exporting table ServiceControl...
Exporting table Signature...
Exporting table MsiFileHash...

$ cat File.idt 
File    Component_      FileName        FileSize        Version Language        Attributes      Sequence
s72     s72     l255    i4      S72     S20     I2      i4
File    File
FoobarEXE       MainExecutable  FoobarAppl10.exe        1                       512     1

To compare two MSIs, use msidiff. It can be handy to find bugs or missing features comparing WiX MSI build and wixl build for the same input.

Generating component groups

wixl-heat can help you to generate the list of files for your project and its dependencies. It takes the list of files as an input, either listed manually, or with ls, find or rpm -ql for instance:

echo /usr/i686-w64-mingw32/sys-root/mingw/bin/zlib1.dll | wixl-heat -p /usr/i686-w64-mingw32/sys-root/mingw/ --component-group CG.zlib --var var.SourceDir
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>
    <DirectoryRef Id="TARGETDIR">
      <Directory Id="dir9A5D56716D566997FA290054D161AF96" Name="bin">
        <Component Id="cmp9FA4766702C1F85ABA6E04DE3B061A12" Guid="*">
          <File Id="filE110AAB4803C5CC567BBCF8AD3BB7C08" KeyPath="yes" Source="$(var.SourceDir)/bin/zlib1.dll"/>
        </Component>
        </Directory>
    </DirectoryRef>
  </Fragment>
  <Fragment>
    <ComponentGroup Id="CG.zlib">
      <ComponentRef Id="cmp9FA4766702C1F85ABA6E04DE3B061A12"/>
    </ComponentGroup>
  </Fragment>
</Wix>
<!-- generated with msitools 0.93.40-6aec -->
<!-- wixl-heat -p /usr/i686-w64-mingw32/sys-root/mingw/ -component-group CG.zlib -var var.SourceDir -->

You can see an example how to automate the build process for a complex project in the virt-viewer source code, https://git.fedorahosted.org/cgit/virt-viewer.git/tree/data/Makefile.am:

# install project in temporary directory 
        $(AM_V_GEN)DESTDIR=`mktemp -d` &&                               \
        make -C $(top_builddir) install DESTDIR=$$DESTDIR >/dev/null && \
# generate a wxs with all the files in CG.virt-viewer component group
        find $$DESTDIR | wixl-heat -p $$DESTDIR$(prefix)/               \
            --component-group CG.virt-viewer --var var.DESTDIR          \
            --directory-ref=INSTALLDIR > virt-viewer-files.wxs &&       \
# build the MSI
        MANUFACTURER="$(MANUFACTURER)" wixl -D SourceDir=$(prefix)      \
             -D DESTDIR=$$DESTDIR$(prefix)                              \
             --arch $(WIXL_ARCH)                                        \
             -o $@                                                      \
             $< virt-viewer-files.wxs &&                                \
# remove temporary files
        rm -rf $$DESTDIR virt-viewer-files.wxs

Notes

Many features supported by WiX are not implemented in wixl, such as the installer UI support

msitools/HowTo/CreateMSI (last edited 2014-10-30 10:27:18 by MarcAndreLureau)