I added the -delta switch when building patches with pyro (WiX) recently which seems to improve sizes somewhat. However when comparing diff sizes to those of our old bdiff-based patcher some files still come out much larger than expected. I delved into the source and it seems to be using mspatchc.dll
It appears to be one file in particular that's causing problems: The original and new versions of the file are both around the 100MB mark. Using bdiff generates a diff of 15KB, but pyro -delta uses 18MB!!
Why is this? Is it a bug in WiX? Is there a way i can improve (reduce the size of) the diffs generated by pyro?
candle patch.wxs
light patch.wixobj
melt ..\old\project.msi -out old.wixpdb -pdb old\project.wixpdb -x old_bin
melt ..\new\project.msi -out new.wixpdb -pdb new\project.wixpdb -x new_bin
torch -p -xi old.wixpdb new.wixpdb -out diff.wixmst
pyro -delta patch.wixmsp -out patch.msp -t proj1 diff.wixmst
I don't know if this should be an answer really, this is just an educated guess.
I'm guessing bdiff creates diff files of only the differences of files and has special logic to apply those diffs to the installed files. Alternatively, pyro -delta includes whole file if they are different.
You could test this by including a gigantic text file and then using bdiff after changing one word to get a tiny diff and then using pyro -delta will probably get you a diff basically the size of the text file.
This is more of a work-around, but by using more fragments this issue can be mitigated (at least until you need to patch the file in question). For example:
<Fragment>
<DirectoryRef Id="MyApp" DiskId="1">
<Component Id="MyApp.exe" Guid="*">
<File Id="MyApp.exe" KeyPath="yes" Source="$(var.OutputDir)\MyApp.exe" />
</Component>
<Component Id="Library.dll" Guid="*">
<File Id="Library.dll" KeyPath="yes" Source="$(var.OutputDir)\Library.dll"/>
</Component>
<Component Id="HugeLibrary.dll" Guid="*">
<File Id="HugeLibrary.dll" KeyPath="yes" Source="$(var.OutputDir)\HugeLibrary.dll" />
</Component>
</DirectoryRef>
</Fragment>
If HugeLibrary.dll always creates big deltas, we can move it to it's own fragment which means that it will only be in the patch if it's explicitly coded in:
<Fragment>
<DirectoryRef Id="MyApp" DiskId="1">
<Component Id="MyApp.exe" Guid="*">
<File Id="MyApp.exe" KeyPath="yes" Source="$(var.OutputDir)\MyApp.exe" />
</Component>
<Component Id="Library.dll" Guid="*">
<File Id="Library.dll" KeyPath="yes" Source="$(var.OutputDir)\Library.dll"/>
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<DirectoryRef Id="MyApp" DiskId="1">
<Component Id="HugeLibrary.dll" Guid="*">
<File Id="HugeLibrary.dll" KeyPath="yes" Source="$(var.OutputDir)\HugeLibrary.dll" />
</Component>
</DirectoryRef>
</Fragment>
All files will still be in the installer, and the component group(s) will remain the same.
<Fragment>
<ComponentGroup Id ="myapp">
<ComponentRef Id="MyApp.exe"/>
<ComponentRef Id="Library.dll"/>
<ComponentRef Id="HugeLibrary.dll"/>
</ComponentGroup>
</Fragment>
Related
I have some additional unmanaged dll's that need to be copied to the install folder. I am using WAX as well as it is supposed to make Wix easier to use. I have added the following markup to Product.wxs:
<Component Id="DataModel1.csdl" Guid="f8fb154d-f0c9-40f5-9bcf-593ed9540bda" Directory="INSTALLFOLDER">
<File Id="DataModel1.csdl" Name="DataModel1.csdl" Source="$(var.FLIR_TargetDir)DataModel1.csdl" />
</Component>
<Component Id="DataModel1.ssdl" Guid="a13c3c3b-e6a6-40ea-b9d8-84fd093ca0d5" Directory="INSTALLFOLDER">
<File Id="DataModel1.ssdl" Name="DataModel1.ssdl" Source="$(var.FLIR_TargetDir)DataModel1.ssdl" />
</Component>
<Component Id="DataModel1.msl" Guid="e308d75b-1f0b-4234-843d-6b44af2e80a9" Directory="INSTALLFOLDER">
<File Id="DataModel1.msl" Name="DataModel1.msl" Source="$(var.FLIR_TargetDir)" />
</Component>
<Component Id="Devart.Data.dll" Guid="a95a3053-7d4a-4030-b8c9-9d860a81a221" Directory="INSTALLFOLDER">
<File Id="Devart.Data.dll" Name="Devart.Data.dll" Assembly=".net" KeyPath="yes" Source="C:\Program Files (x86)\Devart\dotConnect\SQLite\Devart.Data.dll" />
</Component>
<Component Id="Devart.Data.SQLite.dll" Guid="16d0ca3c-425d-49e9-a754-043b0b9e4ada" Directory="INSTALLFOLDER">
<File Id="Devart.Data.SQLite.dll" Name="Devart.Data.SQLite.dll" Assembly=".net" KeyPath="yes" Source="C:\Program Files (x86)\Devart\dotConnect\SQLite\Devart.Data.SQLite.dll" />
</Component>
The files shows up as an unmapped file in the unmapped files area of the UI:
When the install happens the files do not get installed into the installation folder. What am I doing wrong?
The Assembly=".NET" tell's MSI to install the files in the Global Assembly Cache. The parent directory element is ignored/overridden. See:
http://wixtoolset.org/documentation/manual/v3/xsd/wix/file.html
If your looking for a tool to make WiX easier to learn/use then I'd suggest my own open source project:
https://github.com/iswix-llc/iswix-tutorials
Some months ago I posted this question about multiple installation, and I strictly followed the example suggested me.
It worked very fine, but I try to remove the default Instance.
When I type
msiexec /x MyInstaller.msi
my App is "logically" removed, but no folders and no files are deleted on my machine!
Here is the code:
<Directory Id="TARGETDIR" Name="SourceDir">
<Component Id="comp_67F76B3C_8D92_4DCF_8C51_42E51502C4A3" Guid="28C71156-F612-49ED-A4E9-0CB598AA84AB" MultiInstance="yes">
<CreateFolder/>
</Component>
<Component Id="comp_AA102B27_0657_498D_9CD5_683C4F33B5E2" Guid="0F601C97-CDFF-4614-A608-B42253240E2C">
<File Id="_C72F9A1F_AF4A_47A3_928C_238643ABA5D4" Name="CrashReportManager.exe" Source="..\workspace\Release\CrashReportManager.exe" KeyPath="yes" />
</Component>
<Component Id="comp_442142FD_D0B8_410C_8904_E73047757FAB" Guid="0A1AE820-0FF7-442C-8333-9FEFA2E3F33C">
<File Id="_CA463C5C_E397_40E6_9B7B_28CCA647D0CA" Name="BCGCBPRO2440d120.dll" Source="..\workspace\Debug\BCGCBPRO2440d120.dll" KeyPath="yes" />
</Component>
<Directory Id="dir_350BD3EA_2F6C_4AD4_A960_8AB2C40F4F36" Name="Docs">
<Component Id="DocFolderId" Guid="32DAE480-7C01-4BAA-B99D-9FE8D7D43369" MultiInstance="yes">
<CreateFolder />
</Component>
<Component Id="comp_FFFDE3FE_1205_4E74_82B1_E832501A096C" Guid="EECC223C-EBAD-43CA-9F3C-4A19FD9E7429">
<File Id="_D0FE1868_D0D7_4778_8BC6_D40FE2B21DB2" Name="ModuleList(1).txt" Source="..\ModuleList(1).txt" KeyPath="yes" />
</Component>
<Component Id="comp_07E32598_CA8D_46B6_A3D6_DA36FD308E1F" Guid="FCCC41AA-17BC-4ADA-925D-631A614C9F80">
<File Id="_894CFDFB_4514_4CAC_86C3_426CC6836B7C" Name="ModuleList(2).txt" Source="..\ModuleList(2).txt" KeyPath="yes" />
</Component>
<Component Id="comp_21465088_A65C_43D4_A038_93412404BA91" Guid="67D18D29-6466-4D36-A279-A9396A62019C">
<File Id="_C9362477_88D8_463C_B77D_7097255F5E13" Name="ModuleList.txt" Source="..\ModuleList.txt" KeyPath="yes" />
</Component>
<Component Id="comp_3C85BB37_5715_45E7_A135_C640D7348360" Guid="6DDB0F88-7346-4221-8FB1-106C5F707EE5">
<File Id="_BFE55263_BD18_427B_9F87_30403BB2540D" Name="ReadMe.txt" Source="..\ReadMe.txt" KeyPath="yes" />
</Component>
</Directory>
</Directory>
What's wrong?
You used KeyPath="yes" in all of your components. This means, that such a component is used as a checkpoint for the Windows installer, for example if you want to repair an installation. It means that it is a mandatory component required for the application to function.
Since you are using multiple instances, and you mentioned you installed more than just the default instance, the Windows installer does not remove key components until the last one of the instances is getting uninstalled (i.e. it keeps track of them internally with a reference counter).
Use MultiInstance="yes" for each and every component, and like Yan says, get rid of the <CreateFolder/> component
I'm trying to generate Wix source from a custom Visual Studio extension. As such, I'd like to be able to (somehow) just add one file (plus project reference) to the Wix Project, and have the new DLLs added in to the Product.
As an example:
<Product Id="*" Name="blah" Version="..." Manufacturer="foo" UpgradeCode="...">
<Package InstallerVersion="200" ... />
<MajorUpgrade DowngradeErrorMessage="..." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="blah" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<!-- Custom actions, Directories, etc .... -->
</Product>
Then in separate files (which I want to be generated), I have some Fragments:
<Fragment>
<DirectoryRef Id="MYINSTALLDIR">
<Component Id="CMP_FILE1" Guid="...">
<File Id="FILE1" Source="file1.dll" Assembly=".net" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
and
<Fragment>
<DirectoryRef Id="MYINSTALLDIR">
<Component Id="CMP_FILE2" Guid="...">
<File Id="FILE2" Source="file2.dll" Assembly=".net" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
So far so good. The problem is that I need to tie those together with something like:
<Fragment>
<ComponentGroup Id="ProductComponents">
<ComponentRef Id="CMP_FILE1" />
<ComponentRef Id="CMP_FILE2" />
</ComponentGroup>
</Fragment>
That works, but I don't want to do that, because it requires editing of the ComponentGroup when I want to add the next file.
So I want to try to localise the information into my added file. I can live with it always being part of the same Feature.
I tried adding the Feature attribute to Component element:
<Fragment>
<DirectoryRef Id="MYINSTALLDIR">
<Component Id="CMP_FILE1" Guid="..." Feature="ProductFeature">
<File Id="FILE1" Source="file1.dll" Assembly=".net" KeyPath="yes" />
</Component>
</DirectoryRef>
</Fragment>
but that didn't seem to add the Component to the parent feature (empty Media table warning from Wix on build, and Orca confirmed it).
I also tried adding the ComponentGroup to each generated file, but of course I can't duplicate Id attributes, and unique Id just pushes the coupling problem up into Feature...
Is there a way to add a Component without editing the ComponentGroup?
No. But you could generate (rather than edit) your ComponentGroup[#Id="ProductComponents"]. The file where it is defined can be "hidden" by generating it into the obj folder and dynamically adding it to the compile. This is effectively what HarvestDirectory and the other targets that call heat do.
While your extension is adding project references and files into the project, it can also add an MSBuild Include that defines a Target with BeforeTargets="Compile". That Target can do the generation and add the generated file to the Compile ItemGroup.
You just have to have a contract that the extension will use a particular ComponentGroup Id for this purpose. (Heat uses unique names for component and file ids to prevent conflicts. I suggest you do that too, especially for "hidden" source files.)
Pretty simple question, suppose my app will be installed as
myApp
+-- bin
+-- lib
I'd like to rename "lib" to "plugins" after installation, how can i do that?
There is a MoveFiles Element that might help, but no idea how to use it.
EDIT:
The problem here is, in my case, source files could be installed into different paths, this scenario described in here.
The only way I can figure out is to create two component groups and install them conditionally. However, using heat to harvest same subdir twice will cause ID conflicting, so I'm thinking to use different paths(e.g. lib and another-lib), and then rename one of the path back after installation, so this question arised...
Don't. Install the files correctly up front. The way that the Windows Installer tracks things will fight you every step of the way. Just install the files in the correct folder from the beginning. Probably not the answer you wanted.
I finally wrote a C++ program to extend heat generated wxs with another directory structure. So we can decide which path to install under different situations. It worked just like changing name during installation.
Here is the wxs file patched by my program. Basically it creates another directory WEBIDR and different subdirs, then adds another component group webGroup for later reference by condition element.
You can do the same thing manually, but if there are thousands of files awaiting, and if they are frequently updating, maybe a program(or script) is a better choice.
<Fragment>
<DirectoryRef Id="INSTALLDIR">
<Directory Id="dirA5528701EE26FFBF346CCE20EE8ACE99" Name="bin">
<Component Id="cmpEBA9C2A32D81BA8646BD1A64DBB39DB1" Guid="{142C531A-C71C-4890-9318-0FC42026C8FC}">
<File Id="filDB56E052EC783676CEF361C0C5AA71F3" KeyPath="yes" Source="$(var.runDir)\bin\boost_date_time-vc100-mt-1_47.dll" />
</Component>
</Directory>
<Directory Id="dir3279BEF4E08D9A00D2F205F325F00A81" Name="modules">
<Component Id="cmpDECCAE13F8937500E4AC367A8EAC95F4" Guid="{85CC0C94-1BFB-4062-BC4E-FBF143921301}">
<File Id="filDD3B40D68D0437B18B1108FBA49ABC1B" KeyPath="yes" Source="$(var.runDir)\modules\HelloAPI.dll" />
</Component>
</Directory>
</DirectoryRef>
<DirectoryRef Id="WEBDIR">
<Component Id="webcmpEBA9C2A32D81BA8646BD1A64DBB39DB1" Guid="{fec110c5-a1a0-4b07-8a35-50f1af84001a}">
<File Id="webfilDB56E052EC783676CEF361C0C5AA71F3" KeyPath="yes" Source="$(var.runDir)\bin\boost_date_time-vc100-mt-1_47.dll" />
</Component>
<Directory Id="webdirpluginF4E08D9A00D2F205F325F00A81" Name="plugins">
<Component Id="webcmpDECCAE13F8937500E4AC367A8EAC95F4" Guid="{3ef79a47-7681-4991-9726-02db38c22f6d}">
<File Id="webfilDD3B40D68D0437B18B1108FBA49ABC1B" KeyPath="yes" Source="$(var.runDir)\modules\HelloAPI.dll" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="runGroup">
<ComponentRef Id="cmpEBA9C2A32D81BA8646BD1A64DBB39DB1" />
<ComponentRef Id="cmpDECCAE13F8937500E4AC367A8EAC95F4" />
</ComponentGroup>
<ComponentGroup Id="webGroup">
<ComponentRef Id="webcmpEBA9C2A32D81BA8646BD1A64DBB39DB1" />
<ComponentRef Id="webcmpDECCAE13F8937500E4AC367A8EAC95F4" />
</ComponentGroup>
</Fragment>
So, I'm trying to use heat to generate fragments for the files in my project. Looking at files I generated in the past, and various WiX articles from around the net, I see that I have Fragments of Directory/Component's and then a Fragment defining a ComponentGroup using ComponentRef's.
<Fragment>
<DirectoryRef Id="dir0D9F6D272B6344EBFCF7EED0D14AA801">
<Component Id="cmp0BA12DCC33EB11DEB149261CF6904AAE" Guid="392C95D2-37C1-464E-9979-3E9D2BF57FEE">
<File Id="filB95C5D2C0575B59E63201E24176D55FB" KeyPath="yes" Source="SourceDir\something.dll" />
</Component>
<Component Id="cmp895927B450519C5487FDEC914051E715" Guid="DDC12130-C5D5-4727-95D8-660F7229BBD7">
<File Id="fil8C5E22B3AFE9AA6056E1458BD93E3646" KeyPath="yes" Source="SourceDir\whatever.dll" />
</Component>
</Fragment>
<Fragment>
<ComponentGroup Id="SampleProduct">
<ComponentRef Id="cmp0BA12DCC33EB11DEB149261CF6904AAE" />
<ComponentRef Id="cmp895927B450519C5487FDEC914051E715" />
</ComponentGroup>
</Fragment>
Isn't Heat supposed to generate this ComponentGroup fragment? Or did I do it manually and forgot I did so? The various Heat examples I find around the web seem to indicate that Heat does generate this fragment.
I'm doing something like: heat dir . -gg -g1 -cg WhatThe -out test.wxs
which produces Fragments of ComponentGroup/Component - nary a ComponentRef to be found:
<Fragment>
<ComponentGroup Id="WhatThe">
<Component Id="cmp9BDA89E67E0D566068C97D3FA3AEBE70" Directory="dir54021DE1C7E3016732B498A5B2680893" Guid="392C95D2-37C1-464E-9979-3E9D2BF57FEE">
<File Id="filDE3AEB1672B47F4803B1EDAB6016DDD3" KeyPath="yes" Source="SourceDir\something.dll" />
</Component>
<Component Id="cmpA8D6F6019E02CAD0DDA17E2F285D7EC8" Directory="dir54021DE1C7E3016732B498A5B2680893" Guid="DDC12130-C5D5-4727-95D8-660F7229BBD7">
<File Id="filDB325609DF363696B3F5E8D1FC22D4C5" KeyPath="yes" Source="SourceDir\whatever.dll" />
</Component>
</ComponentGroup>
</Fragment>
What am I missing? Besides medium-term memory?
Thanks
Adding the -sfrag flag to your heat.exe command line will give you the desired output.
heat dir . -gg -g1 -sfrag -cg WhatThe -out test.wxs