How to know the value of a built-in variable in WiX Toolset - wix

I know WiX Toolset have some built-in variables that you can use on your projects. A list of them can be consulted here.
But how can I know the value of a variable? For example, if I want to know what value stores LocalAppDataFolder variable, how can I do that from Visual Studio? Is there any command to output its value? I mean something like when outputting system environment variables values from command prompt: echo %Path%

Do you want to know it for your system?
Cause the whole point of these variables is, that they're dependent on the OS installation and you should treat them as variable. In most cases it will point to: %USERPROFILE%\AppData\Local, but since the location of the appdata folder can be controlled during system preparation, it could point to something completely different. Ultimately, this location is retrieved from the Windows Registry:
HKEY_USERS\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Different built-in variables may use different sources to evaluate during installation.

Related

How to ingest property value to cpack-wix without need for running cmake again?

I am using cmake and cpack and wix to build and deploy my project. The installer has some properties that user can enter via GUI or MSI-command-line options.
I set default property values in my CMakeLists.txt using:
set(CPACK_WIX_PROPERTY_<PROPERTY> <value>)
The problem is that if I change these values, I should rerun cmake command before cpack command.
I am looking for a way to change default value of these properties without need for rerunning cmake.
I tried adding additional wxs files using CPACK_WIX_EXTRA_SOURCES or patching generated files with additional xml files using CPACK_WIX_PATCH_FILE, but couldn't find the right code to put in wxs or xml files to accomplish my goal.
I tried The SetProperty command and found out its behavior is not easy to control, I gave up when I saw the user provided values don't replace the initial values and suggested solution at https://web.archive.org/web/20180205001358/http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Unable-to-override-SetProperty-value-with-Edit-Control-value-td7591569.html didn't work. I hope there is a simple way, but even a complex answer using SetProperty is allright.
I am looking for a way to change default value of these properties without need for rerunning cmake.
A script specified in CPACK_PROJECT_CONFIG_FILE variable is the one, which affects on CPack, but which changing doesn't require cmake to re-run. So you may place setting of CPACK_WIX_PROPERTY_<PROPERTY> here: Changing this setting would require only to re-run CPack without re-run cmake on the main project.
Also, when the script specified in CPACK_PROJECT_CONFIG_FILE variable is parsed, CPACK_GENERATOR variable contains the exact CPack generator which is currently processed. This opposites to behavior of the variable inside CMakeLists.txt, when it contains a list of generators.

Is is possible to pass a variable from the build process to Visual Basic code?

