Controlling the sequence of events in a Wixtoolset (.msi) installer - configuration-files

I am creating a Microsoft Installer (.msi file) using the Wixtoolset (Windows Installer XML). This installer must automate the installation of an existing .exe program (named installer.exe below) and copy a custom configuration file (named settings.conf below) to the target directory. In addition the installer must modify the configuration file using the InstallFiles command below. But the timing of events is critical. If the executable installer runs too early, it fails or exhibits strange behavior. And if the executable installer run too late in the install sequence, it overwrites my modified configuration file with the generic values. I believe this can be done by assigning a string to the Before or After property value. What Before or After property assignment will allow the executable to run properly but not overwrite the configuration file I moved by the CopyFile element? Here is my Wixtoolset XML code.
<Property Id="CONFIGFOLDER" Value="C:\acme\config" >
<Feature
Id="ConfigurationFile"
Title="Configuration File"
Level="1"
<ComponentRef Id="CMP_ACME_Config_File" />
</Feature>
<DirectoryRef Id="TARGETDIR">
<Component Id="CMP_ACME_Config_File" Guid="">
<File
Id="ACME_Config"
Source="MySettings.conf"
KeyPath="yes"
<CopyFile Id="Copy_ACME_Config"
DestinationProperty="CONFIGFOLDER"
DestinationName="settings.conf" />
</File>
</Component>
</DirectoryRef>
<Binary
Id="InstallerEXE"
SourceFile="installer.exe" />
<CustomAction
Id="Launch_Installer"
BinaryKey="InstallerEXE"
Impersonate="yes"
Execute="deferred"
ExeCommand=""
Return="check" />
<InstallExecuteSequence>
<Custom Action="Launch_Installer"
Before="InstallFiles">
</Custom>
</InstallExecuteSequence>
</Property>

I can't explain exactly why this works but assigning "InstallFiles" to the "After" property in the "Custom" element seems to do the trick.
<InstallExecuteSequence>
<Custom Action="Launch_Installer"
After="InstallFiles">
</Custom>
</InstallExecuteSequence>

Related

How to return a value from a phing target?

