An Example Build System
Recently, a friend asked me to help with a project, which He and his client couldn't get to build on Windows. I got it to build, but neither of them were able to build using my build script because their environments were different from mine. So, the client asked me to set up a build server.
After setting a server up, I decided to clean the instructions up for general use, and detail how I got it done. Maybe it will be usefil to somone. A few minor points have been changed. I have multiple toolchains setup on my own system, and the client needed to use an additional collection of libraries which I don't document here.
These instructions are for setting up a development and building system on Windows 10 (1909), and a build system on Windows Server 2019. Some steps are different between the two systems. The differences are noted when they come up.
The Initial Setup
If you don't have a fresh system, It's important to clean up your
environment variables, especially the PATH
environment variable.
Many development tools will add themselves to PATH
, modify other
environment variables, or add new ones. This makes it easier to use them,
but comes at the price that they may interfere with each other. This happened
with my friend. CMake would find another toolchain he had installed instead
of the Visual C++ toolchain we were trying to use. So, if a tool or library
makes a change to the environment variables on the system when I install it,
I change the variables back or remove them, as appropriate, and put the
changes into a PowerShell script that I can dot-source before a build.
An example of such a script is below.
If you need to clean up your environment variables, open File Explorer, right click on This PC, click on Advanced System Settings, select the Advanced tab, and click the Environment Variables button. You can use this window to modify your environment variables. There are two sets of environment variables, the upper section shows environment variable values that apply to your user account, and the lower section shows values that apply to the whole system. If you remove or change anything, make a note of it and consider adding it to a PowerShell script for later use. You shouldn't have any of these variables in either ste of values:
LIB
LIBPATH
INCLUDE
Your PATH
variable also shouldn't have paths for any
development tools. Ideally, it should only have the default paths it had when
Windows was installed. Be careful modifying any other values.
My Foundational Tools
These are the tools I find absolutely essential to develop software on Windows using C++ (and C). The version numbers are the ones I used as of writing these instructions, but differences between versions are minor enough that these instructions should work for later versions.
- PowerShell (v7.0.0)
- https://github.com/PowerShell/PowerShell/releases
- I use PowerShell 7, instead of the built-in PowerShell 5.1
for a couple of reasons. PowerShell 7 has features not present
in PowerShell 5.1, and, more importantly, PowerShell 7 also runs
on macOS and Linux, so I can use it to automate builds on all
three systems. When installing PowerShell, I decline to add
PowerShell to the
PATH
environment variable. - Microsoft Visual Studio (2019)
- Visual C++ (v14.25.28610)
- Windows 10 SDK (v10.0.18362.0)
- https://visualstudio.microsoft.com/downloads/
- Microsoft offers an installer and update manager for Visual Studio and various development tools. The installer is available for three SKUs, Community, Professional, and Enterprise, and also without the IDE components, called Build Tools for Visual Studio 2019. ThemBuild Tools version is avaiable under Tools for Visual Studio under All Downloads on the download page.) I use Visual Studio Community edition on my Windows 10 System, and the Build Tools version on the Server. When installing Visual Studio, make sure to install the Windows 10 SDK, and the MSVC v142 VS 2019 C++ x64/x86 build tools. If you install versions different than those listed here, make sure to adjust the script below to match what you installed. While the Visual Studio Installer offers packages for CMake and Git, I download and maintain these separately.
- CMake (v3.16.5)
- https://cmake.org/download/
- Since I build cross platform, I use CMake to generate my build
systems on every system. When I install CMake, I uncheck the
option to add CMake to the
PATH
. - Git (v 2.25.0)
- https://git-scm.com/download/win
- Git is an essential tool for any programmer or developer. When I Install Git, I select Run Git from Git Bash only, and Enable Symbolic Links.
Optional Tools
These tools aren't essential for every project or can be replaced by other tools.
- Ninja 1.10.0
- https://github.com/ninja-build/ninja/releases
- Ninja is a build system that functions like make. It's designed to
be fast, and for its input files to be generated by a system
like CMake rather than by hand. The only reason I use Ninja
specifically is because it's available on macOS, Linux, and
Windows, so I can use the same build scripts on all three
systems. Instead of Ninja, you can use nmake, which comes
with Visual C++, or MSBuild which is available through the
Visual Studio installer. Ninja is released as a single file,
ninja.exe
in a zip file. To install Ninja, you will have to download the zip file, make a directory calledC:\Program Files\Ninja\bin
and copy theninja.exe
file within the zip file to this directory. - WiX Toolset 3.11.2
- https://github.com/wixtoolset/wix3/releases
- The WiX Toolset is a scriptable Microsoft Installer (.msi) file generator. CPack, a package-generation utility inculded with CMake, can use WiX as a backend to generate installer files for your projects. Other options for generating installers and packages are available, such as the Nullsoft Scriptable Install System, or for-pay products like InstallShield. Note that WiX adds a WIX environment variable when it installs, so be sure to clean that up.
- Qt 5.14.1
- https://www.qt.io/download-qt-installer
- Qt is a cross-platform application framework. It provides its own APIs for interacting with GUIs, networks, databases, the filesystem, etc. Instead of Qt, you can write programs using Windows' own APIs. When Installing Qt, make sure to select the MSVC 2017 64-bit option under 5.14.1. This version is compatible with Visual Studio 2019.
System Configuration
Windows 10 has a security measure built in to prevent malicious code from running on a system. So, on Windows 10, open a PowerShell window using the newer PowerShell mentioned above, and run the following command:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
This will allow you to run PowerShell scripts that are signed or that you've written locally. This setting is the default on Windows Server 2019.
Windows Server 2019 should have one or more additional users, separate from the Administrator account, for building software. Whether you use a single dedicated user or setup separate users for each member of your team is up to you.
The Configuration Script
Finally, you will need a script to setup the build environment. The script
below is adapted from my own scripts. Be sure to modify it to set
$Cfg["VsSku"]
to "BuildTools"
if you used
the Build Tools installer for Visual Studio.
You will have to dot-source the script before you can build. So, if you save
the script as BuildConf.ps1
in your user directory, you would run
it like so:
. ~/BuildConf.ps1
If, for some reason, PowerShell gives you an error saying that execution policy doesn't allow running the script, run the following:
Unblock-File ~/BuildConf.ps1
BuildConf.ps1
$Cfg = @{} $Cfg['WinDir'] = "C:\Windows" $Cfg['Prog64Dir'] = 'C:\Program Files' $Cfg['Prog32Dir'] = 'C:\Program Files (x86)' $Cfg['MsvcArch'] ="x64" $Cfg['QtBuild'] = "msvc2017_64" # Visual C++ Toolchain Versions $Cfg["VsVer"]="2019" $Cfg["VsSku"]="Community" $Cfg["VcVer"]="14.25.28610" $Cfg["WinSdkVer"]="10.0.18362.0" # Visual Studio Paths $Cfg["KitBase"]="$($Cfg.Prog32Dir)\Windows Kits\10" $Cfg["VsBase"]="$($Cfg.Prog32Dir)\Microsoft Visual Studio" $Cfg["VsDir"]="$($Cfg.VsBase)\$($Cfg.VsVer)\$($Cfg.VsSku)" $Cfg["VcDir"]="$($Cfg.VsDir)\VC\Tools\MSVC\$($Cfg.VcVer)" # Qt Values $Cfg['QtBaseDir'] = 'C:\Qt' $Cfg['QtVersion'] = '5.14.1' $Cfg['QtDir'] = "$($Cfg.QtBaseDir)\$($Cfg.QtVersion)\$($Cfg.QtBuild)" # Locations of Other Tools $Cfg['GitDir'] = "$($Cfg.Prog64Dir)\Git" $Cfg['CmakeDir'] = "$($Cfg.Prog64Dir)\CMake" $Cfg['NinjaDir'] = "$($Cfg.Prog64Dir)\Ninja" $Cfg['WixDir'] = "$($Cfg.Prog32Dir)\WiX Toolset v3.11" ##### if(![string]::IsNullOrEmpty($ENV:PATH) -and !($ENV:PATH -match '.+?;$')) { $ENV:PATH += ";" } # Setup Visual C++ $ENV:PATH += "$($Cfg.VcDir)\bin\HostX64\$($Cfg.MsvcArch);" $ENV:PATH += "$($Cfg.KitBase)\bin\$($Cfg.WinSdkVer)\$($Cfg.MsvcArch);" $ENV:INCLUDE += "$($Cfg.VcDir)\include;" $ENV:INCLUDE += "$($Cfg.KitBase)\include\$($Cfg.WinSdkVer)\ucrt;" $ENV:INCLUDE += "$($Cfg.KitBase)\include\$($Cfg.WinSdkVer)\um;" $ENV:INCLUDE += "$($Cfg.KitBase)\include\$($Cfg.WinSdkVer)\shared;" $ENV:INCLUDE += "$($Cfg.KitBase)\include\$($Cfg.WinSdkVer)\winrt;" $ENV:INCLUDE += "$($Cfg.KitBase)\include\$($Cfg.WinSdkVer)\cppwinrt;" $ENV:LIB += "$($Cfg.VcDir)\lib\$($Cfg.MsvcArch);" $ENV:LIB += "$($Cfg.KitBase)\lib\$($Cfg.WinSdkVer)\ucrt\$($Cfg.MsvcArch);" $ENV:LIB += "$($Cfg.KitBase)\lib\$($Cfg.WinSdkVer)\um\$($Cfg.MsvcArch);" # Setup Qt $ENV:PATH += "$($Cfg.QtDir)\bin;" $ENV:INCLUDE += "$($Cfg.QtDir)\include;" $ENV:LIB += "$($Cfg.QtDir)\lib;" # Setup Other Tools $ENV:PATH += "$($Cfg.GitDir)\cmd;" $ENV:PATH += "$($Cfg.CmakeDir)\bin;" $ENV:PATH += "$($Cfg.NinjaDir)\bin;" $ENV:PATH += "$($Cfg.WixDir)\bin;" $Env:WIX = "$($Cfg.WixDir)\"
Testing
At this point, I would suggest writing a simple project and testing that it works. How to write C++ and CMake code, and how to use CMake and Qt are beyond the scope of this document, however.
Copyright 2020 Vincent Damewood