Wixtoolset, specifying source to a file based on where the installer is - wix

How do I specify the source of a file based on where the installer I am running is located. Say for example that I want to place a few files from the folder of the installer(that is for instance located on the desktop or in downloaded files) into a specified path in C/programfiles.
<ComponentGroup Id="ProgramFilesFolder_files" Directory="INSTALLFOLDER">
<Component Id="Program.exe" Guid="d0c868d9-4d5b-41f0-9ce8-d655ac80ee7c">
<File Id="Program.exe" Name="Program.exe" Source="???" />
How do I set the source property?
Have i understood it right that I am supposed to set the source to:
Source="..\Program.exe"
Does this refer to where the MSI file is run from. If I for example put my installer file along with the files I need the source for. Will I be able to use the code above as a relative path that changes when I move the installer. So I can run the installer from elsewhere, with the only requierment that the installer is in the same folder as the files I want to program the source for?

I have an open source project that makes authoring WiX installers easier. One of it's features is relative file paths. You can read about it here.
https://github.com/iswix-llc/iswix-tutorials
Essentially the project templates use an XPI called SourceDir to create an abstraction for where to find the source files. This is relative to the WXS file. The GUI tool uses the location of the WXS and the SourceDir to enumerate the source structure for drag / drop operations and then uses it to author the File elements like such.
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define ComponentRules="OneToOne"?>
<!-- SourceDir instructs IsWiX the location of the directory that contains files for this merge module -->
<?define SourceDir="..\Deploy"?>
<Module Id="DesktopApplicationMM" Language="1033" Version="1.0.0.0">
<Package Id="04cfbb1b-8105-4f3e-9b7a-c1d5354dc670" Manufacturer="DesktopApplicationMM" InstallerVersion="200" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" />
<Directory Id="MergeRedirectFolder">
<Component Id="owc17DECDE7A34AF545285829FF09EF24AE" Guid="4791fdfe-28ff-3c07-2f9e-e2f418c712f8">
<File Id="owf17DECDE7A34AF545285829FF09EF24AE" Source="$(var.SourceDir)\DesktopApplication.exe" KeyPath="yes">
<Shortcut Id="sc06A337B51AED2DF7E22F894A213D2792" Name="Desktop Application" Directory="DesktopFolder" />
</File>
</Component>
</Directory>
</Directory>
<ComponentGroupRef Id="Custom" />
</Module>
</Wix>
If you ever refactor where the files come from you only have one line to update.

Related

WiX 3.11.1 -- error LGHT0103 : The system cannot find the file

WiX 3.11.1 on Windows 10 Pro Version 1703 64-bit. NOT using Visual Studio. Using simple text files as follows.
Here is a test case of the situation I am finding.
myapp.wxs is this:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
<Product Id="*"
Name="MyApp"
Language="1033"
Version="1.0.0.0"
Manufacturer="Me"
UpgradeCode="PUT-GUID-HERE">
<Package InstallerVersion="301"
Compressed="yes"
InstallScope="perMachine"
Manufacturer="Me"
Description="My App"
Keywords=".NET,Installer,MSI" />
<MediaTemplate EmbedCab="yes" />
<UIRef Id="WixUI_Minimal" />
<UIRef Id="WixUI_ErrorProgressText" />
<!-- ****************************************** -->
<WixVariable Id="WixUIDialogBmp"
Value="[CMP_Refresh_file]" />
<!-- ****************************************** -->
<Feature Id="MyFeature"
Title="MyApp Feature"
Description="Installs MyApp"
Level="1">
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="TestFolder" Name="TESTfolder" />
</Directory>
<Component Id="CMP_Refresh_file" Guid="*" Directory="TESTfolder">
<File Id="FILE_RefreshPNG" Source= "Refresh.png" KeyPath="yes" />
</Component>
</Fragment>
</Wix>
myapp.bat is:
"C:\Program Files (x86)\WiX Toolset v3.11\bin\candle.exe" myapp.wxs
"C:\Program Files (x86)\WiX Toolset v3.11\bin\light.exe" myapp.wixobj -ext WixUIExtension -ext WixUtilExtension
#pause
When I run the .bat file, it cannot find the CMP_Refresh_file (error LGHT0103 : The system cannot find the file). The folder TESTfolder is a direct sub-folder of the folder in which the .wxs file exists.
Substituting the full file path solves the issue. BUT that is not what I want as I will have a multitude of files and folders. (Also, if I put the file in the same folder as the .wxs file, of course, it finds it.)
I suspect it is purely a syntactic issue caused by my ignorance. In any case, I have tried endless variations of syntax for the Value of WixUIDialogBmp without joy (except the full name).
Help to resolve very much appreciated. Thanks!
Looking at your WiX code I can see that you are getting confused between the MSI destination and the source folder paths. The Directory tag is to create a folder on the machine where you run the MSI (where you want to deploy your application) - it has nothing to do with the source folders from where you package your files.
Replace your filepath with:
<File Id="FILE_RefreshPNG" Source= "\TESTfolder\Refresh.png" KeyPath="yes"/>
As you can see, the file -> source attribute path should be written with reference to your WXS file path.
If you are planning to deploy the refresh.png underneath the testfolder on the client machines then you got to move to closing </Directory> tag right after your <File> tag
Try <WixVariable Id="WixUIDialogBmp" Value="[#CMP_Refresh_file]" /> which references the installed path. See Formatted Strings for more info.
Came across this while trying to figure out a different issue with source file resolution. Isaiah4110's answer is misleading. The <Directory> structure can specify both the destination on the target machine and where to find the source files as long as the directory structure is the same. You just have to nest your <Component> elements within the corresponding directory elements and specify the Name attribute rather than Source.
In this case, the following should work:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="TestFolder" Name="TESTfolder">
<Component Id="CMP_Refresh_file" Guid="*">
<File Id="FILE_RefreshPNG" Name="Refresh.png" KeyPath="yes" />
</Component>
</Directory>
</Directory>
This works because the Source of a <File> defaults to the source path of the <Component>'s parent <Directory> element (if there is one) plus the Name. See: File element reference and How to specify source files. Unfortunately this logic does not seem to work when placing the <Component> outside of the <Directory> structure as the example in the question does.

