Post

vcpkg with libc++

I have been using vcpkg for a few C++ projects recently. Over the last few months or so, I have mostly been switching over to LLVM tools for my personal C++ projects.

One small pain point is that, if you link against libc++ with your consuming project that links against libraries from vcpkg, you will almost certainly get linker errors. This is because vcpkg will link libraries against libstdc++ by default and, because your project uses libc++ instead, you will get undefined symbols.

If (like me) you want to use a recent version libc++ with your projects to unlock some modern C++ library features, you probably want to know how we can avoid these issues!

One possible answer to this problem is to use a custom vcpkg triplet. There are a few answers out there in GitHub issue replies and StackOverflow posts, but no single place which seemed to explain everything from end-to-end.

Custom vcpkg Triplets

A vcpkg triplet defines configuration for package builds. It contains information such as target architecture, platform, compilation options, default library linkage, and more. The vcpkg repository ships with some default supported and untested community triplets. It is also possible to define our own triplets and overlay them on top of the ones the ship with vcpkg.

So if we want to use libc++, we can define a custom triplet that uses it for vcpkg builds and consume that triplet in our project. Then we should be good to go!

To define our own triplet, we just need a single CMake file that sets a few well-defined variables for vcpkg. I’m calling my triplet file x64-linux-libcxx.cmake. For tidiness, we can put this in a triplets directory local to our project.

1
2
3
4
5
6
7
8
9
10
# Standard platform configuration.
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Linux)

# libc++ configuration
set(VCPKG_C_FLAGS "") # This must be set if VCPKG_CXX_FLAGS is.
set(VCPKG_CXX_FLAGS "-stdlib=libc++")
set(VCPKG_LINKER_FLAGS "-lc++ -lc++abi")

A few interesting notes on the above code sample:

  • VCPKG_CRT_LINKAGE is set for the official Linux x64 triplet, even though it is MSVC specific. I wonder why that is? For parity with the official version, I also set it here.
  • VCPKG_C_FLAGS must be set if VCPKG_CXX_FLAGS is set. Otherwise, we will get CMake configuration errors.

Consuming Custom Triplets

To consume our custom triplet, we need to set a couple of CMake variables to overlay the triplet directory and select our custom triplet. This can be done with the VCPKG_OVERLAY_TRIPLETS and VCPKG_TARGET_TRIPLET CMake variables respectively. For context, I use CMake presets and vcpkg in manifest mode in my projects.

For overlaying the triplets, we need to point vcpkg to our triplets directory. I had to define the VCPKG_OVERLAY_TRIPLETS variable in my CMakeLists.txt file. Although, I have seen other examples online that defined it in the cacheVariables section of their configure presets. I couldn’t get this to work for some reason. Either way, the important point is that this must be set before the root project() call.

1
2
3
4
5
# Must come before project(...)!
set(VCPKG_OVERLAY_TRIPLETS ${CMAKE_SOURCE_DIR}/triplets)

project(MyCoolThing)
# ...

For setting the target triplet, set VCPKG_TARGET_TRIPLET to the name of the CMake triplet file we wrote earlier (i.e. x64-linux-libcxx.cmake) without the extension in the configure preset’s cacheVariables.

Here is a full set of CMake presets that demonstrate this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
  "version": 6,
  "configurePresets": [
    {
      "name": "x64-linux-clang",
      "description": "Base preset for Linux development using Clang compilers and libc++.",
      "hidden": true,
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "installDir": "${sourceDir}/out/install/${presetName}",
      "toolchainFile": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
      "generator": "Ninja",
      "cacheVariables": {
        "CMAKE_CXX_COMPILER": "clang++",
        "CMAKE_C_COMPILER": "clang",
        "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
        "CMAKE_EXE_LINKER_FLAGS": "-lc++ -lc++abi",
        "CMAKE_SHARED_LINKER_FLAGS": "-lc++ -lc++abi",
        "VCPKG_TARGET_TRIPLET": "x64-linux-libcxx"
      },
      "architecture": {
        "value": "x64",
        "strategy": "external"
      },
      "condition": {
        "type": "equals",
        "lhs": "${hostSystemName}",
        "rhs": "Linux"
      }
    },
    {
      "name": "x64-linux-clang-debug",
      "displayName": "Clang x64 Linux Debug",
      "inherits": "x64-linux-clang",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_CXX_FLAGS": "-stdlib=libc++"
      }
    },
    {
      "name": "x64-linux-clang-release",
      "displayName": "Clang x64 Linux Release",
      "inherits": "x64-linux-clang",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "CMAKE_CXX_FLAGS": "-stdlib=libc++"
      }
    }
  ]
}

Please note, that these presets are by no means complete for a real project. They simply aim to demonstrate minimal clang based presets.

And that’s it! Now you can use whichever version of libc++ you like with your vcpkg projects. This approach can also be extended to other custom configurations you might need for your projects.

This post is licensed under CC BY 4.0 by the author.