I would like to have a foreach task like this, which iterates over all the files/directories in a directory "A" -
<foreach param="dirname" absparam="absname" target="subtask">
<fileset dir="${dir.destination}/${dir.subdir}/">
<type type="file" />
</fileset>
</foreach>
The target "subtask" should check if the counterpart of the file/folder exists in another directory "B" (I am comparing directory A and B basically), and return either of the following if it does not -
a flag.
name of the file.
Following is some code for reference -
<target name="subtask">
<if>
<filesmatch file1="${file1}" file2="${file2}"/>
<then>
Return false. But how?
</then>
<else>
Return true of name of the file. How?
</else>
</if>
</target>
Note - It is okay if this can be done without calling a target. I am not sure if the logic can be fit inside the foreachtask itself. Could not find any such thing in the phing documentation.
Basically, I should be having the list of file names which are not present in the directory B, by the end of the loop.
You may also read this question of mine, if you can give some pointers to solve the issue in some other way.
Update
Rephrasing this question, since I feel that the problem description is not clear. The phing documentation says, a target has no return value -
Targets are collections of project components (but not other targets)
that are assigned a unique name within their project. A target
generally performs a specific task -- or calls other targets that
perform specific tasks -- and therefore a target is a bit like a
function (but a target has no return value).
I don't understand why is it designed so. With this bounty, I would like to know if there is some workaround for me other than having to define my own custom tasks in PHP, and then set properties -
$this->getProject()->setNewProperty('modifiedElements', implode("\n\n",$modifiedElementsArray));
which can be accessed in the build file
I have a target which checks whether my production code base has any differences from the expected git revision -
<target name="compare_prod_with_expected_revision">
<input propertyname="box.git_version">
Enter git version of the production codebase:
</input>
<exec command="git reset --hard ${box.git_version}" dir="${dir.scratchpad}" />
<!-- Scratchpad brought to revision ${box.git_version} -->
<echo>Verifying whether production is at revision ${box.git_version}..</echo>
<exec command="diff -arq --exclude='.git' ${dir.scratchpad}/${dir.subdir} ${dir.destination}/${dir.subdir}" outputProperty="diffList"/><!-- #TODO ignore.swp files in this step. Diff says .swp files present in production code. But doing ls -a there does not show the same. -->
<php function="strlen" returnProperty="productionDeviationFromExpectedBranch"><!-- #TODO - find how to not show this step during build process. Put it in a target and set hidden="true" -->
<param value="${diffList}"/>
</php>
<if>
<equals arg1="${productionDeviationFromExpectedBranch}" arg2="0" />
<then>
<echo>Verified production is at revision ${box.git_version}</echo>
</then>
<else>
<echo>Differences - </echo>
<echo>${diffList}</echo>
</else>
</if>
</target>
Now, I want to phingcall this target and would like to access some property set by it.
I think I understood your purposes, and at the same time I feel like you chosen not the optimal tool of doing this.
As you mentioned in your question, official documentation on phing is clear about tasks (targets):
Targets are collections of project components (but not other targets) that are assigned a unique name within their project. A target generally performs a specific task -- or calls other targets that perform specific tasks -- and therefore a target is a bit like a function (but a target has no return value).
Targets should be components of your application, which execute specific task, atomic task. It could be initialization task, configuration fetching, compilation step, assets preparation and dumping, deployment task, clean-up task, etc. There's no "output", returned by target in the standard sense, but the result of target execution is the success of execution itself: success or failure.
One should not try to put way too much of logic into such project targets, as it is not intended to do complicated calculations, do heavy logical decisions, etc. I mean, Phing can do it, such things are possible, but this setup would be bulky, unreadable, and hard to scale/re-factor.
With Phing you can easily define conditional execution and branching of logical flow, you may define the sequence of execution of tasks (dependencies) - this is what makes it laconic and elegant. Keep targets as simple as possible, split the project into small, finished logical tasks.
Based on the projects I've been working with, the biggest targets, probably, were initialization stage and configs fetching. Here's some example, to understand what it might contain, I took it from real project:
<target name="init_configuration">
<echo msg="Define initial configuration for the deployment..." />
<if>
<not>
<isset property="host" />
</not>
<then>
<property name="host" value="dev" override="true" />
<echo message="The value of hostname has been set to ${host}" />
</then>
<else>
<echo message="The value of hostname is ${host}" />
</else>
</if>
<if>
<not>
<isset property="version" />
</not>
<then>
<property name="version" value="1.0.0" override="true" />
<echo message="The value of version has been set to ${version}" />
</then>
<else>
<echo message="The value of version is ${version}" />
</else>
</if>
<property name="host_credital_file" value="config/hosts/${host}.properties" />
<property file="${host_credital_file}" />
<available file="${host_credital_file}" property="hostfilefound" value="true"/>
<fail unless="hostfilefound" message="Missing Hostfile configuration file (${host_credital_file})!" />
<echo msg="Configuration is done" />
</target>
Other targets were extremely simplistic, they are normally – 1-5 lines long, and do only small purpose, small task. This would be, probably, the best recommendation when working with Phing.
The logic which you are trying to put on shoulders of Phing is possible, but would be extremely bulky.
Consider the point: how much quicker, easier, and more readable the same thing could be done with simple bash script in your example. Or even to write small CLI utility in PHP, which will do the job elegantly and quick. After that in Phing you'll leave parametric target which will execute this "revision diff script" from CLI.
Phing is a great tool for what it is designed for, but it can't be an optimal choice for every purpose. Just do not put way to much responsibility and logic into it.
As a workaround, for more complicated things it's better to combine Phing with with something specialized: bash scripting, PHP CLI, nodeJS (+ Grunt, Gulp, etc)... and just to add calls of a Phing targets later.
This is the way I managed to have targets which behave like functions:
<target name="-console-cmd-return-property" hidden="true">
<exec command="${command}" checkreturn="${checkreturn}" logoutput="${logoutput}" outputProperty="${outputProperty}"/>
</target>
It gets invoked like this:
<phingcall target="--console-return-property">
<property name="command" value="ps auxwww"/>
<property name="checkreturn" value="true"/>
<property name="logoutput" value="false"/>
<property name="outputProperty" value="ps_output"/>
</phingcall>
Of course it works because it relies on existing exec, and it is not generic...
The target "subtask" should check if the counterpart of the file/folder exists in another directory "B" (I am comparing directory A and B basically), and return either of the following if it does not -
a flag.
name of the file.
You could compare two directories without using a foreach task like this:
<project name="Phing Build Test" default="print-missing" basedir=".">
<resolvepath propertyName="dir.a" path="path/to/dir/a"/>
<resolvepath propertyName="dir.b" path="path/to/dir/b"/>
<target name="print-missing">
<apply executable="echo" failonerror="false" returnProperty="files.found" outputProperty="missing">
<srcfile/>
<fileset id="srcfiles" dir="${dir.a}" includes="*">
<present present="srconly" targetdir="${dir.b}"/>
</fileset>
</apply>
<if>
<equals arg1="${files.found}" arg2="0"/>
<then>
<echo msg="${missing}"/>
</then>
</if>
</target>
</project>

