Compiler Versioning and Continuous Integration
I have been using a home-grown continuous integration system based on a hacked version of CruiseControl.Net since 2007. Whilst it’s not perfect, it works, and I have quite a bit of custom tooling that builds configurations for it for various client projects and builds of various versions of The Server Framework.
By exporting the configuration of Visual Studio as a vsconfig file you can create a custom installation with specific tools in a stand-alone directory structure that can be referenced by build tools, and you can then link specific revisions of code to specific versions of Visual Studio so that they ‘always build’, even if the installed compiler is incompatible.
Despite being very strict about tagging releases and version control in general, one thing that has always been an issue for me has been rebuilding older versions of code. The problem is that I tend to develop using the latest, greatest, version of the tools available so that I get an early warning of things that might break. So, for much of the Windows code that I build, I use Visual Studio 2022 Preview daily and update it every time it wants updating. As time passes, perfectly good C++ code from, say, yesterday, may no longer be considered perfectly good today. As long as the progress is ‘forward’ this is a good thing. The code gets fixed up and builds nicely, and I know that I won’t get a call from a client in a month when the preview compiler changes move into the main-line compiler release. But of course, it’s fragile unless you’re in active development. Eventually, the previously released version of the code and the ‘version of the compiler that you get when you install from scratch’ become out of sync and that may result in support calls.
I’ve just started playing around with a solution to this, and it’s something that I’ve used before, for ‘odd’ clients with strange tooling requirements but hadn’t fully embraced.
The Visual Studio installer allows you to export the installed configuration as a human-readable text file, and import that configuration into a system at a later date. It also allows you to install the configuration with a script, using a command-line installation tool. Using the command-line installation tool, it’s possible to install multiple different copies of Visual Studio, with unique configurations and names, in a side-by-side manner, into a directory of your choice.
With all of this information, it’s possible to install multiple different versions of a Visual Studio tool-chain in separate directories and have my continuous integration server use the correct tool-chain for different versions of different client’s code.
If you store the exported vsconfig configuration file next to a solution file, when you open the solution in Visual Studio, you will be prompted to install any missing components. And, of course, the vsconfig can be version controlled…
As usual, this post is for the future me who forgets how I’ve been setting this up. There are some details that took me a while to get right…
Step one is to export a config file from an installation of Visual Studio. This can contain anything as it’s only going to be used as a starting point for the client-specific version that we’re going to configure.
Step two is to download a Visual Studio bootstrap installer; links to which can be found here. I tend to use the community edition installer as it’s the minimum viable version that works for me, even though I do my day-to-day development with the Enterprise Preview version.
Next, you need to install a custom version of Visual Studio to configure. The easiest way to do this is with the bootstrapper so that you can add a nickname to identify the installation easily in the installer…
vs_Community.exe --config VS2022.vsconfig --installPath G:\VS --nickname USPS
You now have a duplicate installation with a unique nickname. The next step is to start up the normal Visual Studio Installer and modify this installation to contain just the tools that this particular installation needs. For me, at present, this tends to mean that I select specific versions of the compilers and libraries rather than “latest”.
Once you’ve modified the installation, you should export the configuration again and take a look at the config file as there may be things that you can remove.
You could stop here, using the exported config to install the correct components on any machine that needs it and scripting the CI builds to install the tools for each version as they are required. But there’s a problem…
There’s a rather serious issue if you’re doing normal development on a machine with multiple configurations of a particular version of Visual Studio installed on it, and that is that the ‘Visual Studio Version Selector’ that is used to determine which version of Visual Studio opens a sln file when you double-click it will use the latest installed configuration of a given version; so if you have three different configurations of VS2022 installed, you’ll get the most recent opening your sln files, which is likely the exact opposite of what you need.
The fix for this is quite simple. Once you have your custom configuration installed, and you’re happy with it, copy the directory structure, in this case, G:\VS somewhere, into a customer-specific compiler tree, and then use the installer to remove the version you just installed and configured. It seems that the ‘Visual Studio Version Selector’ uses ‘stuff in the registry’ to find the versions that it selects between. The installer manages this ‘stuff’ and so the version selector will only select from versions that show in the installer and, once you’ve removed it, your new custom version doesn’t get selected by the version selector.
You can now run the custom version from your new directory tree directly by running, for example:
G:\CC-Build\Compilers\USPS\VS17.14\Common7\IDE\devenv.exe
and reference the tools directly from this directory structure so that the CI build uses them.