How to set the variable -var defined in heat.exe to the wxs file?

I am running the following command:
heat.exe dir bin\Release -sfrag -sreg -var var.sourcebin -dr myappfolder -cg myapp_comp_group
This generates a nice wxs file that looks a bit like this:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<ComponentGroup Id="myapp_comp_group">
<ComponentRef Id="cmpE519314043C2FFE1104E067D33CBC652" />
<ComponentRef Id="cmp0431CFEC47E793CE59C185A8BDD9D865" />
</ComponentGroup>
</Fragment>
<Fragment>
<DirectoryRef Id="myappfolder">
<Directory Id="dir4ADCEBD4F8C9DC384017088D96B7A1C3" Name="somebinfolder">
<Component Id="cmpE519314043C2FFE1104E067D33CBC652" Guid="EB84C1A8-7BF5-4967-878D-8DAD9DFFA0A6">
<File Id="filD8C8D39058B0FF5C9608B1F99B0CD5BA" KeyPath="yes" Source="$(var.sourcebin)\app.config" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
</Wix>
I am using this by heat generated wxs file in my project, but I get an error, because the $(var.sourcebin) has not been set.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define sourcebin="C:\svn\myapp\bin\Release"?>
<!-- leaving some stuff out of course -->
<Module>
<ComponentGroupRef Id="myapp_comp_group"/>
</Module>
</Wix>
error CNDL0150: Undefined preprocessor variable '$(var.sourcebin)'.
How do I define this variable so it is picked up by the heat generated file? I don't want to change the heat generated file manually, because it is re-generated every time.
Open project Properties, Choose Build tab and add preprocessor variable as needed.
sourcebin="C:\svn\myapp\bin\Release";
In the define section use:
<?define sourcebin = $(var.sourcebin)?>
Heat command you can leave as it is.
This will be the same when you will need to pass variables from msbuild (Wixproj file) to other files in your project(wxs,wxi...). Common use when using TFS.

WiX: How do I run heat.exe with a multi-folder setup to get multiple WXS files?