WiX Bootstrapper: How do I set burn variables from the command line?

Using WiX 3.7 and .NET 4.0.
How does one set burn variables when running a WiX bootstrapper EXE from the command line?
First of all, the burn variables that you wish to set need to be set as Overridable. To do this you must include the follow namespace in your WXS: xmlns:bal="http://schemas.microsoft.com/wix/BalExtension" and if you're using Visual Studio like me you need to include WixBalExtension.dll in your project references. Next you need to add the following attribute to all of the burn variables that you want to set via the command line: bal:Overridable="yes".
Now you can set the variables via the command line in this fashion:
BootstrapperSetup.exe /i /passive MyBurnVariable1=1 MyBurnVariable2=2
Below is an example of a WXS file that satifies all of the conditions described above:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle Name="MyProduct" Version="1.0.0" Manufacturer="MyManufacturer" UpgradeCode="PUT-UPGRADE-CODE-HERE">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkLicense">
<bal:WixStandardBootstrapperApplication LicenseUrl="MyLicense.htm" ThemeFile="MyThemeFile.xml" LocalizationFile="MyLocFile.wxl" />
</BootstrapperApplicationRef>
<Variable Name="MyBurnVariable1" bal:Overridable="yes" Type="numeric" Value="0" />
<Variable Name="MyBurnVariable2" bal:Overridable="yes" Type="numeric" Value="0" />
<Chain>
<MsiPackage Id="MyFirstMsiPackage"
SourceFile="first.msi"
InstallCondition="MyBurnVariable1 = 1" />
<MsiPackage Id="MySecondMsiPackage"
SourceFile="second.msi">
<MsiProperty Name="MY_PROPERTY" Value="[MyBurnVariable2]" />
</MsiPackage>
</Chain>
</Bundle>
</Wix>

Run PowerShell script from WiX installer

