Reputation: 604
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
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
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
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