Using system package manager dependencies with vcpkg

Daniel Shaw

According to the C++ 2022 developer survey, the top 3 ways to manage C++ libraries were having the library source code as part of the build, compiling the library separately from instructions, and acquiring the library from a system package manager. Language package managers, such as vcpkg, simplify library management by offering the ease of use of a system package manager with the power, flexibility, and portability of building from source.

vcpkg brings several advantages to C++ library management. First, vcpkg supports a wide range of customizations for each library. vcpkg installs libraries with a specific version. For additional configuration, each library supports various features and flags. Second, the vcpkg catalog supports over 1900 libraries. Third, vcpkg brings finer control: once a library and feature set are selected, that library’s configuration will be consistent on any platform.

However, there are certain scenarios where using a system package manager is the better choice. You can deploy ABI compatible security and bug fixes without deploying the entire application. Multiple applications can use the same shared library, which reduces deployment size. And sometimes, company policy requires using a particular system package dependency.

In the first example, we will show what it will look like if we strictly use dependencies from vcpkg. In the second example, we will replace some dependencies with system package manager ones.

This blogpost is for advanced users of vcpkg. To get started with vcpkg instead, see https://github.com/microsoft/vcpkg.

Building an application with dependencies from the vcpkg catalog

In this example, we are using Ubuntu 22.04 and the x64-linux triplet. First, create three files in an empty project directory: vcpkg.json, main.cpp, and CMakeLists.txt.

//vcpkg.json
{
    "name": "test-project", 
    "version": "1.0.0",
    "dependencies": [
        "azure-core-cpp",
        "curl"
    ]
}
//main.cpp
#include <iostream>

#include <curl/curl.h>
#include <azure/core.hpp>

using namespace std;

int main() {
    cout << curl_version() << endl;
    cout << Azure::DateTime(std::chrono::system_clock::now()).ToString() << endl;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(test-project)
add_executable(main main.cpp)
find_package(CURL REQUIRED)
find_package(azure-core-cpp CONFIG REQUIRED)
target_link_libraries(main PUBLIC Azure::azure-core CURL::libcurl)

Configuring the project:

cmake <path to project> -DCMAKE_TOOLCHAIN_FILE=<path to vcpkg>/scripts/buildsystems/vcpkg.cmake

... [build output]
Detecting compiler hash for triplet x64-linux...
The following packages will be built and installed:
azure-core-cpp[core,curl,http]:x64-linux -> 1.7.1
curl[core,non-http,openssl,ssl]:x64-linux -> 7.84.0#1
* openssl[core]:x64-linux -> 3.0.5#4
* vcpkg-cmake[core]:x64-linux -> 2022-07-18
* vcpkg-cmake-config[core]:x64-linux -> 2022-02-06#1
* zlib[core]:x64-linux -> 1.2.12#1
Additional packages (*) will be modified to complete this operation.
...

Building and running main:

cmake --build . && ./main

[ 50%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable main
[100%] Built target main
libcurl/7.82.0-DEV OpenSSL/3.0.2 zlib/1.2.12
2023-11-07T02:58:10.0215004Z

We can use ldd to list the shared libraries:

ldd main

linux-vdso.so.1 (0x00007ffc32bf1000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f816357a000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8163493000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f8163473000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f816324b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8163cf0000)

Using curl and OpenSSL from the system package manager

You can use port overlays to configure vcpkg to use the system package manager dependencies. We will build the previous example and use curl and OpenSSL from the system package manager.

Install the developer dependencies for curl with an OpenSSL backend and OpenSSL:

sudo apt-get install libcurl4-openssl-dev libssl-dev

Create the port overlay directory and file structure:

mkdir -p <path to project>/overlays/curl <path to project>/overlays/openssl
//<path to project>/overlays/curl/vcpkg.json
{
    "name": "curl",
    "version": "1.0.0",
    "port-version": 0,
    "features": {
        "ssl": {
            "description": ""
        }
    }
}
# <path to project>/overlays/curl/portfile.cmake
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)
//<path to project>/overlays/openssl/vcpkg.json
{
    "name": "openssl",
    "version": "3.0.5"
}
# <path to project>/overlays/openssl/portfile.cmake
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)

Building with CMake (we’re using the same project in the previous example):

cmake <path to project> -DVCPKG_OVERLAY_PORTS=<path to project>/overlays -DCMAKE_TOOLCHAIN_FILE=<path to vcpkg>/scripts/buildsystems/vcpkg.cmake

