Published on

In this entry we are going to discuss how to manage files bundled within an installer.

It is common to have a separate tool or program that must be bundled with and run from the installer, but before the file copying part of the installation process has completed. A common example would be a license validation program. Typically, all files bundled within an installer are unpacked and then any tools would be run. In the case of a license validator, that is less than ideal because the user may end up waiting for the files to be unpacked only to find that the license is not valid. The user would then have to wait for the installation to be rolled back.

InstallBuilder provides you with actions to deal with these situations. The most important are <unpackFile> and <unpackDirectory>. Let’s assume we are in the situation detailed above. A typical project XML file would look something like:

<project>
    ...
    <componentList>
        <component>
            <name>default</name>
            <folderList>
                <folder>
                    <name>programfiles</name>
                    <destination>${installdir}</destination>
                    <distributionFileList>
                        <distributionDirectory origin="/path/to/dir/to/pack"/>
                        <distributionFile origin="/path/to/checker"/>
                    </distributionFileList>
                </folder>
            </folderList>
        </component>
    </componentList>
    <parameterList>
        <stringParameter>
            <name>licenseCheck</name>
            <description>Introduce your license key</description>
            <value></value>
        </stringParameter>
    </parameterList>
    ...
</project>

As you can see, the user is asked to supply a license key. Without the <unpack> actions, the information provided would not be checked until several steps later, which is not our desired behavior. Instead, we can do the following:

<parameterlist>
   <stringparameter>
       <name>licenseCheck</name>
       <title>Introduce your license key</title>
       <value></value>
       <validationactionlist>
           <unpackfile component="default" folder="programfiles" origin="checker" destination="/tmp">
           <runprogram>
               <program>/tmp/checker</program>
               <programarguments>${project.parameter(licenseCheck).value}</programarguments>
           </runprogram>
           <throwerror text="Wrong license key, please enter a valid one or cancel the installation">
               <rulelist>
                   <comparetext text="${program_stdout}" logic="equals" value="1">
                   </comparetext>
               </rulelist>
           </throwerror>
           </unpackfile>
       </validationactionlist>
   </stringparameter>
</parameterlist>

The packed file can even be a zip file. You just need to use an <unzip> action before executing it:

<unzip>
  <destinationdirectory>${installdir}</destinationdirectory>
  <zipfile>/tmp/unpacked/file.zip</zipfile>
</unzip>

As you can see in the example above, the <origin> in the unpack action corresponds with the location of the file inside the installer, not with the path to the file in the build machine. If you packed a directory “/path/to/dir/to/pack” containing three files, fileA, fileB and fileC, to unpack fileB, you should use an <unpackfile> with origin=“pack/fileB”:

<distributiondirectory origin="/path/to/dir/to/pack"/>
...  
<unpackfile component="default" folder="programfiles" origin="pack/fileB" destination="/some/destination"/>

You could imagine this structure inside the installer:

To reference logo.jpg:

<unpackfile component="componentA" folder="folder1" origin="someDirectory/logo.jpg" destination="${installdir}"/>

It does not matter where the file was located originally in the building machine, just the path inside the installer:

<component name="componentA">
<folderlist>
<folder>
<name>folder1</name>
<distributionfilelist>
   <distributiondirectory>
        <origin>/some/long/path/in/the/building/machine/someDirectory</origin>
   </distributiondirectory>
</distributionfilelist>
</folder>
</folderlist>
</component>

InstallBuilder also includes functionality to facilitate uninstalling bundled programs or tools. When an uninstaller is run, all files packed are automatically deleted, but if one of the bundled applications generates other files or directories, those have to be manually deleted using <deletefile> actions. Another way to take care of deleting all of the files is to use the <addfilestouninstaller> action:

<addfilestouninstaller path="/some/path/to/the/file"/>

Note that this action will only work with existing files. You cannot add a file that is going to be created but does not yet exist. As a workaround for these situations, you can use a <touchfile> action:

<touchfile path="/some/path/to/the/file"/>
<addfilestouninstaller path="/some/path/to/the/file"/>

Even if the real file is a directory, the uninstaller will register it correctly.

You can also remove files to be deleted from the uninstaller to avoid deleting them during uninstallation:

<removefilesfromuninstaller files="${installdir}/*" exclude="*.txt"/>

This will exclude from the uninstaller all of the contents of “installdir” except for .txt files.

The example above uses the <exclude> tag, introduced in InstallBuilder 6.1.1, which allows you to specify patterns of file that should not be affected by the action.