I have found a couple of examples showing how to run a PowerShell script from WiX but have not been successful running either of them. So, I'd like to post what I have with the hope that someone can point out what I am doing wrong.
<!--Install the PowerShell script-->
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="cmp_ShutdownIExplore" Guid="{4AFAACBC-97BB-416f-9946-68E2A795EA20}" KeyPath="yes">
<File Id="ShutdownIExplore" Name="ShutdownIExplore.ps1" Source="$(var.ProjectDir)Source\PowerShell\ShutdownIExplore.ps1" Vital="yes" />
</Component>
</DirectoryRef>
<!--Define the CustomAction for running the PowerShell script-->
<CustomAction Id="RunPowerShellScript" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="yes" />
<InstallExecuteSequence>
<!--Invoke PowerShell script -->
<Custom Action="RunPowerShellScript" After="InstallFiles"><![CDATA[NOT Installed]]></Custom>
</InstallExecuteSequence>
<!-- Define custom action to run a PowerShell script-->
<Fragment>
<!-- Ensure PowerShell is installed and obtain the PowerShell executable location -->
<Property Id="POWERSHELLEXE">
<RegistrySearch Id="POWERSHELLEXE"
Type="raw"
Root="HKLM"
Key="SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
Name="Path" />
</Property>
<Condition Message="This application requires Windows PowerShell.">
<![CDATA[Installed OR POWERSHELLEXE]]>
</Condition>
<!-- Define the PowerShell command invocation -->
<SetProperty Id="RunPowerShellScript"
Before ="InstallFiles"
Sequence="execute"
Value =""[POWERSHELLEXE]" -Version 2.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command "& '[#ShutdownIExplore.ps1]' ; exit $$($Error.Count)"" />
</Fragment>
When I run the installer I have created I get the following error (from log):
MSI (s) (DC:F8) [11:21:46:424]: Executing op: ActionStart(Name=RunPowerShellScript,,)
Action 11:21:46: RunPowerShellScript.
MSI (s) (DC:F8) [11:21:46:425]: Executing op: CustomActionSchedule(Action=RunPowerShellScript,ActionType=1025,Source=BinaryData,Target=CAQuietExec,)
MSI (s) (DC:9C) [11:21:46:459]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI8228.tmp, Entrypoint: CAQuietExec
CAQuietExec: Error 0x80070057: failed to get command line data
CAQuietExec: Error 0x80070057: failed to get Command Line
CustomAction RunPowerShellScript returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 11:21:46: InstallFinalize. Return value 3.
I am not at all clear what this error is trying to say. Are my internal references bad? Is the command to execute the script bad? Something else?
Any help is most appreciated and thanks in advance.
Looks like you have scheduled the CAQuietExec action as deferred. In this case you have to pass the command line to be executed via a CustomActionData property called QtExecDeferred which is written to the execution script. The deferred action can then access the property from the script.
More details at http://wixtoolset.org/documentation/manual/v3/customactions/qtexec.html
I didn't understand Stephen's answer, however I eventually got it working with the help of this blog post.
Here's a summary of the change I made to Greg's code to get it to work:
I changed CAQuietExec to WixQuietExec (I'm not sure if this was necessary).
In SetProperty I changed the value of the Before attribute from InstallFiles to the Id of the custom action; in Greg's case it would be RunPowerShellScript.
Although unrelated to the question, I ended up needing to change the -Version of powershell to 3.0 from 2.0 to prevent an error when running my script.
Here was my actual working code:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="*" Name="..." Language="1033" Version="..." Manufacturer="..." UpgradeCode="...">
<Property Id="POWERSHELLEXE">
<RegistrySearch Id="POWERSHELLEXE"
Type="raw"
Root="HKLM"
Key="SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
Name="Path" />
</Property>
<Condition Message="This application requires Windows PowerShell.">
<![CDATA[Installed OR POWERSHELLEXE]]>
</Condition>
<SetProperty Id="InstallMongoDB"
Before ="InstallMongoDB"
Sequence="execute"
Value=""[POWERSHELLEXE]" -Version 3.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command "& '[#MONGODB_INSTALL.PS1]' ; exit $$($Error.Count)"" />
<CustomAction Id="InstallMongoDB" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Return="check" Impersonate="yes" />
<InstallExecuteSequence>
<Custom Action="InstallMongoDB" Before="InstallFinalize"><![CDATA[NOT Installed]]></Custom>
</InstallExecuteSequence>
<Component Id="MONGODB_INSTALL.PS1" Guid="..." DiskId="1">
<File Id="MONGODB_INSTALL.PS1" Name="mongodb-install.ps1" Source="mongodb-install.ps1"/>
</Component>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="APPLICATIONFOLDER" Name="...">
<Directory Id="InstallScripts" Name="InstallScripts">
<Component Id="MONGODB_INSTALL.PS1" Guid="..." DiskId="1">
<File Id="MONGODB_INSTALL.PS1" Name="mongodb-install.ps1" Source="mongodb-install.ps1"/>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Fragment>
</Wix>
Only the following example helped me
https://github.com/damienbod/WiXPowerShellExample/blob/master/SetupWithPowerShellScripts/Product.wxs
you need to add smth similar into your 'Product.wxs'. the 'Value' property of the first 'CustomAction' contains a ps script (create and run a windows service in my case).
<!-- assign the string (ps command) to RegisterPowerShellProperty -->
<CustomAction Id="RegisterWindowsService"
Property="RegisterPowerShellProperty"
Value=""C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NonInteractive -InputFormat None -NoProfile sc.exe create MyService binpath= 'C:\Program Files (x86)\My service\MyService.exe';sc.exe start MyService"
Execute="immediate" />
<!-- Deferred execution of the above script -->
<CustomAction Id="RegisterPowerShellProperty"
BinaryKey="WixCA"
DllEntry="CAQuietExec64"
Execute="deferred"
Return="check"
Impersonate="no" />
<InstallExecuteSequence>
<!-- On installation we register and start a windows service -->
<Custom Action="RegisterWindowsService" After="CostFinalize">NOT Installed</Custom>
<Custom Action="RegisterPowerShellProperty" After="InstallFiles">NOT Installed</Custom>
</InstallExecuteSequence>
you will need to add a reference to 'WixUtilExtension' in order to run the script.

First run notepad with my.cfg and only then start the service

I install along with my application:
1) a service that starts and stops my application as needed
2) a conf file that contains actually the user data and that will be shown to the user to modify as needed (I give the user the chance to change it by running notepad.exe with my conf file during installing)
The problem is that in my code the service I install starts before the user had the chance to modify the conf file. What I would like is:
1) first the user gets the chance to change the conf file (run notepad.exe with the conf file)
2) only afterward start the service
<Component Id="MyService.exe" Guid="GUID">
<File Id="MyService.exe" Source="MyService.exe" Name="MyService.exe" KeyPath="yes" Checksum="yes" />
<ServiceInstall Id='ServiceInstall' DisplayName='MyService' Name='MyService' ErrorControl='normal' Start='auto' Type='ownProcess' Vital='yes'/>
<ServiceControl Id='ServiceControl' Name='MyService' Start='install' Stop='both' Remove='uninstall'/>
</Component>
<Component Id="my.conf" Guid="" NeverOverwrite="yes">
<File Id="my.cfg" Source="my.cfg_template" Name="my.cfg" KeyPath="yes" />
</Component>
[...]
<Property Id="NOTEPAD">Notepad.exe</Property>
<CustomAction Id="LaunchConfFile" Property="NOTEPAD" ExeCommand="[INSTALLDIR]my.cfg" Return="ignore" Impersonate="no" Execute="deferred"/>
<!--Run only on installs-->
<InstallExecuteSequence>
<Custom Action='LaunchConfFile' Before='InstallFinalize'>(NOT Installed) AND (NOT UPGRADINGPRODUCTCODE)</Custom>
</InstallExecuteSequence>
What am I doing wrong in the above code and how could I change it in order to achieve what I need? (first run notepad with my conf file and then start the service).
I would extend the MSI UI to ask for the parts the user needs to modify and then update the text file using XmlFile and XmlConfig elements. Then Windows installer can come by and start the service.

