I've got one customer specific file added to my WiX project like that:
<Component Id="IMPORT" DiskId="1" Guid="xxxxx">
<File Id="IMPORT" Name="Import.xml" Source="..\..\config_customerA\Import.xml"/>
</Component>
The rest of the file is the same for all customers.
Right now I have one Wix project for each customer. The project Wix file is all the same, only the above lines are changed according to the customer. I don't like this solutions because of all the redundancy and duplication (DRY principle...).
How do you solve this dilemma?
Would be nice to use one Wix file and only change the link to the specific customer file.
Three possible approaches...
1) Make this a build issue not an installer issue. Leave the WXS alone and have the build automation copy the correct XML file into scope when building the MSI.
2) abstract the path of the XML file using a preprocessor statement and pass the path into the build so that the WXS dynamically points to different XML files at build time.
3) Identify "what" is different about the XML and use the XML Changes capability to transform the base XML to the customer specific XML at installtime. For example, let's say that the XML file is different except some key/value pair has a value with the customers name. Use a proprocessor statement to define a variable for customers name and pass the customers name into the build. Use the preprocessor statement so that a property gets the customers name. Then use the property in an XmlConfig element to apply the customers name to the key value attribute in the XML file.
One possible approach is to package your XML file as 'loose' (not in the embedded in the msi). You'll need a separate Media element and reference it with the DiskId on the File element. Then, you have a single msi file that references the external file so you're free to change the XML file for each customer.
I've done this in one of my projects and it works. Whether this works for you depends on your requirements. One downside is that this isn't very download friendly since you now have two files instead of a single msi.
Related
I have a Visual Studio solution with over 100 projects in it. I want to apply certain settings to all projects, so I used a Directory.Build.Props file and it works great. However, after reading the documentation I just realized that all of the solutions in the sub directories will use those settings too, but I don't want to affect those solutions since I don't maintain them. Is there a way to limit the scope of a Directory.Build.Props file to the current directory, or a particular solution? (Perhaps you can customize the name of the Props file and import it for a particular solution?)
For example, consider a directory structure organized as such:
/code/MySolutionFile.sln
/code/Project001/
/code/Project002/
/code/Project003/
...
/code/Project100/
/code/OtherStuff/OtherStuff.sln
/code/OtherStuff/ProjectA
/code/OtherStuff/ProjectB
/code/OtherStuff/[lots of other solutions somewhere in this directory tree]
I have put my Directory.Build.Props file in the /code directory because I would like to define settings for all projects in /code/MySolutionFile.sln. But I don't want to affect any of the other solutions in subdirectories of the /code folder.
If all else fails I think I could create an empty Directory.Build.Props file and drop it in every directory that contains a solution file except for the one I want mine to apply to. (But this feels like a last resort.)
Modifying my Directory.Build.props file in this way accomplishes my goal:
<Project>
<PropertyGroup Condition="$(SolutionFileName) == 'MySolution.sln'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
This works by setting the PropertyGroup to conditionally run the rules. (Note the condition could also be applied to specific rules rather than the group.) In this case I was able to use the name of the solution file to limit the scope of this rule to my desired solution without affecting any others.
One caveat though, the documentation claims that the solution variables are only available when used in the IDE. However, with my limited testing the rules are also correctly applied when I use msbuild from the command line, like this: msbuild.exe MySolution.sln
I tested it from both a VS Developer Command Prompt and a regular Windows Command Prompt, and both still read the $(SolutionFileName) variable properly.
MSBuild supports Conditions on many types of element, including PropertyGroup and individual properties. In this case, you don't need the Choose-When - you could just put the condition on the PropertyGroup or TreatWarningsAserrors directly.
Is there a way to limit the scope of a Directory.Build.Props file to
the current directory, or a particular solution? (Perhaps you can
customize the name of the Props file and import it for a particular
solution?)
The Directory.Build.Props will act on the xxxx.sln of the current folder which contains many included xxxx.csproj files and then also act on the xxxx.proj files of all the sub folders. It will go down one step at a time for any xxx.proj file which it finds and it is designed by that. You can see the search scope of it.
Since your workaround works well but it is a bit complex, or you can try my solution if you are willing to:
Workaround
Please create a folder called MyStuff under the folder code and then put Project001---Project100 into this folder. After that, put your Directory.Build.Props file into MyStuff folder. With it, the file will affect only Project001---Project100.
Hope it could help you.
I'm creating an installer for a client at the moment but I know that I'll have to create another in a couple of weeks for a different client. What techniques do people use to keep things tidy? The only differences will be whether to include certain dlls with the installer and which initial config file to include.
I was thinking of creating a main wxs file which has most of the share installation information on it and a secondary file which would be customised to the client which would control which components should be included.
Either that or rewrite the main wxs file for each client but that means maintaining a full wxs file for each client with lots of duplicated information.
I assume many other people have come across this situation and I would like to know if I'm on the right path or if there are other much better solutions.
Thanks for any help, Neil.
The solution you choose will depend on how extensive the differences are going to be between the different client installers and how many different clients you'll have to support.
If you only have to maintain 2-3 client installers with max of 10 variables files between them just create a single shared .wxs file that brings in a client-specific .wxi based on build-time parameters. It's easy enough to manually create and maintain 2-3 client-specific .wxi files.
If you have to maintain more clients, or there are 50 different possible dll/config file permutations I'd make use of WiX's heat.exe "harvester" tool. You would create a staging directory for every client you had to support that contained the dlls/config files required for each installer, use heat to harvest each directory into separate .wxs files, and then create a single shared .wxs file that would compile against each of the different harvested .wxs files to create the client installers. This solution requires the build process to be a little more complicated, but it's easier than trying to maintain 20 different client-specific wix files.
In Visual Studio 2010, I want to create a project template that includes links to two files that should exist on the system. One of them is a common AssemblyInfo.cs file. Another is the strong name key file (*.snk).
I need these references to be relative, because each developer's workspace will be set up differently. Is it possible for the project template to somehow figure out where these files reside in each developer's environment?
From reading about templates, it sound like they're pretty static so I wonder if tricks can be done to do something like this. If nothing else, I can add bogus references that will cause compilation errors and force the developer to hook these files in. But if I can do it for them, that would be better.
You should set the CreateInPlace property to true in the vstemplate. The documentation says
Specifies whether to create the project and perform parameter replacement in the specified location, or perform parameter replacement in a temporary location and then save the project to the specified location.
If you want relative paths to work, you need the parameter replacement to occur in the place where you're creating the project, not in a temporary location.
Microsoft have confirmed that this is a bug with the extensibility model and project templates. I managed to get round this by using IWizard. I added the following code in the RunFinished method of my IWizard implementation:
//Get linked file directory
string coreDir = Path.GetDirectoryName(MyProject.FullName);
//Data folder
ProjectItem propertiesProjectItem = slSharedProject.ProjectItems.Cast<ProjectItem>().Where(p => p.Name == "Data").First();
propertiesProjectItem.ProjectItems.AddFromFile(coreDir + #"\Service\TheFileIWantToLink.cs");
This code links in a copy of TheFileIWantToLink.cs file to my shared Silverlight project (slSharedProject).
You could try to integrate a Wizard into your Project Template and set the Paths to the linked files. If i remember right you don't have to create an User Inteface; you only have to specify the correct replacements in the replacementsDictionary and VS will replace the values in your Template File. See this or this for further information.
What is the purpose of the Source attribute? Have a look at this snippet:
<Component Id="MyComponent" Guid="123456789-abcd-defa-1234-DCEA-01234567890A">
<File Id="myFile" Name="myFile.dll" Source="myFile.dll"/>
</Component>
Since Name and Source have the same value, what does Source add? The code does not compile without it.
Where can I find documentation that explains these attributes? I have tried MSDN for MSI but did not find an answer.
Thanks.
WiX and MSI are not the same. Hence no reference in the MSDN documentation ;)
You need to refer to WiX.CHM where you installed WiX, or the online WiX documentation.
Assuming you're talking about File/#Name and File/#Source, this is optional if your source files are laid out in the same way as your WiX directory structure.
The nifty part comes in when you use multiple -b arguments to light and SourceDir in the File/#Source attribute. For example...
<File Id="example.dll" KeyPath="yes" Source="SourceDir\example.dll" DefaultLanguage="0" />
I usually specify 4 folders with -b in my standard build. One for various installer specfiic resources, one for where I store merge modules, one for common resources between all my installs and one for my source files. Now WiX will look in every directory specified on the command line, which makes things a lot more portable if I'm building on a different system with a different directory layout.
As per the documentation, if (in your example) myfile.dll was in the current directory, you could omit the File/#Source attribute.
File/#Source provides the location to get information about the file (size, language, hash) and to copy it to the correct location (either in a cabinet or laid out in a directory relative to the MSI file).
File/#Name is optional if you do not want to install the file with a different name. In other words, if the file exists with the right name on your build machine, just refer to it using the File/#Source and leave off File/#Name.
File/#Id is also optional as long your file name is unique. You cannot have two files with the same File/#Id so add File/#Id when you have collisions.
In WiX v3.5 I often just do:
<Component>
<File Source="my.exe"/>
</Component>
I have created an installer project in VS2008 and need to supply a settings/parameters file along with the .msi/exe file; is this possible? The settingsfile will basically contain some information that is needed in the configuration and our different clients can control the settingsfile.
I've posted some code we use to insert an updated config file to an existing MSI before our customers deploy across the network. Code at Simplest solution to replace a tiny file inside an MSI?