My goal is to create build definitions within Visual Studio Team Services for both test and production environments. I need to update 2 variables in my code which determine which database and which blob storage the environment uses. Up till now, I've juggled this value in a Resource variable, and pulled that value in code from My.Resources.DB for a library, and Microsoft.Azure.CloudConfigurationManager.GetSetting("DatabaseConnectionString") for an Azure worker role. However, changing 4 variables every time I do a release is getting tiring.
I see a lot of posts that get close to what I want, but they're geared towards C#. For reasons beyond my influence, this project is written in VB.NET. It seems I have 2 options. First, I could call the MSBuild process with a couple of defined properties, passing them to the .metaproj build file, but I don't know how to get them to be used in VB code. That's preferable, but, at this point, I'm starting to doubt that this is possible.
I've been able to set some pre-processor constants, to be recognized in #If-#Else directives.
#If DEBUG = True Then
BarStaticItemVersion.Caption = String.Format("Version: {0}", "1.18.0.xxx")
#Else
BarStaticItemVersion.Caption = String.Format("Version: {0}", "1.18.0.133")
#End If
msbuild CalbertNG.sln.metaproj /t:Rebuild /p:DefineConstants="DEBUG=False"
This seems to work, though I need to Rebuild to change the value of that constant. Should I have to? Should Build be enough? Is this normal, or an indication that I don't have something set quite right?
I've seen other posts that talk about pre-processing the source files with some other builder, like Ant, but that seems like overkill. It feels like I'm close here. But I want to zoom out and ask, from a clean sheet of paper, if you're given 2 variables which need to change per environment, you're using VB.NET, and you want to incorporate those variable values in an automated VS Team Services build process upon code check-in, what's the best way to do it? (I want to define the variables in the VSTS panel, but this just passes them to my builder, so I have to know how to parse the call to MSBuild to make these useful.)
I can control picking between 2 static strings, now, via compiler directives, but I'd really like to reference the Build.BuildNumber that comes out of the MSBuild process to display to the user, and, if I can do that, I can just feed the variables for database and blob container via the same mechanism, and skip the pre-processor.
You've already found the way you can pass data from the MsBuild Arguments directly into the code. An alternative is to use the Condition Attribute in your project files to make certain property groups optional, it allows you to even include specific files conditionally. You can control conditions by passing in /p:ConditionalProperty=value on the MsBuild command. This at least ensures people use a set of values that make sense together.
The problem is that when MsBuild is running in Incremental mode it is likely to not process your changes (as you've noticed), the reason for this, is that the input files remain unchanged since the last build and are all older than the last generated output files.
To by-pass this behavior you'd normally create a separate solution configuration and override the output location for all projects to be unique for that configuration. Combined with setting the Compiler constants for that specific configuration you're ensured that when building that Configuration/Platform combination, incremental builds work as intended.
I do want to echo some of the comments from JerryM and Daniel Mann. Some items are better stored in else where or updated before you actually start the compile phase.
Possible solutions:
Store your configuration data in config files and use Configuration Transformation to generate the right config file base don the selected solution configuration. The process is explained on MSDN. To enable configuration transformation on all project types, you can use SlowCheetah.
Store your ocnfiguration data in the config files and use MsDeploy and specify a Parameters.xml file that matches the deploy package. It will perform the transformation on deploy time and will actually allow your solution to contain a standard config file you use at runtime, plus a publish profile which will post-process your configuration. You can use a SetParameters.xml file to override the variables at deploy time.
Create an installer project (such as through Wix) and merge the final configuration at install time (similar to the MsDeploy). You could even provide a UI which prompts for specific values (and can supply default values).
Use a CI server, like the new TFS/VSTS 2015 task based build engine and combine it with a task that can search&replace tokens, like the Replace Tokens task, Tokenization Task, Colin's ALM Corner Build and Release Tasks. And a whole bunch that specifically deal with versioning. Handling these things in the CI server also allows you to do a quick build locally at all times and do these relatively expensive steps on the build server (patching source code breaks incremental build in MsBuild, because there are always newer input files.
When talking specifically about versioning, there are a number of ways to set the AssemblyVersion and AssemblyFileVersion just before compile time, usually it involves overriding the AssemblyInfo.cs file before compilation. Your code could then use reflection to read the value at runtime. You can use the AssemblyInformationalversion to specify something like you do in the example above which contains .xxx or other text. It also ensures that the version displayed always reflects the information obtained when reading the file properties through Windows Explorer.

How to create a Wix patch in combination with Heat?

I'm developer on a big system (>100 Projects in Solution, >100 000 LOC, > 10 Services, ...) and did the installation of this system in the past with wix and it worked fine. Now I need a way to patch (Minor Upgrade) parts of the system and run into several issues.
My Current Wix Setup is as following:
I have VS2010 and Wix3.6 Toolset and TFS2012 to Build the whole thing and get an installer
I'm using a Setup Library Project Type per Service
I'm using exactly one Setup Project to bundle things together and get one installer for the whole system.
It's not possible to change this setup.
The Setup Library Projects are set up as following:
I use the heat-directory msbuild task to generate the components and files and I'm using preprocessor variables to modify the file paths.
I need to modify the file paths because it must be possible to build an installer on the local developer system and to build the installer on the tfs build system which is different in folder structures.
The TFS uses always the same directory to compile subsequent versions of the software and moves the output after successful compilation to a unique folder structure.
Now I need a patch.
I created the Patch.wxs and called candle and light for it. I called torch to get the difference file. And finally want to create the patch with pyro.
Everything worked fine with a simple testproject, but on the big system
Pyro has the problem that it can't find the files to install.
Through my setup (see above), I must use preprocessor variables and have a full qualified path in my wix output (for example: C:\builds\myproduct\prodct.exe as file source). After moving the TFS output to another location this path is not valid anymore. I tried to use -bt and -bu switches for pyro, but this does only work for relative paths or for named bindpaths.
Now I wanted to change my wix project setup to use named bindpaths rather than preprocessor variables, but it seems that this is not possible.
heat can only use preprocessor variables or wixvariables but it seems not to be possible to use bindpath variables. heat provides a switch -wixvar which should create binder variables instead of preprocessor variables but I does exactly nothing.
Now I tried do use no wix and no preprocessor variables in heat and tell light per -bu -bt switches where to find the files. But if I do not set a preprocessor variable the resulting files look like Sources\product.exe. I can't get rid of this Sources. I know that I can transform all the xml with xslt and remove the Sources but thats a workaround which I would only implement if no other solution is possible. This would also mean that there is a problem in the wix toolchain.
It looks like pyro does only support bindpath variables and heat does only support preprocessor and wix variables. This seems to be really crazy, because how should they work together?
How can I create a patch if I use lit, light, candle, heat, torch and pyro and if the original build paths have changed (which is very common on a build system) and the file paths are created with heat and therefore be fixed or preprocessor or wix variables?
As you've found heat wasn't designed to be used in the patching scenario. It was only in recent versions of the WiX toolset that the generated GUIDs got to a point where there was even a chance that heat could successfully build output that would be patchable. Still need to do work there to make patching where heat is used work well.
Ultimately, I believe the answer is to simplify the "original source" problem. It is challenging to get all the bindpaths set up correctly and that makes patching, which is a hard problem, even harder. We've kicked around a few ideas but nothing has come together yet.
You could always use admin image based patching. It's slower but can be easier to get the "original source" and "target" laid out. That path does lose filtering though.
Basically, we need to do a bit more work in patching scenarios to make it much easier.
PS: "Source" in the path for a File/#Source attribute is an alias for the "default bindpath". You can use bindpaths there.

MSI Properties Empty on Passive, Basic UI, and No UI Install

I am working on an installer with a handful of custom actions that look for values in the following MSI properties:
ADDLOCAL
ADDDEFAULT
REINSTALL
REMOVE
When running the installer with the full UI, these properties contain lists of comma-separated feature names as appropriate given the options that are selected by the user. However, when I run the installer in passive mode or with only a basic UI (or no UI) from the command line, I find that the properties are empty/blank. It is only when I explicitly set them from the command line that they have any value. This is an acceptable work-around, but it would be much better if these could take on some kind of default, such as "all", without requiring values to be passed on the command line. Is there some way I can specify this in WiX (which I am using to build the MSI) or do I have to do something in the custom action code (or something else entirely)?
I have looked at the property reference here, but I did not see any mention of how one could specify default features for these properties.
What I also found interesting is if I do specify a feature this way on the command line during install, it seems to be stored for the uninstall in the REMOVE property (in other words, I do not have to pass any parameters when uninstalling in any mode). Is this a feature that I can rely on? Will it automatically update if someone modifies the installation later?
I'm running WiX 3.5.2519 and using Visual Studio 2010 with Visual C++ for the custom action code. Thanks for any help you can give me!
Edit:
Actually, I was wrong. It appears that the REMOVE property is always set to "all" when uninstalling and running in one of these modes, even if I pass a different value on the command line or only install a subset of features. This seems broken. Am I doing something wrong here?
C:\> msiexec REMOVE=FeatureName /passive /l* uninstall.log /x Product.msi
It will completely ignore what I specify for "FeatureName" and use "all" in its place.
Your custom actions probably shouldn't examine those properties. Instead they should examine the feature and/or component states of the product, depending on what they're trying to do. In conditional statement syntax, this looks like $component-action or &feature-action (where you use the name of the component or feature whose action you are trying to condition against). In C++ (for inside the custom action) this looks like MsiGetFeatureState or MsiGetComponentState, and these are of course made available through similar means in most other languages (such as session.FeatureRequestState / session.ComponentRequestState in a language you shouldn't use).
It is a recommended practice that all properties to be used by the installation be entered into the Property table with an initial value. The installer sets the properties to these values at the launch of the installation. Properties for which a blank is an acceptable value and properties built into the installer do not need to be initialized.
You can then change the default value programmatically or on the command line as described here: Using Properties.

Why can I use some environment variables in some of the elements in the csproj file but not others in msbuild?

What reasons could there be for the following strange behaviour, and how might I track down the issues?
We use a combination of make files and msbuild.
I have a project which needs to be strongly named. I was previously setting the snk to use in the project file like this:
<AssemblyOriginatorKeyFile>$(EnvironmentVariable)TheKeyName.snk</AssemblyOriginatorKeyFile>
where EnvironmentVariable was defined in the batch file that launched the shell for the build like this:
set EnvironmentVariable='SomePath'
and this worked ok. Now I need the string name key to be able to be changed, so it can be different on the dev machine and the release build server. There is a variable which exists to hold the full path to the strong name key file, called StrongNameKeyFile. This is defined in the msbuild environment, and if I put some text output in the targets or properties files that are included as part of the msbuild task which build the project then I can see that this StrongNameKeyFile points to the correct location. So I changed the csproj to have this instead:
<AssemblyOriginatorKeyFile>$(StrongNameKeyFile)</AssemblyOriginatorKeyFile>
but when I try and compile this is evaluating to empty and no /keyfile is specified during the build.
We also have variable defined in the make files and these can be accessed in the csproj as well. These are used to point to the locations of referenced dlls, so that they can be different on dev and build machines. I know that these are set as the references come out correctly and everything compiles, but if I try and use one of these variables in the AssemblyOriginatorKeyFile element then it evaluates to empty in that element, but works in the reference element.
Why might this be? Is AssemblyOriginatorKeyFile treated specially somehow? How can I go about tracking the cause of this down?
There's no good reason why this should happen - as you know it normally Just Works; it's likely to be something in the chain dropping it on the floor.
One thing to try is explicitly passing it via /p:StrongNameKeyFile=XX - that would eliminate environment variables and the correct propagation thereof from your inquiries.
Another potential thing is that something is clobbering the variable as the name is used vy something else?
Run with /v:diag and you'll get dumps of all the inputs and/or variables as they change.
Or if on V4, use the MSBuild Debugger
And buy the Hashimi et al MSBuild book