Month: November 2018

Creating a config file for MSI packaging software for an executable name that varies

mugsWithPaintings

The problem

Client wants our software packaged as an MSI (Microsoft Installer).  Found some software that will do this called exemsi.  Exemsi comes in a free mode with a GUI interface and a professional mode ($200 per machine) that can be called from a build process.

The professional mode relies on a configuration file.  The name of the executable file that you want to wrap in MSI is specified in the configuration file.

If the name of the executable file changes from release to release (for example, if it has a version number as part of the file name), the configuration file is supposed to have a way of finding the file based on wildcards and regular expressions.  I could not get this to work and had extreme difficulty reaching the author of the software.  He did reply to me once, but his suggestion did not solve the problem.

So, after much frustration, I decided to just make a little script that would read from a basic template configuration file, and write out nearly every line to a temporary configuration file for the current build.  Nearly every line because one line would have a substitution.  The line that specifies the executable name would be substituted for the installer file name of the current release.

I started writing this script as a DOS batch file.  I ran into trouble trying to make a loop with multiple command lines inside the loop.  I started thinking I’d scrap DOS and do it in Python, when my boss suggested Powershell.  Either one would serve.  The two machines on which this script would need to run already had Powershell installed, so that’s what won.

Besides, it afforded me a nice opportunity to learn how to use Powershell, having never used it before.

The solution

Here’s the Powershell script, which I called makeMsiWrapperConfig.ps1:

#To call from command line:
#  powershell -noexit "& ""path\makemsiwrapperconfig.ps1"" ""path\installer executable file name"" ""path\exemsi config template file"" ""path\resultant exemsi config file"""
#
#  where path is substituted with the actual path to the powershell file,
#  and installer executable file name is substituted with the actual lite installer file name,
#  and exemsi config template file is the name of the template configuration file for the exemsi software that wraps our installer executable in an MSI package,
#  and resultant exemsi config file is the output file with the correct executable name in it.
#  The outer set of quotation marks is so that powershell won't incorrectly parse the parameters if there are any spaces in the paths or filenames.
#  The next level of quotation marks is to escape the inner-most level of quotation marks so that once inside the script, the variable will be quoted.
#
#Example: 
#  powershell -noexit "& "".\makemsiwrapperconfig.ps1"" "".\target\app-123456.exe"" ""c:\resources\msiWrapper.xml"" "".\target\msiWrapper.xml"""  

$exeFile = $args[0]
$configTemplate = $args[1]
$resultantConfig = $args[2]
write-host "Creating configuration file for exemsi, the software which wraps the lite installer in an MSI package.  Parameters:"
write-host "  exe file to wrap - " $exeFile
write-host "  config template - " $configTemplate
write-host "  resultantconfig - " $resultantConfig
" " | set-content -path $resultantConfig
foreach($line in Get-Content $configTemplate) {
    $exportLine = $line
    if ($line -like '*<Executable*') {
        $exportLine = '<Executable FileName="' + $exeFile + '"/>'
    }
    #write-host $exportLine
    $exportLine | add-content -path $resultantConfig
}
return

 

It just reads every line from the template exemsi configuration file, and when it finds the line that has “<Executable” in it, makes a substitution using the file name from the parameter passed in.  Then it spits out the lines to a tailored-to-the-current-release configuration file.

I may tweak this to customize other lines in the configuration file.  For example the version number is one of the things in the configuration file that would vary from release to release and that has its own tag.  So more work will likely need to be done but at least this proves the concept.

The next thing I needed to do was call this from the Apache Ant build.xml file.  Here’s that code (the names have been changed to remove references to proprietary software):

<!-- create MSI (Microsoft Installer) artifacts -->
<exec executable="powershell" >
    <arg line="-noexit"/>
    <arg line="&amp; c:\resources\makemsiwrapperconfig.ps1 .\target\app-123456.exe c:\resources\msiwrapper.xml .\target\msiwrapper.xml"/>
</exec>
<exec executable="c:\resources\MsiWrapperBatch.exe">
    <arg value='config=.\target\msiWrapper.xml'/>
</exec>

It works.  I would hope there are easier solutions than this.  But once I had asked my boss to purchase the exemsi software and spent some time learning it, I was kinda like a dog with a bone and determined to make this solution work.  And hey, I learned something about Powershell and how to call a Powershell script from an Apache Ant build.xml file in the process.