WiX - Force a patch to update an auto-generated file - wix

Newbie to Wix in need of some help with patch making.
In my installer, it uses Heat.exe on an entire folder called "Assemblies" which contains a bunch of .dll files. I want to update one of these .dll files with one that has a higher version number (say from 1.1.0.0 to 4.0.1.0). The command for the heat is done before the installer is built and is below:
<HeatDirectory Directory="..\..\..\ProgressCode\Assemblies" OutputFile="$(ProjectDir)\Generated\Assemblies.wxs" PreprocessorVariable="var.AssembliesPath" DirectoryRefId="Assemblies" AutoGenerateGuids="true" Toolpath="$(WixToolPath)" ComponentGroupName="AssembliesComponentGenerated" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" Transforms="$(SolutionDir)\WixInstaller\RemoveAssembliesXml.xslt" />
These are also stored in the GAC but I have no idea how it does that.
The transform file (RemoveAssembliesXml.xslt) is below:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wi="http://schemas.microsoft.com/wix/2006/wi">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="compsToRemove"
match="wi:Component[(contains(wi:File/#Source,'Assemblies.xml'))]"
use="#Id"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[self::wi:Component or self::wi:ComponentRef] [key('compsToRemove',#Id)]" />
</xsl:stylesheet>
My patch file is pretty simple as I want it to pick up all the differences between the original and transformed msi.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Patch
AllowRemoval="yes"
Manufacturer="Test"
DisplayName="Sample Patch"
Description="Small Update Patch"
Classification="Update"
>
<Media Id="5000" Cabinet="Patchy.cab">
<PatchBaseline Id="Patchy"/>
</Media>
</Patch>
</Wix>
It picks up the new files I want to add but not the changed .dll file. Is there anything specific I need to do to force it to find and update the .dll?

Related

Is bug report XALANC-421 incorrectly listed as fixed in the 1.11.0 release notes?

XALANC-421 (omit-xml-declaration ignored) is supposed to be fixed in Xalan 1.11.0 according to the release notes at https://apache.github.io/xalan-c/releases.html. However, I still observe this issue until Xalan 1.12.0.
Sample files from Ch. 6 of Learning XSLT were used in my testing.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="eu">
<xsl:apply-templates select="member"/>
</xsl:template>
<xsl:template match="member">
<eu-members>
<xsl:apply-templates select="state[#founding]"/>
</eu-members>
</xsl:template>
<xsl:template match="state">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Shortened version of input file
<?xml version="1.0" encoding="UTF-8"?>
<!-- European Union member states and candidate states -->
<eu>
<member>
<state>Austria</state>
<state founding="yes">Belgium</state>
<state>Denmark</state>
</member>
<candidate>
<state>Bulgaria</state>
<state>Cyprus</state>
</candidate>
</eu>
Output still has the xml declaration using xalan 1.11.0
<?xml version="1.0" encoding="UTF-8"?>
<eu-members xmlns:xml="http://www.w3.org/XML/1998/namespace">
<state>Belgium</state>
</eu-members>
Output using xalan 1.12.0
<eu-members>
<state>Belgium</state>
</eu-members>

Can MSBuild Alter its own project file?

I have written a custom task that builds files by creating a parallel file with a different extension.
When MSBuild goes and creates such a file I'd like to add it to the project file itself. I'd also like to nest the built file under the source (with DependentUpon).
Can MSBuild do this? Will it need to reload the project? Can I automate all that?
The following is my .targets file that gets installed by NuGet when my package is added:
<UsingTask TaskName="PreCompiler" AssemblyFile="..\tools\Compiler.dll">
</UsingTask>
<UsingTask TaskName="GenerateDependencies" AssemblyFile="..\tools\Compiler.dll">
</UsingTask>
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
BuildCoffeeFiles;
BuildLessFiles
</BuildDependsOn>
<ContentDir>Content\</ContentDir>
</PropertyGroup>
<ItemGroup Condition="'$(BuildingInsideVisualStudio)'=='true'">
<AvailableItemName Include="CoffeeScript" />
<AvailableItemName Include="Less" />
</ItemGroup>
<Target Name="GenerateCoffeeDependencies">
<GenerateDependencies Include="#(CoffeeScript->'%(FullPath)')">
<Output TaskParameter="Dependencies" ItemName="InputCoffeeFiles"/>
</GenerateDependencies>
<ItemGroup>
<InputCoffeeFiles
Include="#(InputCoffeeFiles->Distinct())"
KeepDuplicates='false' />
</ItemGroup>
</Target>
<Target Name="BuildCoffeeFiles"
DependsOnTargets="GenerateCoffeeDependencies"
Inputs="#(InputCoffeeFiles)"
Outputs="#(CoffeeScript->'%(RelativeDir)%(Filename).js')">
<PreCompiler Include="#(CoffeeScript->'%(FullPath)')" />
</Target>
<Target Name="GenerateLessDependencies">
<GenerateDependencies Include="#(Less->'%(FullPath)')">
<Output TaskParameter="Dependencies" ItemName="InputLessFiles"/>
</GenerateDependencies>
<ItemGroup>
<InputLessFiles
Include="#(InputLessFiles->Distinct())"
KeepDuplicates='false' />
</ItemGroup>
</Target>
<Target Name="BuildLessFiles"
DependsOnTargets="GenerateLessDependencies"
Inputs="#(InputLessFiles)"
Outputs="#(Less->'%(RelativeDir)%(Filename).css')">
<PreCompiler Include="#(Less->'%(FullPath)')" />
</Target>
You can apply a XslTransformation msbuild task in your project, before you build it, adding the required file.
This is the sample transformation file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="http://schemas.microsoft.com/developer/msbuild/2003">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Identity. -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="//ms:Compile[#Include='Program.cs']">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
<xsl:element name="DependentUpon"
namespace="http://schemas.microsoft.com/developer/msbuild/2003">
<xsl:text>Output.cs</xsl:text>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
And here is the msbuild target sample file:
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="Build">
<XslTransformation
XmlInputPaths="ConsoleApplication1.csproj"
OutputPaths="ConsoleApplication1.transformed.csproj"
XslInputPath="proj.xslt">
</XslTransformation>
<MSBuild Projects="ConsoleApplication1.transformed.csproj" Targets="Build" />
</Target>
</Project>
You will need to build with .NET 4.0 at least. For this specify the ToolsVersion in msbuild file.

Harvesting multiple directories in WiX

I'm trying to build an installer that includes a number of features and I'm using heat to harvest a directory of files for each feature.
My source directory structure looks something like this:
HarvestDir
\FeatureA
\FeatureImpl.dll
\FeatureImpl2.dll
\FeatureB
\FeatureImpl.dll
\FeatureImpl2.dll
So I execute heat.exe for each feature to create a fragment for each feature but I get basically identical fragments e.g.
[...] Source="SourceDir\FeatureImpl.dll"
[...] Source="SourceDir\FeatureImpl2.dll"
What I really want is something like this:
[...] Source="SourceDir\FeatureA\FeatureImpl.dll"
[...] Source="SourceDir\FeatureA\FeatureImpl2.dll"
and
[...] Source="SourceDir\FeatureB\FeatureImpl.dll"
[...] Source="SourceDir\FeatureB\FeatureImpl2.dll"
I could use -var to specify a separate variable to represent the source location for each feature, but then I'd have to pass values for these variables into the wixproj (and I'm going to have ~10 features).
So, is there any way I can include a relative path in my harvested fragment?
Rather than using heat on each Feature* folder, you could just harvest the whole feature layout folder that you have created and have heat transform the results. I'm assuming that each subfolder is a feature. It's a simple matter of creating a ComponentGroup for each one that references all the components under it.
You'd then use the component groups like this:
<Feature Id="FeatureA">
<ComponentGroupRef Id="FeatureA"/>
</Feature>
<Feature Id="FeatureB">
<ComponentGroupRef Id="FeatureB"/>
</Feature>
Heat command (I actually use the HarvestDirectory target in WiX 3.7):
Heat.exe dir FeatureLayout -dr FeatureLayoutDir -srd -ag -sfrag -t FeatureLayout.xslt -out obj\Debug\__dir.wxs
FeatureLayout.xslt:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="wix"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="wix:Wix">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<xsl:for-each select="wix:Fragment/wix:DirectoryRef/wix:Directory">
<wix:Fragment>
<wix:ComponentGroup Id="{#Name}">
<xsl:for-each select="descendant::wix:Component">
<wix:ComponentRef Id="{#Id}"/>
</xsl:for-each>
</wix:ComponentGroup>
</wix:Fragment>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You can either assign your source paths in .wixproj in DefineConstants or you can assign them in a WIX include file but either way you will have to use "var" option to specify the variable being used for your sources.
If you want to use DefineConstant in wixproj then you will have to do something like
<Target Name="BeforeBuild">
<PropertyGroup>
<DefineConstants>BINFOLDER=..\PATH\TO\SOURCE\$(Configuration)</DefineConstants>
</PropertyGroup>
<HeatDirectory OutputFile="output.wxs" Directory="..\PATH\TO\SOURCE\$(Configuration)" DirectoryRefId="INSTALLFOLDER" ComponentGroupName="COMPONENTGROUPNAME" ToolPath="$(WixToolPath)" PreprocessorVariable="var.BINFOLDER" />
</Target>
In Heat task make sure you add any additional attributes that you might need. In case you have added a reference to your source project in the .wixproj then you may directly use the project reference variables in the "PreprocessorVariable" attribute. For example instead of var.BINFOLDER use var.MyProject.TargetDir.
If you want to use a WIX include file (.wxi) then declare your variables in an include file for example Variables.wxi:
<?xml version="1.0" encoding="UTF-8"?>
<Include>
<?define PATH1="PATH\TO\SOURCE"?>
<?define PATH2="$(var.PATH1)\$(Configuration)"?>
</Include>
Now you will need to pass in PATH1 and PATH2 using the -var in the heat command line. Once you have your .wxs file you will then need to include the Variables.wxi file in that using
<?include Variables.wxi ?>
in your .wxs. The problem with this way is that you will have to either add this include directive manually each time you generate your WIX source files or you will have to inject it using XSL Transformation.
hit properties for the setup project, this is the command i'm using:
"%WIX%\bin\heat.exe" dir "$(SolutionDir)\Export\Release" -suid -dr bin -srd -cg ExportComponentGroup -var var.sourcePath -ag -sreg -out "$(SolutionDir)\SetupProject\AppExportDir.wxs"
the only thing else you need to define in the build tab is:
check the check box for "define 'debug'preprocessor variable" and enter.
sourcePath=%SystemDrive%\App\Main\Export\Release\
this please make sure that you use the flags that you want like suid or -dr
for more information about the flags you have see here:
http://wix.sourceforge.net/manual-wix3/heat.htm

WiX: installing and starting a service, when the file elements are generated by heat.exe

I have a WiX installer to install and start a service. However, all the examples I find place the ServiceInstall tag directly below the file tag for the .exe file to be installed as a service.
I can’t do this as I am using heat to generate my file elements in a separate file. So my wix script looks like this:
<Directory Id="INSTALLLOCATION" Name="Email Generation Service">
<Component Id="SetupService" Guid="51E78696-80E0-4CDA-8F49-902C67CB129C">
<CreateFolder />
<ServiceInstall Id="ServiceInstaller"
Type="ownProcess"
Vital="yes"
Name="EmailGenerationService"
DisplayName="Email Generation Service"
Description="Service for generating Emails from Nexus"
Start="auto"
Account="LocalService"
ErrorControl="ignore"
Interactive="no">
</ServiceInstall>
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="EmailGenerationService" Wait="yes" />
</Component>
</Directory>
How can I tell WiX which file I want to install as a service?
I have used XSLT to set the KeyPath on all files to no, with the exception of the file I want to install, despite the fact that all files are in their own component. I am at a bit of a loss now :(
A service must be linked to a specific file. This is a Windows Installer limitation. So one way or another you need to create the ServiceInstall element under your EXE file element.
A solution would be to hard-code the EXE file instead of letting it be generated automatically.
Adding this as relevant information, I had a similar problem and used a similar solution to you, adding an xml transform. However, I used the transform to insert the service control/install elements into the heat-generated fragment. I've pasted my transform below, you may have to modify properties or remove items you don't need.
Some things to note:
The service control doesn't auto-start the service on install, I had
issues with assembly refs not being populated by the time the msi tried to invoke them
A key part of the XSLT adds
an "include" statement at the top of the heat.exe file so that I can
reference variables from my .wxi file
This assumes the variable name
you told heat to inject as your -var parameter is "var.SourcePath"
I never extracted the name of the .exe file or settings file out as
variables that can be injected into the transform because they were
fairly stable and it seemed...difficult
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
xmlns="http://schemas.microsoft.com/wix/2006/wi">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Template for the new ServiceInstall element -->
<xsl:param name="pServiceInstall">
<xsl:element name="ServiceInstall">
<xsl:attribute name="Id">SVINSTL_$(var.ServiceName)</xsl:attribute>
<xsl:attribute name="Description">$(var.ServiceInstallDescription)</xsl:attribute>
<xsl:attribute name="Account">$(var.SystemAccount)</xsl:attribute>
<xsl:attribute name="DisplayName">$(var.ServiceInstallDisplayName)</xsl:attribute>
<xsl:attribute name="ErrorControl">normal</xsl:attribute>
<xsl:attribute name="Name">$(var.ServiceName)</xsl:attribute>
<xsl:attribute name="Interactive">no</xsl:attribute>
<xsl:attribute name="Start">auto</xsl:attribute>
<xsl:attribute name="Type">ownProcess</xsl:attribute>
<xsl:attribute name="Vital">yes</xsl:attribute>
</xsl:element>
</xsl:param>
<!-- Template for the new ServiceControl element -->
<xsl:param name="pServiceControl">
<xsl:element name="ServiceControl">
<xsl:attribute name="Id">SVCTRL_$(var.ServiceName)</xsl:attribute>
<xsl:attribute name="Name">$(var.ServiceName)</xsl:attribute>
<xsl:attribute name="Stop">both</xsl:attribute>
<xsl:attribute name="Remove">uninstall</xsl:attribute>
<xsl:attribute name="Wait">yes</xsl:attribute>
</xsl:element>
</xsl:param>
<!-- Insert a ?include statement at the top of the fragment so it can use config variables -->
<xsl:template match="wix:Wix">
<xsl:copy>
<xsl:processing-instruction name="include">InstallerSettings.wxi</xsl:processing-instruction>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- Turn the file name of the executable into a variable so it can be targeted for shortcuts,etc -->
<xsl:template match="#Source[. ='$(var.SourcePath)\HARD_CODED_NAME_OF_PROJECT.exe']">
<xsl:call-template name="identity" />
<xsl:attribute name="Id">$(var.ServiceExecutableFileId)</xsl:attribute>
</xsl:template>
<!-- Insert the ServiceInstall and ServiceControl elements into the component with the exe file -->
<xsl:template match="//wix:File[#Source='$(var.SourcePath)\HARD_CODED_NAME_OF_PROJECT.exe']">
<xsl:call-template name="identity" />
<xsl:copy-of select="$pServiceInstall"/>
<xsl:copy-of select="$pServiceControl"/>
</xsl:template>
<!-- Identity template (copies everything as is) -->
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Customize KeyPath item when using wix heat.exe to harvest multiple files

I have a lot files to harvest in a per user install project in wix.
I used heat.exe to harvest the file, but each file in one component has its own keypath property, while my files will copy to "app data" so it has to use a registry key under HKCU as its KeyPath, so I have to change each item in the XML file.
Can it be done by heat.exe? I have thousands of files to harvest, it is terrible to fix it manually.
Use this xslt to customize KeyPath item for nodes that have child nodes.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
xmlns:my="my:my">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match='wix:Wix/wix:Fragment/wix:ComponentGroup/wix:Component'>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="KeyPath">
<xsl:text>no</xsl:text>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
derived from #KirillPolishchuk 's answer https://stackoverflow.com/a/8035049/483588
As far as I know, heat doesn't support this out-of-the-box. However, you can apply an XSL template to the heat output and tweak the final wxs file the way you'd like. See -t: switch of heat.exe for more details.