How do I optionally require a command line arguement for Ant?

I'm new to ant, and I want to require a file name if something other than the default target is used, so the calling syntax would be something like this:
ant specifictarget -Dfile=myfile
I'm using the ant contrib package to give me additional functionality, so I have this:
<if>
<equals arg1="${file}" arg2="" />
<then>
<!-- fail here -->
</then>
</if>
My thinking is that if file was not specified it might be equal to the empty string. Obviously, this didn't work, and I'm not finding any examples on google or the right syntax in the manual.
So what syntax should I use?
You don't really need the contrib package. This is more conveniently done using built-in ant capabilities like if/unless and depends. See below:
<target name="check" unless="file" description="check that the file property is set" >
<fail message="set file property in the command line (e.g. -Dfile=someval)"/>
</target>
<target name="specifictarget" if="file" depends="check" description=" " >
<echo message="do something ${file}"/>
</target>
You've got the right idea. The
ant specifictarget -Dfile=myfile
sets Ant Properties from the command line. All you really need is
<property name="file" value=""/>
for your default value. That way if file is not specified, it will be equal to the empty string.
Since properties are not mutable in Ant, you can add this:
<property name="file" value="" />
This will set the property file to an empty string if it hasn't already been set on the command line. Then your equality test will work as you intended.
Alternately, you can use escape the value since ant just spits out the actual text when it can't do a property substitution.
<if>
<equals arg1="${file}" arg2="$${file}" />
<then>
<echo>BARF!</echo>
</then>
</if>