-- Running vcpkg install
Detecting compiler hash for triplet x64-linux...
The following packages will be built and installed:
azure-core-cpp[core,curl,http]:x64-linux -> 1.7.1
curl[core,non-http,openssl,ssl]:x64-linux -> 7.84.0#1 -- <path to project>/overlays/curl
* openssl[core]:x64-linux -> 3.0.5#4 -- <path to project>/overlays/openssl
* vcpkg-cmake[core]:x64-linux -> 2022-07-18
* vcpkg-cmake-config[core]:x64-linux -> 2022-02-06#1
Additional packages (*) will be modified to complete this operation.
...

Building and running main:

cmake --build .
.\main

libcurl/7.81.0 OpenSSL/3.0.2 zlib/1.2.11 brotli/1.0.9 zstd/1.4.8 libidn2/2.3.2 libpsl/0.21.0 (+libidn2/2.3.2) libssh/0.9.6/openssl/zlib nghttp2/1.43.0 librtmp/2.3 OpenLDAP/2.5.11
2023-11-07T03:02:26.8637898Z

Checking whether we’ve linked the correct shared libraries (libssl and libcurl):

ldd main

libcurl.so.4 => /lib/x86_64-linux-gnu/libcurl.so.4 (0x00007f632c1c0000)
libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x00007f632c11c000)
libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f632bcda000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f632baae000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f632b9c7000)
... (25+ more dependencies)

You will notice curl and openssl from the system package manager have more dependencies than the curl and openssl from the vcpkg catalog. In the above example, we only added port overlays for the dependencies that we use. If you were to build a more complex application that would depend on other dependencies, you would need to add additional overlays.

Both the vcpkg and the system packager catalogs can contain the same library, and we need to make sure the application only links one copy of each library. Linking two different versions of the same library could lead to ODR violations. In the example, curl from the system package manager pulls in OpenSSL as a dependency. We overlayed the OpenSSL dependency in vcpkg to prevent vcpkg from also acquiring OpenSSL from its catalog.

How did CMake find curl?

When find_package(CURL) is called, CMake searches for the CURL libraries in the vcpkg_installed directory. If the CURL libraries are not found on the CMake paths, it defaults to the FindCurl module which finds the system dependency.

Learn More

If you would like to contribute to vcpkg and its library catalog, or want to give us feedback on anything, check out our GitHub repo. Please report bugs or request updates to ports in our issue tracker or join a more general discussion in our discussion forum. For an overview of our top priorities and backlog, take a look at our roadmap page.

6 comments

Comments are closed. Login to edit/delete your existing comments

  • Paulo Pinto 0

    It would be interesting to know what is the suggest path for similar workflow on Windows, the bread and butter of Visual C++ users.

    Windows store, winget, chocolatey, NuGET,….

    • A Neumann 0

      It is basically the same? The main step for vcpkg here is to provide the empty overlay.

      The main difference on windows is that vcpkg cleans the environment by default so you have to setup a triplet using `VCPKG_ENV_PASSTHROUGH(_TRACKED)` and maybe pass through `PATH` or use some other way to tell CMake within vcpkg where to find stuff.

      • Paulo Pinto 0

        For starters, most Windows shops use MSBuild or nmake.

        Secondly, the fact that I had to search and failed to find VCPKG_ENV_PASSTHROUGH, shows how well documented it is.

        • A Neumann 0

          > For starters, most Windows shops use MSBuild or nmake.

          my condolences, at least use CMake to generate VS project files. (also I doubt that assessment considering the extremely low number of ports using nmake/msbuild within vcpkg.)

          But your real question then seems to be:
          How to use the overlay-ports feature with Visual Studio using vcpkg in manifest mode to achieve the same?
          (you asked for a similar workflow on windows which implicitly assumes CMake as a basis)

          > Secondly, the fact that I had to search and failed to find VCPKG_ENV_PASSTHROUGH, shows how well documented it is.

          https://github.com/microsoft/vcpkg/blob/master/docs/users/triplets.md#vcpkg_env_passthrough

          • Paulo Pinto 0

            No need for condolences, at least I get to use modules already.

            I asked the question from point of view from Visual C++ users, which any Microsoft advocate would naturally understand the audience.

            The point that there is a deep link to the documentation doesn’t mean it is easy to find.

  • Oliver Schneider 0

    Are there any plans of getting vcpkg “bootstrapped” via WinGet or is this generally not feasible or perhaps not a good idea for whatever reason (which?)? Thanks.

Feedback usabilla icon