So here's the basic setup. I have an existing WIX project that builds a bunch of individual fragments in to a larger MSI. I'm trying to change the project around to allow you to select individual pieces to install. The program I've run in to is that when I run heat on the smaller directories to create the individual components, the Source path isn't correct. I'll give an example as hopefully that will make more sense.
So I have basic folder structure like this:
C:\ProjDir\Foo\Bar1
C:\ProjDir\Foo\Bar2
I used to a command to simply harvest C:\Foo (Heat.exe dir Foo -dr FOO_DIR_REF -out File.wxs), and now I've changed it to to harvest each individual Bar folder (Heat.exe dir Foo\Bar1 -dr BAR1_DIR_REF -out File1.wxs) and (Heat.exe dir Foo\Bar2 -dr BAR2_DIR_REF -out File2.wxs). The problem I'm having is that the output of the harvest looks like this:
<Component Id="cmpblablabla" Guid="{stuff-here}">
<File Id="filblabla" KeyPath="yes" Source="SourceDir\Bar1\file.here" />
</Component>
And when trying to build the msi it complains because it can't find SourceDir\Bar1. Basically what I need is a way to make it look something like this:
<Component Id="cmpblablabla" Guid="{stuff-here}">
<File Id="filblabla" KeyPath="yes" Source="SourceDir\Foo\Bar1\file.here" />
</Component>
This seems like a very simple problem, that I'm sure is easily done, but all the searching I've done has not come up with anything useful.
Note that light will search additional SourceDir's for your file if you add them to the search path with -b
e.g.
light.exe -b Foo ...
It should be
<Component Id="cmpblablabla" Guid="{stuff-here}">
<File Id="filblabla" KeyPath="yes" Source="$(var.ProjectName.TargetPath)\Bar1\file.here" />
</Component>
Different properties available are
$(var.ProjectName.TargetPath)
$(var.ProjectName.ProjectDir)
The answer to your question is all in heat.exe help text. :-)
In order to end up with correct directory harvesting, point the heat to your root directory (Foo), and specify the -srd switch in the command line. As the help text states, this will omit root dir harvesting and you'll most likely end up with what you need.
For even more flexibility, you can specify -var switch providing the WiX variable which is to replace the SourceDir explicit statement. Again, just run heat.exe and look through the output - you'll find enough info and examples.
Related
I am new to Wix Toolkit. I am creating an .msi for a console application. Below is my one of my line which is generated using heat command :
<Component Id="cmp2B116558C64AF876AD223372FA4B8DFF" Directory="dir99DE416F55C8960850D5A4FCA3758AD4" Guid="PUT-GUID-HERE">
<File Id="fil19E0EEE3E5D0208523116CAFE9E9B06E" KeyPath="yes" Source="$(var.SourceDir)\App.config" />
</Component>
I just need that PUT-GUID-HERE to be get set automatically. Is there anything that i need to change in my heat command. Below is my heat command which i am using.
heat dir "C:\Users\rp5026921\Documents\Project\Automation Testing\SourceCode\MeridianAutomation\bin\Release" -cg ComponentsGroup -out "C:\Users\rp5026921\Documents\Project\Automation Testing\SourceCode\MeridianAutomationSetUp\MeridianAutomationComponents.wxs" –gg
One more issue is that, i am having app.config in my console application. In that i need to change certain values after installing the .msi. But after installation when i go inside the folder its not allowing me to edit the app.config file.
You are using the right command line option but just move the "-gg" before the "-out" command line option as shown below.
-gg : Generate guids now. All components are given a guid when heat is run.
-ag : Auto generate component guids at compile time, e.g. set Guid="*".
heat dir "C:\Users\rp5026921\Documents\Project\Automation Testing\SourceCode\MeridianAutomation\bin\Release" -cg ComponentsGroup -gg -out "C:\Users\rp5026921\Documents\Project\Automation Testing\SourceCode\MeridianAutomationSetUp\MeridianAutomationComponents.wxs"
I have a Wix (3.8) project that is relatively generic in that we are attempting to produce multiple Demo installers using a variety of tools such as the preprocessor to change the name, include different files and options. I also modify the ProductId/ProductCode/UpgradeCode for each so that they appear as 2 different products and install side by side. Installing and uninstalling one alone works fine. When I install 2 side-by-side and uninstall one there are several abandoned resources one of which is the shortcuts.
Here is the code I am using for the shortcuts:
<DirectoryRef Id="DesktopFolder">
<Component Id="DesktopShortcuts"
Guid="*">
<Shortcut Id="ApplicationDesktopShortcut"
Name="!(bind.property.ProductName)"
Description="Demo Application"
Target="[INSTALLDIR]Demo.exe"
WorkingDirectory="INSTALLDIR"
Advertise="no"/>
<RegistryKey Root="HKMU"
Key="SOFTWARE\$(var.Manufacturer)\!(bind.property.ProductCode)\DesktopShortcuts">
<RegistryValue Name="Installed"
Type="integer"
Value="1"
KeyPath="yes" />
</RegistryKey>
</Component>
</DirectoryRef>
After reviewing the installer logs I note that the ComponentId for the shortcuts is always the same which explains the abandoned resources (shortcuts).
I was under the impression that because the Path to my registry value was different for each installer (note that I bind the product code into the key) I would get a different Guid for each installer as a result of the Guid="*" attribute. However, the wix documenation is unclear WRT Registry keys as are other discussions I located. Apparently this isn't an issue with files that are in separate directories.
Such as this and the Wix docs.
So I figured out what is going on. It's all got to do with some subtle issues on this line:
Key="SOFTWARE\$(var.Manufacturer)\!(bind.property.ProductCode)\DesktopShortcuts">
As noted in the question the documentation and references suggest that the Path for files and registry entries are used to dynamically generate the Guid. Note the path includes the file/Value Name. This is in fact happening but how was I getting the same Guid for the ComponentId for 2 different installers using 2 different ProductCodes? You would think that the inclusion of the ProductCode into the path would make each unique. Well, turns out it's because I was using !(bind.property.ProductCode) in the path. Binder variables don't get substituted until the Linking phase with light. The Guid="*" element is generated in the Compiling phase with Candle which is obviously before the linker. So while the substitution was occurring and the proper keys were being generated in the registry, the Guid was being generated from a non unique path:
SOFTWARE\MyCompany, Inc\!(bind.property.ProductCode)\DesktopShortcuts\Installed
So don't use binder variables in any KeyPath element when using the auto generated Guid functionality.
So I am using Wix to install my application, it works great and does everything that I need. The one issue I am having now is this. I am using the following command line argument in my VS 2010 Pre-build Event command:
heat.exe dir "C:\My Main Folder" -cg MyApplication -gg -scom -sreg -sfrag -dr APPLICATIONROOTDIRECTORY -var var.MyApplication -out "..\..\MyApplication.wxs"
My issue is that the .wxs file that is generated gives me the following folder structure:
<Fragment>
<DirectoryRef Id="APPLICATIONROOTDIRECTORY">
<Directory Id="dirB19DCB4311BD6C765579FE56A2C72DF8" Name="My Main Folder">
My question is if there is a way to pass in an argument that could change the name of the "My Main Folder" directory, because as of right now I am changing it manually but this is extremely inefficant and makes it so that we cannot have an automated build process. If anyone has any suggestions or places I could look that would be great! Right now I am leveraging the "WiX: A Developer's Guid to Windows Installer XML" book to try and find a solution but am not finding anything.
Depending on exactly what you are aiming to do, I would be tempted to use -srd to suppress the generation of the root directory and then change the -dr parameter to point at the directory with the correct name.
Other than that, I think the only other simple option is probably an XSL transform, which you can use by specifying the -t parameter.
As ChrisPatrick suggested you can use the -srd to suppress the generation of the root directory and use your own with -dr.
heat.exe dir "C:\My Main Folder" -srd -dr "DIRNAMEHERE"
You can find more information here, heat documentation
In my Wix, I have lots of files included in this way:
<Component Id="mycomponent" Guid="*" Feature="Core" >
<Condition>$(var.Include) = 1</Condition>
<File Id="mycomponent.file" KeyPath="yes"
Source="$(var.BinDir)\mycomponent.file" />
</Component>
So I can pass in a different value of var.Include to generate packages for different environment.
While the resulting packages do seem to work, however I noticed the size of the packages are always quite big even when I set it to not include these components. It looks as if WiX is always including all components in building the msi, and only chose to not install these components when the package was build with var.Include = 0...
Is this a normal behavior?
The condition element is used to determine whether a component gets installed not whether it gets included in the build or not. Also be sure not to confuse Windows Installer properties used in Conditional Statements and preprocessor variables / statements. Two different beasts.
You can confirm by opening your MSI output file using some File compressing/uncompressing software such as 7zip and open the package.cab file inside the opened MSI file. and check whether your files with the id like "mycomponent" is present there or not.
I hope it is expected since it is dependent on the variable and that can be something which even can be set from install command call as install property.
UPDATE: You can amend the WIX like below by using Preprocessor statements, so it can exclude these optional component from the resulting msi
<?if $(env.MySku) = Enterprise ?>
<Component Id="mycomponent" Guid="*" Feature="Core">
<Condition>$(var.Include) = 1</Condition>
<File Id="mycomponent.file" KeyPath="yes" Source="$(var.BinDir)\mycomponent.file" />
</Component>
<?endif ?>
As #RinoTom and #Christopher point out, install-time selection (Condition tag) is very different from build-time selection (?if meta-tag). To be selectable at install time, the included components must be in the .msi . The advantage of this approach is that you can set the properties that determine their conditions, not only at build time, but at install time as well via dialogs or AppSearch.
But what you're asking for is multiple package builds, each tailored to a specific set of conditions, selected at build time. An alternative that might work for you is to define each of the optional components as a Fragment in a separate file. Then for each package configuration, compile only the fragments you want in it:
del /q *.wixobj
candle main_package.wxs
for %%f in (optional_1.wxs optional_5.wxs optional_27.wxs) do candle %%f
light *.wixobj -out tailored_package_A.msi
Since only those fragments you wanted included have been compiled to .wixobj, only they appear in the output package. This scales particularly nicely if you have some components that are always present, but only a handful that are optional.
The following doesn't compile, but how can I get the equivalent functionality of [MYDIR]?
<Component Id="MyComponent" Guid="MY_GUID" KeyPath="yes" Directory="[MYDIR]">
<File Source="MyFile.dll" Name="MyFile.dll"/>
</Component>
(I'm trying to put the file MyFile.dll into a directory whose path is determined when the installer is actually run.)
The Directory attribute must correspond to a <Directory> tag somewhere in your installer. You can set that directory to the value of a property that gets set at runtime. A good example of this is using the WixUI_InstallDir to ask the user where they want to install an application.
Here is an example of usage: https://wixtoolset.org//documentation/manual/v3/wixui/dialog_reference/wixui_installdir.html