Using Visual C++ Express with BuildBot

Posted by Nathan Osman on June 4, 2015

I recently began setting up a small build farm for a Qt library I was working on. Due to the nature of the library, it was important that the test suite run on as many different platforms and compilers as possible. After some research, I came to the conclusion that BuildBot was the ideal tool for the job.

My goal was to create build slaves for the following targets:

  • gcc/g++ on Linux (i386, amd64, armhf)
  • Clang on Mac OS X
  • Visual C++ Express on Windows (x86 and x86_64)

Getting build slaves for the gcc machines was simple enough. All I needed to do was install the buildbot-slave package on a machine with a recent Ubuntu release. For the armhf slave, I was able to repurpose my Raspberry Pi 2. After a rather painful amount of configuration, I was able to get the OS X build slave up and running on my Mac Mini.

That left me with the Windows slaves.

The BuildBot Wiki contains a helpful guide for getting the build slaves set up as a single Windows service. After tracking down pre-built copies of the required Python modules, I was able to get the build slaves up and running. The two slaves connected to the master and everything seemed to be fine. Well, almost everything.

The machine itself (which is really just a t2.micro EC2 instance) was running Windows Server 2012 and had Visual C++ Express 2013 installed (including the compilers, libraries, and headers for x86 and x86_64). To “activate” one of the compilers, Visual C++ provides a script named vcvarsall.bat. This sets a number of environment variables (which the compiler uses for finding headers, libraries, etc.) and adds the compiler’s path to %PATH%.

The problem is that there is no easy way to get the BuildBot slave to use this script.

When creating a slave, BuildBot generates a .tac file. This file contains the configuration for the slave. The first “solution” that comes to mind is to use the following snippet within the file:

from subprocess import call
call(["path\\to\\vcvarsall.bat", "x86"])

The problem with this approach is that the environment variables are set only within the command interpreter that is used to execute vcvarsall.bat. As soon as the script completes, the interpreter exits and the environment variables are lost. In fact, the more I pondered this, the more it seemed like a completely impossible task.

If only there were some way to grab a copy of all of the environment variables and their respective values after running the script and before the interpreter exits.

It was at that point that I thought of a really, really nasty hack.

from subprocess import check_output
for v in check_output(['path\\to\\vcvarsall.bat', 'x86', '&&', 'set']).strip().split('\r\n'):
  v = v.split('=', 1)
  os.environ[v[0]] = v[1]

What exactly do these (rather ugly-looking) lines of Python code do? The check_output() function does exactly what call() does in the previous snippet with one exception - it captures the contents of STDOUT from the interpreter. Notice the addition of the && set at the end of the command. The set command prints a list of environment variables to STDOUT.

Next we remove extra newlines and split the output into a list of lines. Each line is in the format:

NAME=VALUE

Then we take the line, extract the name and value, and set the environment variable in os.environ.

And with that, our original goal has been achieved. BuildBot will now have the correct environment variables set when it performs a build. What’s more, since this hack is entirely contained within the .tac file, it is possible to have multiple builders on the same machine using different compilers. I have both an x86 and x86_64 builder happily running on my Windows Server.