Vikram
Vikram

Reputation: 604

How to build WiX so that it copies files based on where .msi is being installed?

I have to build an .msi using WiX which gets deployed to several environments. Each env. has its own config file. Right now, we build one msi per environment and I want to move away from this practice. Is there a way to build just one MSI which is intelligent to copy specific files based on where it is running?

Upvotes: 3

Views: 1000

Answers (3)

Shonn Lyga
Shonn Lyga

Reputation: 109

In my case I declared a property like this:

<Property Id="MACHINE_ENVIRONMENT" Admin="yes" />

From now on, every component has a "Condition" element that is set like:

<Component Id="Log4Net_config" Guid="{666}">
  <Condition>MACHINE_ENVIRONMENT = "dev"</Condition>
  <File Id="Log4Net_config" Source="$(var.dir)\Log4Net.config.$(var.Environment)" Name="Log4Net.config" />
</Component>

Execute the installalation via command line:

msiexec.exe /i "C:\mymsi.msi" MACHINE_ENVIRONMENT="dev"

That way my single MSI contains all the different log4net configuration files, but only installs the relevant one depending on the provided flag.

Upvotes: 1

saschabeaumont
saschabeaumont

Reputation: 22416

The MSI File Table requires specifying the byte size of the file to be installed, to do something based on install source location would require a custom action (which are generally an admission of failure)

See my question Simplest solution to replace a tiny file inside an MSI? - the solution I arrived at is to create an MSI Transform (MST). The original code snippet I posted doesn't work on Windows 7, so here's an update:

Option Explicit

Const MY_CONFIG = "MyConfigApp.xml"
Const CAB_FILE = "config.cab"
Const MSI = "MyApp.msi"

Dim filesys : Set filesys=CreateObject("Scripting.FileSystemObject")

If filesys.FileExists("temp.tmp") Then filesys.DeleteFile("temp.tmp")
filesys.CopyFile MSI, "temp.tmp"

Dim installer, database, database2, view
Set installer = CreateObject("WindowsInstaller.Installer")
Set database = installer.OpenDatabase ("temp.tmp", 1)
Set database2 = installer.OpenDatabase (MSI, 1)

If Not filesys.FileExists(MY_CONFIG) Then WScript.Quit 2 ' No config file, abort!

Dim objFile, size, result, seq, objCab

' MakeCab object has been depreciated so we fallback to makecab.exe for with Windows 7
On Error Resume Next ' Disable error handling, for a moment
Set objCab = CreateObject("MakeCab.MakeCab.1") 
On Error Goto 0  ' Turn error handling back on

If IsObject(objCab) Then ' Object creation successful - use XP method   
    objCab.CreateCab CAB_FILE, False, False, False
    objCab.AddFile MY_CONFIG, filesys.GetFileName(MY_CONFIG)
    objCab.CloseCab
    Set objCab = Nothing
Else ' object creation failed - try Windows 7 method
    Dim WshShell, oExec
    Set WshShell = CreateObject("WScript.Shell")
    Set oExec = WshShell.Exec("makecab " & filesys.GetFileName(MY_CONFIG) & " " & CAB_FILE)
End If

Set objFile = filesys.GetFile(MY_CONFIG)
size = objFile.Size

Set view = database.OpenView ("SELECT LastSequence FROM Media WHERE DiskId = 1")
view.Execute
Set result = view.Fetch
seq = result.StringData(1) + 1 ' Sequence for new configuration file

Set view = database.OpenView ("INSERT INTO Media (DiskId, LastSequence, Cabinet) VALUES ('2', '" & seq & "', '" & CAB_FILE & "')")
view.Execute

Set view = database.OpenView ("UPDATE File SET FileSize = " & size & ", Sequence = " & seq & ", FileName = 'MYC~2.CNF|MyConfigApp.xml' WHERE File = '" & MY_CONFIG & "'")
view.Execute

database.GenerateTransform database2, "CustomConfig.mst"
database.CreateTransformSummaryInfo database2, "CustomConfig.mst", 0, 0
filesys.DeleteFile("temp.tmp")

Set view = nothing
Set installer = nothing
Set database = nothing
Set database2 = nothing
Set filesys = Nothing
WScript.Quit 0

Upvotes: 0

Stephen Connolly
Stephen Connolly

Reputation: 1639

Once you have decided what your target environment actually is, based on whatever characteristics you define, you can create a discrete component for each of the config files you want to deploy per environment, and give each component a condition that evaluates to true only for that target environment, and false otherwise.

If the environment is likely to change, you need also to make the component condition transitive so that a repair\upgrade will deploy the correct config file.

One difficulty you may face is that components are supposed to represent unique resources. but it looks like you probably have lots of different config files all with the same name and destined for the same target folder. You may find it easier to give your config files all different 'pseudo' names and use a CopyFile to copy the pseudo version to its terminal destination.

Upvotes: 6

Related Questions