Thursday, February 04, 2010

Using Environment Variables in WiX

Today I ran across a challenge with WiX, which took me a couple of hours to resolve. Maybe I can help you saving this time by describing what I tried to achieve and how I resolved it in WiX.
Although our build machine uses a 32 bit operating system in some cases we build certain solutions on a 64 bit OS. One of these solutions includes a WiX project which packages a merge module for the C runtime libraries. On the 32 bit platform merge modules are located in “c:\Program Files\Common\Merge Modules”. In our WiX installer script (a WSX file) we package a merge module with a hardcoded path. This works fine on a 32 bit operating system.
However, it fails on a 64 bit operating system. There Visual Studio 2008 places the merge modules in “c:\Program Files (x86)\Common\Merge Modules”. As a consequence the hard code path doesn’t work.
I’d like to do set this path differently based on an environment variable. I chose “CommonProgramFiles(x86)” which exists on the 64 bit platform but is undefined on a 32 bit OS. Based on information I found at WiX’s web site, my first attempt looked as follows:
    3 <?ifdef $(env.CommonProgramFiles(x86)) ?>
    4 <!-- Variable is defined, we are building on 64 bit -->
    5 <?define commonProgramFiles = "c:\Program Files (x86)\Common\" ?>
    6 <?else?>
    7 <!-- Variable is not defined, we are building on 32 bit -->
    8 <?define commonProgramFiles = "c:\Program Files\Common\" ?>
    9 <?endif?>
Note, that this is not about the computer on which I want to install the software package. This is about the computer on which the installer is created. So the idea is that I would then be able to use the variable ‘commonProgramFiles’ later in the script, e.g. when referencing a file:
    1 <File Source="$(var.commonProgramFiles)Merge Modules\Microsoft_VC90_CRT_x86.msm" >
    2    ...
    3 </File>
Trouble was that it wouldn’t work. With some further digging I found a message in an email list that contained the helpful hint. The relevant quote is:
“Use <?ifdef?> first to see if it's defined. Don't use $() around it in an <?ifdef?>.”
So I rewrote the the WiX script as follows:
    3 <?ifdef env.CommonProgramFiles(x86) ?>
    4 <!-- Variable is defined, we are building on 64 bit -->
    5 <?define commonProgramFiles = "c:\Program Files (x86)\Common\" ?>
    6 <?else?>
    7 <!-- Variable is not defined, we are building on 32 bit -->
    8 <?define commonProgramFiles = "c:\Program Files\Common\" ?>
    9 <?endif?>
Note how line 3 has changed. Now it worked like a breeze! I can now build the installer on both a 32 bit OS and a 64 bit OS and the project will still find the required merge module.
One further note: If you want use any of the WiX tools in a batch file, you may want to use %WIX% instead of a hard coded path to the toolset.
Jason, thank you very much for your help in sorting this out!
P.S. I haven’t forgotten about the mini-series of blogs about WCF-based services I’ve started a few days ago. It’s still coming!

5 comments:

  1. Wouldn't it be easier to copy the files in a local build directory?

    ReplyDelete
  2. Anonymous2:43 AM

    You can refer to other variables when defining a variable (in WiX 3.5 at least) so you don't have to hard-wire the paths:

    ReplyDelete
  3. .peter7:52 PM

    The tip "Don't use $() around it in an " really saved my day thanks!!!

    ReplyDelete
  4. Kristian12:00 AM

    Excellent, I've been having the problems with the ifdef/env combination for a day...

    ReplyDelete
  5. Anonymous12:29 AM

    Should be \Common Files\ not \Common\

    ReplyDelete

All comments, questions and other feedback is much appreciated. Thank you!