A Modular CMake Recipe for Linking std::filesystem in Modern C++17

Same build on three different setups
Same build in three different setups

So you’ve started using features of C++17 and modern CMake. Great idea! This post outlines how you might handle a corner-case issue when using features at the bleeding edge of your toolchain. In this example, I’m trying to use a std::filesystem API not supported by specific versions of the GNU toolchain. So we need to add an extra library to our target’s dependencies for this scenario, but not others. Let’s review one way to do that effectively.

I initially tried blindly including ‘stdc++fs’ for my application, which worked on RHEL8. But I soon realized this broke my MacOS and other builds. It failed in different situations because toolchain versions differed. Some builds required the additional library, and some wouldn’t tolerate it as it doesn’t exist for them. Also, unlike GCC, no versions of Clang need or have this library.

I needed to include the supplemental library conditionally. So I researched the best way to do this. Like many problems and solutions online, I had to wade through many proposals to find one I liked. I found and refined a solution that creates a workable build for all scenarios I’ve tried. It’s a single line of CMake code. While it’s difficult to parse visually, it is modular and efficiently solves the issue. In addition, it’s easy to add to any project without any dependencies.

The last line of CMake code shown below selectively adds the ‘stdc++fs’ library to the executable if you’re using GNU, ^and^ its version is less than 9.0. The syntax is called a CMake Generator Expression. They are a powerful building block to master.

If you go back far enough in GNU versions, you can’t compile C++17 code. But that’s true, with or without this extra line. Note that Clang toolchains will never require the additional library.

cmake_minimum_required(VERSION 3.14)
project(is_regular)

set(CMAKE_CXX_STANDARD 17)
add_executable(is_regular main.cpp)
target_link_libraries(is_regular $<$<AND:$<CXX_COMPILER_ID:GNU>,$<VERSION_LESS:$<CXX_COMPILER_VERSION>,9.0>>:stdc++fs>)

I’ve added this to a couple of projects. Check out some sample code at https://github.com/polishedprogrammer/polished-cmake/tree/main/std-filesystem-recipe and try it in your development environment. I’ve even experimented with different versions of toolchains using Podman to check compatibility. It’s a quick way to run through many combinations. Of course, you can also use this form to solve similar issues in CMake. I hope you can put it to good use.