6

I have this WIX command that uses all invariant paths and it doesn't need a system environment (unlike this example http://weblogs.sqlteam.com/mladenp/archive/2010/02/23/WiX-3-Tutorial-Generating-filedirectory-fragments-with-Heat.exe.aspx):

"%wix%bin\heat.exe" dir "$(SolutionDir)Web\obj\$(Configuration)\Package" 
                    -cg PACKAGEFILES -gg -g1 -sreg -srd -dr DEPLOYFOLDER 
                    -var wix.PackageSource="$(SolutionDir)Web\obj\$(Configuration)\Package"
                    -out "$(SolutionDir)WebInstaller\PackageFragment.wxs"

It works great, except on our build server where the solution path has a space in it and this error is thrown:

heat.exe error HEAT5057: The switch '-var' does not allow the spaces from the value. Please remove the spaces in from the value: wix.PackageSource=C:\Build\Builds 1\26e27895ae75b7cb\CADPortal\src\trunk\Web\obj\Debug\Package

I can't change the path and it shouldn't be necessary anyway in my opinion.

My question is: How do I solve this? (I don't even get why WIX is making trouble over a quoted path/string var with a space)

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
Martin Clemens Bloch
  • 1,047
  • 1
  • 12
  • 28

5 Answers5

8

To include a variable and its definition using heat, use the following mechanism.

  1. Create an include (myinclude.wxi) file where you define your variable and its value:
<?xml version="1.0" encoding="utf-8"?>
<Include> 
  <?define PackageSource="c:\somePath"?>
</Include>
  1. Create an xsl file (mytransform.xsl) for adding the <?include myinclude.wxi?> to the wxs file:
<?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">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <xsl:template match="wix:Wix">
    <xsl:copy>
      <xsl:processing-instruction name="include">myInclude.wxi</xsl:processing-instruction>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <!-- Identity transform. -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>
  1. Run heat and specify the -t parameter that points to the transform:

    heat.exe dir "c:\somePath" -cg PACKAGEFILES -gg -g1 -sreg -srd -dr DEPLOYFOLDER -var PackageSource -t mytransform.xsl -out PackageFragment.wxs

This will create the PackageFragment.wxs file as intended, add the include statement using the xsl transform, and use the variable value from the wxi file when compiling the msi (using candle later on).

JYelton
  • 35,664
  • 27
  • 132
  • 191
Dan
  • 778
  • 7
  • 18
  • Kudos to Peter Shirtcliffe for the xsl syntax http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Transform-output-of-heat-to-insert-an-include-statement-td7355923.html#a7364854 – Dan Apr 28 '15 at 21:49
  • This helped me out a lot – Giridhar Karnik Oct 27 '16 at 08:57
  • Saved the day...as I switch from a hard coded list of component files to a heat.exe generated list of component files. Thank you! – granadaCoder Mar 10 '17 at 22:05
5

For this cases you can define a prepocessor variable in the Build section of the project properties, in your case PackageSource=Web\obj\$(Configuration)\Package

and reference in the heat call like

"%wix%bin\heat.exe" dir "$(SolutionDir)Web\obj\$(Configuration)\Package"
                   -cg PACKAGEFILES -gg -g1 -sreg -srd -dr DEPLOYFOLDER
                   -var var.PackageSource
                   -out "$(SolutionDir)WebInstaller\PackageFragment.wxs"
Narkha
  • 1,197
  • 2
  • 12
  • 30
3

The -var switch provides the name of a preprocessor variable. Something like var.Foo. Preprocessor variable names cannot contain spaces in them. The value wix.PackageSource=Whatever SolutionDir Expands To\Web\obj\Whatever Configuration Expands To\Package is not a valid name for a preprocessor variable because it has spaces in it. I expect the backslashes will be a problem as well.

Rob Mensching
  • 33,834
  • 5
  • 90
  • 130
  • So, pass `-var var.PackageSource` to heat. It just writes `var.PackageSource` into the output, which has to be resolved later. You would bind it to a value in other commands or .wxs files. – Tom Blodget May 28 '13 at 17:02
  • Could you describe how to do that? I have tried passing a var to Heat and then defined this var in another WXS file that includes Heat file/references ComponentGroup in the Heat generated file. The heat file doesn't seem to be actually included. It doesn't work, only if I disable Heat and manually put in the var definition into the Heat generated file. – Martin Clemens Bloch May 29 '13 at 07:14
2

So I ended up coding some build event code that inserts the necessary definition into the Heat generated file at the top, but under the starting WIX tag. Personally I'm starting to question the power of WIX if you need to do this kind of shenanigans/hacks.

Anyway this is my full build event code for anyone that needs it. (It also finds an invariant path for MSBuild.exe and creates a web package.)

echo off

set THEME_REGKEY=HKLM\Software\Microsoft\MSBuild\4.0
set THEME_REGVAL=MSBuildOverrideTasksPath

REM Check for presence of key first.
reg query %THEME_REGKEY% /v %THEME_REGVAL% 2>nul || (echo No theme name present! & exit /b 1)

REM query the value. pipe it through findstr in order to find the matching line that has the value. only grab token 3 and the remainder of the line. %%b is what we are interested in here.
set THEME_NAME=
for /f "tokens=2,*" %%a in ('reg query %THEME_REGKEY% /v %THEME_REGVAL% ^| findstr %THEME_REGVAL%') do (
    set THEME_NAME=%%b
)

REM Possibly no value set
if not defined THEME_NAME (echo No theme name present! & exit /b 1)

REM replace any spaces with +
set THEME_NAME=%THEME_NAME: =+%

if errorlevel 1 goto BuildEventFailed
%THEME_NAME%MSBuild  "$(SolutionDir)Web\Web.csproj" /t:Build;Package /p:Configuration=$(Configuration)
if errorlevel 1 goto BuildEventFailed

"%wix%bin\heat.exe" dir "$(SolutionDir)Web\obj\$(Configuration)\Package" -cg PACKAGEFILES -gg -g1 -sreg -srd -dr DEPLOYFOLDER -var var.PackageSource -out "$(SolutionDir)WebInstaller\PackageFragment.wxs"

REM FUNC "HeatFix" - This inserts the var. definition in top of the heat generated fragment:
 MOVE /Y "$(SolutionDir)WebInstaller\PackageFragment.wxs" temp.txt
(
  FOR /F "tokens=*" %%A IN (temp.txt) DO (
    ECHO %%A
    IF "%%A" EQU "<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">" (
      ECHO ^<^?define PackageSource^=^"$(SolutionDir)Web\obj\$(Configuration)\Package^"^?^>
    )
  )
) > "$(SolutionDir)WebInstaller\temp.txt"
move /Y "$(SolutionDir)WebInstaller\temp.txt" "$(SolutionDir)WebInstaller\PackageFragment.wxs"
REM END FUNC "HeatFix"

goto BuildEventOK
:BuildEventFailed
echo POSTBUILDSTEP for $(ProjectName) FAILED
exit 1
:BuildEventOK
echo POSTBUILDSTEP for $(ProjectName) COMPLETED OK
Martin Clemens Bloch
  • 1,047
  • 1
  • 12
  • 28
0

I just ran into this issue and solved it by executing in the Solution Folder and using a relative path.

This works because I happen to be running heat within a PowerShell script that Jenkins is executing.

Based on the following assumptions:

  • Executing a PowerShell script is kosher with your build method
  • The PowerShell script lives inside a folder in the Solution Directory

The Question Asker's script might look like the following:

$SolutionDir = (get-item $PSScriptRoot).Parent.FullName
Set-Location $SolutionDir
& $env:WIX\bin\heat.exe" dir "$SolutionDir\Web\obj\$(Configuration)\Package" 
    -cg PACKAGEFILES -gg -g1 -sreg -srd -dr DEPLOYFOLDER 
    -var wix.PackageSource=".\Web\obj\$(Configuration)\Package"
    -out "$SolutionDir\WebInstaller\PackageFragment.wxs"