Recently, I was able to streamline my CMake-based C++ development workflow quite a bit by defining just six environment variables:
| |
This gives me compiler caching for C/C++/Cuda sources everywhere through Ccache and makes installing dependencies via vcpkg a breeze. All I need to do is add such a snippet to my ~/.bashrc or ~/.zshrc et voilà! For the fish shell I do a similar thing 1 and it should not be too hard to port this to other shells.
Please see below for further explanations of these two integrations.
Compiler Caching Everywhere
Ccache is a wrapper that adds a caching layer around many popular compilers. If a piece of code is to be recompiled with the same compiler and flags a second time it might be fetched from a cache instead which can often yield a significant reduction in the overall project compile time, but this can of course vary drastically from project to project. Ccache offers first class support for C/C++ compilation with GCC, Clang, and MSVC as well as CUDA compilation via NVCC on Linux, macOS, and Windows2. Objective-C/C++ and further Unix-like operating systems also see some support.
I have never really found myself using Ccache because I thought it would be annoying to integrate. Fortunately, integrating it with CMake is trivial nowadays. Since CMake version 3.17, the environment variable CMAKE_<LANG>_COMPILER_LAUNCHER can be used, as the name suggests, to specify a program that launches the compiler for any CMake supported language (replace <LANG> with CXX for C++, C for C, etc.).
Another way of setting the compiler launcher is by specifying -DCMAKE_<LANG>_COMPILER_LAUNCHER=... when invoking CMake to configure a build directory. While this is less convenient than any global configuration via the shell environment, it is worth keeping in mind since this flag takes precedence over anything set in the environment variable3.
A few other tools exist which might be useful compiler wrappers:
- sccache: similar tool to Ccache by Mozilla that can leverage cloud and local storage for the cache
- distcc: Tool for distributed C/C++ and Objective C/C++ builds on multiple machines
It is quite likely that they can be integrated in a similar way after some initial setup.
C++ Dependency Management with Minimal Boilerplate
For most projects at home and at work, I use vcpkg to manage dependencies. However, it still can be cumbersome to use by either writing custom CMake preset files for the project or having to specify the toolchain file that ships with vcpkg by adding -DCMAKE_TOOLCHAIN_FILE=... to the CMake incantation.
Since CMake 3.21, globally setting the CMAKE_TOOLCHAIN_FILE environment variable alleviates all this friction. I can just run vcpkg add mydep to add a dependency to the project’s manifest and then use something along the lines of find_package(mydep CONFIG REQUIRED)4 in my CMakeLists.txt file to integrate it.
Caveats
There might be some scenarios where these defaults might not be desirable:
- When having to use a custom CMake toolchain file.
- The build system plainly does not work with Ccache (I have not yet encountered such a situation).
In such a case, it is straightforward to unset or overwrite the environment variables making it easily revertible.
Conclusion
While none of the above is revolutionary, these small tweaks noticeably reduce friction in my day-to-day C++ development workflow. It is one of those setups that quietly disappears into the background and just works which usually is a good sign.
The equivalent for fish
↩︎1 2 3 4 5 6 7 8 9# vcpkg integration set -gx VCPKG_ROOT $HOME/vcpkg fish_add_path $VCPKG_ROOT set -gx CMAKE_TOOLCHAIN_FILE $VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake # Ccache set -gx CMAKE_CUDA_COMPILER_LAUNCHER ccache set -gx CMAKE_C_COMPILER_LAUNCHER ccache set -gx CMAKE_CXX_COMPILER_LAUNCHER ccacheTechnically, by setting the environment variable, we set the default value of the
CMAKE_<LANG>_COMPILER_LAUNCHERvariable that is used if it is not explicitly set via-DCMAKE_<LANG>_COMPILER_LAUNCHER. This value is then used to set the default value of the<LANG>_COMPILER_LAUNCHERproperty for all CMake targets. ↩︎Some dependencies might still require
pkg-configor custom integrations, but that is an upstream problem. ↩︎