I've been asked to jump on a project and help build an installer. I'm normally a hardware guy so my coding experience is limited, especially when it comes to this particular instance.
Essentially I have a bunch of components: exe's, dll's, drivers, etc. that I would like to bundle together into one installer to deliver to the client. A coworker suggested using the Wix toolset to do this.
Most of the material I have found has only covered the very basics and it seems like I'm being asked to do something custom and a bit more nuanced. Any help in the form of some guidance on the most efficient/easiest way to do this would be a huge help. A rough outline of what I'll need to do and/or any examples would go a long way.
Thanks!
If you want to send a bundle of files, you can simply mention those specific files in Product.wxs file of wix setup project .
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="abc.exe" Guid="de34d0c6-3b9e-439c-a254-c9f695e2e389">
<File Id="abc.exe" Name="abc.exe" Source="$(var.abc_TargetDir)abc.exe" />
</Component>
<Component Id="abc.exe.config" Guid="3a38ad30-51ce-42fd-b262-1249c7087111">
<File Id="abc.exe.config" Name="abc.exe.config" Source="$(var.abc_TargetDir)abc.exe.config" />
</Component>
</Fragment>
Like the above abc.exe and the .config file, any other file can be sent using the component tag inside the ComponentGroup tag.
Related
I have a component
<Component Id="ProductComponent" Guid="7935315f-4242-4c7a-a02c-6fd256805356">
<CreateFolder/>
<File
Id="propFile"
Name="aaa.properties"
DiskId="1"
Source="$(var.Project.TargetDir)"
Vital="yes"
KeyPath="yes" ></File>
<?endif?>
</Component>
I want to copy the file just on install , not upgrade.
But I can't find how to do it.
Any idea?
Have you tried using Condition element. I think you can provide a Condition inside Component element to check whether product is already installed or not. If not installed, then create file.
<Component Id="ProductComponent" Guid="7935315f-4242-4c7a-a02c-6fd256805356">
<Condition> NOT Installed </Condition>
<CreateFolder/>
<File
Id="propFile"
Name="aaa.properties"
DiskId="1"
Source="$(var.Project.TargetDir)"
Vital="yes"
KeyPath="yes" ></File>
</Component>
This is a weak spot of MSI (which WiX uses).
MSI installs a file
User modifies the file
MSI goes to install the file. Should it:
a) overwrite and lose user data
b) not overwrite and lose new applciation data
c) merge --- MSI doesn't support this.
If the user data is only one or few attributes there are tricks with custom actions to harvest the user data and reapply it but this is very tricky stuff.
IMO, the best way to approach this is never keep user data in a file installed by the installer. Take app.config appSettings element as an example. It was an atttribute that allows you to extend the file with another file that overrides the settings in the first file. Using this pattern the installer can lay down the app config and the application can create the override file and everything just works because MSI doesn't have to deal with the problem at all.
We use a wixlib that has all the dialogs (most of which can be shared with other products on our portfolio).
One of these dialogs will show a EULA. This EULA is added to the wixlib project as (RTF) content (Build Action: Content, Copy to output: DontNotCopy)
Now next to showing the EULA, I must also install it with all the products.
Can I some how reference the EULA that (has to be?) is embedded in the wixlib?
Or do I need to copy this file to all the wixproj of all products? Given it's a EULA, which won't change that much, it's still a hassle if it does change. I trying to avoid that.
I'm guessing, I need to copy it to all products, but I wanted to double check.
In hindsight, I overthought this. I simply added the file to a component in the wixlib project which can be referenced from the consuming Wix project.
In the wixlib:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<Component Id="C_General_EULA" Directory="INSTALLFOLDER" Guid="{INSERT_GUID}" KeyPath="yes">
<File Id="F_General_EULA" Source="Localization\EULA_en-us.rtf"/>
</Component>
</Fragment>
</Wix>
In the consuming Wix project:
<Feature Id="EULAFeature" Level="1" Display="hidden">
<ComponentRef Id="C_General_EULA"/>
</Feature>
Again, in hindsight really simple.
I am trying to use Wix Toolset 3.10 to install a small app. The thing I am having trouble with it getting it to launch a third-party .exe that is an optional feature.
Here is the related code in my Product.wxs file:
<Feature Id="iCalSetup" Title="Automation" Level="2">
<ComponentRef Id="icalsetup"/>
</Feature>
<Component Id="icalsetup" Guid="*" Directory="PRODUCTFOLDER">
<File Id="icalsetup" Name="foo.exe" Source="$(var.*****.TargetDir)foo.exe" KeyPath="yes"/>
</Component>
I am wrapping the ending msi in a bootstrapper application. to generate a .exe. The feature/selection tree shows the optional components correctly, but the .exe is never executed when it is selected. Please help!
I found a way to accomplish what I was looking to do. The .exe I was using was a self-extracting executable. I extracted it and created component groups for the extracted files. then I put an MsiPackage in the Bootstrapper app. if the component groups are not copied over, then the msi does not run.
I am tasked to build 2 installers based on the same wixlib supplied by 3rd party. Full installer includes all Components, Lite only the core stuff. I assume the wixlib has the following structure:
<Fragment>
<ComponentGroup Id="CoreStuff">
<!-- List of components for this bit, roughly 5MB -->
</ComponentGroup>
</Fragment>
<Fragment>
<ComponentGroup Id="ExtraStuff">
<!-- List of components for this bit, roughly 45MB -->
<ComponentRef Id="BigFile1"/>
</ComponentGroup>
<Component Id="BigFile1">
<File Id="BigFile1_bin" Name="BigFile1.bin"/>
</Component>
</Fragment>
The installer is build in msbuild. The components are included in to the project the following way:
<Feature Id="ThirdPartyStuff">
<ComponentGroupRef Id="CoreStuff"/>
<?if $(var.Configuration) = "Full"?>
<ComponentGroupRef Id="ExtraStuff"/>
<?endif?>
</Feature>
In the end, as expected, I get LGHT0267 error:
error LGHT0267: Found orphaned Component 'BigFile1'. If this is a Product, every Component must have at least one parent Feature. To include a Component in a Module, you must include it directly as a Component element of the Module element or indirectly via ComponentRef, ComponentGroup, or ComponentGroupRef elements.
Have anyone come up with workaround on how to selectively use components provided in wixlib? I could include ExtraStuff as separate Feature for user, but the goal here is to shrink the installer. I could ask politely ThirdParty to provide two separate wixlib's, but I'd like to avoid it.
I don't think that wix has the ability to consume only part of a wixlib package. Think of wixlibs as merge modules (without viewable tables). You can't consume only half of a merge module, and you can't consume only half of a wixlib.
I'm afraid you'll have to ask the third party to break up their wixlib into two packages.
After a struggle I have got the workaround, nasty workaround:
Create dummy wixproject and include wixlib as you would normally do.
Add dummy project as your dependency for final project
In post-build event of dummy project set powershell script to run, which will:
De-compile dummy.msi with binaries output (switch -x) using dark.exe
Craft new.wxs file based on dummy.wxs with inclusion of tags to make the components conditional
Include new.wxs in the final project
I'm using Wix 3.6 beta from the command line, not as VS projects. I have a web application that is harvested with heat as a directory. This works. I'm using web.config transforms to manage each of the target environment web.config files. These are output with msbuild, this works and keeps things visible in Visual Studio and source control.
I've hit a problem deploying one of the several web.config files which I am manually including in product.wxs as components with conditions. I was expecting to include all components as deployable features and let the conditions select just one as active. For example:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Source="$(var.ConfigSourceDir)\setup_a\web.config" />
<Condition>ENVIRON = setup_a</Condition>
</Component>
<Component Id="setup_b" Guid="some_guid" >
<File Source="$(var.ConfigSourceDir)\setup_b\web.config" />
<Condition>ENVIRON = setup_b</Condition>
</Component>
This didn't create any file renaming, moving or deleting issues, but has the very fundamental problem that multiple web.config files are mapped to the same destination and this gives me a light error of "Product.wxs(xxx) : error LGHT0091 : Duplicate symbol 'File:web.config' found. This typically means that an Id is duplicated. Check to make sure all your identifiers of a given type (File, Component, Feature) are unique."
An alternative approach was to use different named .config files and rename/move one to be the web.config, so something like:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Id="setup_a.config" Source="$(var.ConfigSourceDir)\setup_a.config" />
<CopyFile Id="moveit" SourceDirectory="wwwroot" SourceName="setup_a.config" DestinationDirectory="wwwroot" DestinationName="web.config" />
</Component>
This doesn't throw an error, bot the CopyFile command does nothing at all. I just get setup_a.config in the wwwroot folder.
If I nest the CopyFile inside the File, the copy action then works:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Id="setup_a.config" Source="$(var.ConfigSourceDir)\setup_a.config" >
<CopyFile Id="moveit" DestinationName="web.config"/>
</File>
</Component>
...but nested CopyFile means I can't add (it's disallowed) the Delete="yes" attribute to create a 'move' action. Instead I'm left with both setup_a.config and web.config in the wwwroot folder. Alternatively, if I add a seperate removefile within the same component element it also does nothing:
<RemoveFile Id="removefile" On="install" Directory="wwwroot" Name="setup_a.config"/>
</Component>
So, I'm hoping for a working example of how handle multiple web.config files in a conditional deployment, that doesn't leave files behind. the destination filename of web.config is fixed by the framework and can't be changed. The different configs are also pre-generated outside of wix using config transforms, this also can't be changed but the generated filenames could be anything.
cheers!
You complicate it too much. This should work:
<Component Id="setup_a" Guid="some_guid" >
<File Name="web.config" Id="config_a" Source="$(var.ConfigSourceDir)\setup_a\web.config" />
<Condition>ENVIRON = setup_a</Condition>
</Component>
<Component Id="setup_b" Guid="some_guid" >
<File Name="web.config" Id="config_b" Source="$(var.ConfigSourceDir)\setup_b\web.config" />
<Condition>ENVIRON = setup_b</Condition>
</Component>
Pay attention to a couple of things here:
the File/#Name is the same - that's the target file name you'd like to have (web.config)
the File/#Id is different for each File in order to avoid the light error you mentioned first
the File/#Source can be anything - it just describes what file to take as a source
In this sample light will still complain with warning LGHT1076, but that's just a warning - it pays your attention that conditions MUST be mutually exclusive to avoid problems.
I usually take a different approach. Rather then placing multiple mutually exclusive files into an installer that are tightly coupled to specific instances I put a generic file in the installer and use XML changes to transform the XML with the variation point data such as connection string and what not.
This allows me to make installers that can be deployed anywhere silently just by passing a few properties and the command line.