I have a source file setup something like this: C:\Base, and in Base is FolderA, FolderB, FolderC, etc.... I want to run "heat dir" on each sub-folder in Base, so that I get one WXS file containing one ComponentGroup for each folder.
If I run heat (from C:)
heat dir Base\FolderA -cg FolderAGroup -gg -scom -sreg -sfrag -dr -var var.SourceDir INSTALLDIR -out Components-FolderA.wxs
(or 'heat dir FolderA ...' from C:\Base)
(Note that I'll run heat once per FolderA, FolderB, etc... from a script. I don't expect one heat statement to take care of all folders.)
Components-FolderA.wxs looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLDIR">
<Directory Id="dirA..." Name="FolderA">
<Component Id="cmpF..." Guid="{GUID-HERE}">
<File Id="filB..." KeyPath="yes" Source="$(var.SourceDir)\RandomFile.txt" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="FolderAGroup">
<ComponentRef Id="cmpF..." />
</ComponentGroup>
</Fragment>
</Wix>
The problem with this is that Source="$(var.SourceDir)\RandomFile.txt uses whatever I specify as SourceDir. So if I designate C:\Base as SourceDir, the file RandomFile.txt can't be found because it's looking for C:\Base\RandomFile.txt, not C:\Base\FolderA\RandomFile.txt. I can't specify C:\Base\FolderA as SourceDir because FolderB, FolderC, etc... will be wrong. So how do I tell heat to insert 'FolderA\' in between '$(var.SourceDir)\' and 'RandomFile.txt'?
I have looked at Harvesting multiple directories in WiX, but the first answer is too much manual work (we're lookong for almost complete automation) and the second answer puts everything in one file under one ComponentGroup, which would be a maintenance nightmare as we have thousands of files and dozens of folders and sub-folders to be installed.

WiX Relative Path to the Source File

I have a simple Solution for my Project, which works well. But I am unable to grasp how to make the Source paths relative. Can somebody help me?
<Component Id="Bla.exe" Guid="*">
<File Id="Bla.exe" Source="D:\Projects\Bla\Bla\bin\Debug\Bla.exe" KeyPath="yes" Checksum="yes"/>
</Component>
How can I make the Path relative to the Wix Solution? WiX and all necessary files are in the same Solution.
You can use the relative path like so:
<File Id="Bla.exe" Source="..\bin\Debug\Bla.exe" KeyPath="yes" Checksum="yes"/>
OR
You can add a configuration file to your project to define common variables. To do so, add a new "WiX Include" file to your project, call it config.wxi. Then in your include file, you can define a SourceDir variable like so:
<?xml version="1.0" encoding="utf-8"?>
<Include>
<?define SourceDir = "D:\Projects\Bla\Bla\bin\Debug" ?>
</Include>
Now in your .wxs file, you can add a reference to the config file at the top, ex:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include "config.wxi"?>
Then just reference your SourceDir variable like so:
<File Id="Bla.exe" Source="$(var.SourceDir)\Bla.exe" KeyPath="yes" Checksum="yes"/>
Also, there are some built in WiX project variables that you may use.
There are many ways to do this but personally what I like to do is put my application installer projects in different solutions. I build the application solution first and use postbuild commands to publish the content to a deploy folder.
In my installer projects I set $(var.SourceDir)="..\deploy" and then $(var.SourceDir)\foo.exe for a source path.

Wix: Merge Module with multiple wxs files

So, I am trying to create a merge module where I have multiple wxs files. I thought that everything was OK since the build of my project succeeded. Later, I realized that the path used in Source attribute for one of the secondary wxs files was completely wrong but the compiler didn't give the following error:
The system cannot find the file '..\..\release_area\WrongPath\Component2.dll'.
In the secondary wxs file, I added a DirectoryRef element referencing the folder AssemblyFolder in the main wxs file to create the reference between the two files.
Main wxs file:
<Module Id="MyModule" Language="1033" Version="1.0.0.0">
<Package InstallerVersion="200" Platform="x86"/>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="AssemblyFolder">
<Component Id="Component1.dll">
<File Id="Component1.dll" Name="Component1.dll"
KeyPath="yes" Assembly=".net"
Source="..\..\release_area\Comp\Component1.dll" />
</Component>
</Directory>
</Directory>
</Module>
Secondary wxs file:
<Fragment>
<DirectoryRef Id="AssemblyFolder">
<Component Id="Component2.dll">
<File Id="Component2.dll" Name="Component2.dll"
KeyPath="yes" Assembly=".net"
Source="..\..\release_area\WrongPath\Component2.dll" />
</Component>
</DirectoryRef>
</Fragment>
Just to clarify: To test, I copied my <Component Id="Component2.dll"> directly in the main wxs and I got the error: The system cannot find the file '..\..\release_area\WrongPath\Component2.dll'.
So, my guess is that the reference between the secondary wxs file and the main wxs file is completely wrong and that's why the compiler doesn't validate the file path but I cannot figure out what is the problem.
The issue isn't in the compiler (candle.exe). Candle.exe creates the separate "sections" (Module and Fragment are both "sections"). It is the linker (light.exe) that is responsible for pulling it all together. Light.exe starts at the "entry section" (Product, Module or Patch) and follows the references down from there. Since you don't have a reference from your Main.wxs to your Secondary.wxs, the stuff in the Secondary.wxs is never pulled in. To resolve this just add:
<ComponentRef Id="Component2.dll"/>
somewhere under your Module element (I usually put them at the end). If you get lots of Components then ComponentGroup and ComponentGroupRef becomes useful.