The Windows Installer XML (WiX) is a toolset that builds Windows installation packages from XML source code. The toolset supports a command line environment that developers may integrate into their build processes to build MSI and MSM setup packages.

http://sourceforge.net/projects/wix http://blogs.msdn.com/robmen/

This tool properly handles discovering all of the dependencies in the WiX source files. It can follow include files and all elements in the WiX schema which reference files that get pulled into the Windows installer package or merge module. One thing to keep in mind is that the path specified in the 'src' attribute of WiX source files is assumed to be relative to the SCons root (where your SConstruct file is located).

I have not tried working with Windows Installer patch files yet, but it should be easy to extend this tool to handle them. The tool also does not try validating the WiX source file against the WiX schema, it is assumed that the WiX compilers will flag any errors with the source files.

Here is a very simple example using the tool to build a merge module:

   1 env.WiX('module.msm', ['module.wxs'])

At the end is the source for the tool itself. You will need to save this in something like 'wixtool.py', then load it into your construction environment in the standard way:

   1 env.Tool('wixtool', localtoolpath)

   1 """
   2 Tool to support WiX (Windows Installer XML toolset)
   3 http://blogs.msdn.com/robmen/
   4 http://sourceforge.net/projects/wix
   5 """
   6 __revision__ = "$Revision: 1.1 $"
   7 __date__ = "$Date: 2004/05/21 20:44:46 $"
   8 __author__ = "elliot.murphy@veritas.com"
   9 __credits__ = ""
  10 
  11 import os
  12 import xml.sax
  13 
  14 import SCons.Defaults
  15 import SCons.Util
  16 import SCons.Scanner
  17 
  18 def wix_scanner(node, env, path):
  19     ext = os.path.splitext(str(node))[1]
  20     known_wix_sourcefiles = ['.wxs', '.wxi']
  21     if ext not in known_wix_sourcefiles:
  22         return []
  23 
  24     include_files = []
  25     other_deps = []
  26 
  27     class MyHandler(xml.sax.handler.ContentHandler):
  28 
  29         def processingInstruction(self, target, data):
  30             if target == 'include':
  31                 data = str(data.strip())
  32                 if data not in include_files:
  33                     include_files.append(data)
  34 
  35         def startElement(self, name, attrs):
  36             # EJM - Not sure about the Directory and DirectoryRef elements. They
  37             # both have src attributes, but I don't know enough about MSI to
  38             # decide # if that means they should contribute to our dependency
  39             # graph. For now, they are not included.
  40             #
  41             # Not sure about the SFPCatalog element. I don't think groups
  42             # outside of MS will need to update the SFP catalog, so it's probably
  43             # safe to ignore for now. If someone from MS starts using this,
  44             # please update accordingly
  45             element_names = [
  46                 'File',
  47                 'Merge',
  48                 'Binary',
  49                 'Icon',
  50                 'DigitalCertificate',
  51                 'DigitalSignature',
  52                 'FileGroup',
  53                 'Text'
  54                 ]
  55 
  56             if name in element_names:
  57                 names = attrs.getNames()
  58                 if 'src' in names:
  59                     src = str(attrs.getValue('src'))
  60 
  61                     # This part is a bit of a hack for handling relative paths
  62                     # in both WiX and SCons. If the src attribute in the WiX
  63                     # file contains a directory separator, we assume that it
  64                     # is supposed to be a relative path from the root of the
  65                     # source tree, not from the directory that the Sconscript
  66                     # file is in. In order to do this, we prepend the magic #
  67                     # to the path so that scons will know the path is relative
  68                     # to the source tree root. The WiX compiler and linker are
  69                     # invoked from the root of the source tree, so they already
  70                     # treat src attributes as relative to the root of the src
  71                     # tree.
  72                     if '/' in src:
  73                         src = '#' + src
  74 
  75                     if src not in other_deps:
  76                         other_deps.append(src)
  77 
  78     xml.sax.parseString(node.get_contents(), MyHandler())
  79     print include_files, other_deps
  80     return include_files + other_deps
  81 
  82 def generate(env):
  83     """Add Builders and construction variables for WiX to an Environment."""
  84     env['WIXCANDLE'] = 'candle.exe'
  85     env['WIXCANDLEFLAGS'] = ['-nologo']
  86     env['WIXCANDLEINCLUDE'] = []
  87     env['WIXCANDLECOM'] = '$WIXCANDLE $WIXCANDLEFLAGS -I $WIXCANDLEINCLUDE -o ${TARGET} ${SOURCE}'
  88 
  89     env['WIXLIGHT'] = 'light.exe'
  90     env['WIXLIGHTFLAGS'] = ['-nologo']
  91     env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}"
  92 
  93     wxi_scanner = env.Scanner(
  94         function = wix_scanner,
  95         name = 'WiX Scanner',
  96         skeys = ['.wxs', '.wxi'],
  97         recursive = 1)
  98 
  99     env['SCANNERS'] += [wxi_scanner]
 100 
 101     object_builder = SCons.Builder.Builder(
 102         action = '$WIXCANDLECOM',
 103         suffix = '.wxiobj',
 104         src_suffix = '.wxs')
 105 
 106     linker_builder = SCons.Builder.Builder(
 107         action = '$WIXLIGHTCOM',
 108         src_suffix = '.wxiobj',
 109         src_builder = object_builder)
 110 
 111     env['BUILDERS']['WiX'] = linker_builder
 112 
 113 def exists(env):
 114     return 1 # TODO: Should we do a better job of detecting?

WiX_Tool (last edited 2009-05-23 15:42:46 by c-24-8-133-107)