Baduit

A young French developper who really likes (modern) C++

About me
03 December 2022

Cleanup my dependency management with vcpkg

by Baduit

Article::Article

Sometimes ago, I made a small esoteric language named PainPerdu. It was inspired by the brainfuck language. Here’s what a hello world in PainPerdu can look like:

+72] +29] +7]] +3] -79] +55] +24] +3] -6] -8] -67] -23]

I had a lot of fun defining the syntax and implementing the interpreter. I even coded a brainfuck interpreter in Painperdu!

I also made an online interpreter (Yeah it is ugly, as you can see it, front end is not my thing), and here’s the issue: I made the interpreter in C++, this mean I could not run the interpreter in the browser. That’s why I made a trivial web API. The flow is really simple the front send its code, the back run the code and then return the result: the console output and the state of the memory.

But actually, I can run C++ in the browser, with WASM (Web Assembly)! That’s what I did recently! But I also took the time to cleanup my code and it was more interesting that expected.

That’s why I will tell how I did it in a series of article. And in this first article I will talk about how I cleaned up my dependency management with Vcpkg.

How dependencies were handled before

I just included the source into the repo, with a git submodule, or most of the time just copying the source files myself.

It is simple, but it is far from optimal if I want to integrate a new library to rely on or even just update one.

The cleanup with Vcpkg

What is Vcpkg

Vcpkg is a package manager for C/C++ projects. You can install libraries easily and it integrates very well with CMake. It is also cross platform, so it works on my windows computer, on my linux laptop and my raspberry pi!

You can browse available packages here: https://vcpkg.io/en/index.html

The installation is described in the readme of vcpkg on github and is straightforward: clone the project, execute the installation script and you are ready to go!

There are two ways to use it:

In the next section, we will see the second technique, because that’s the one I used in this project and I also prefer it.

How to use it and integrate it with CMake

Define your dependencies

The first step is to write the file vcpkg.json. It’s fairly simple, you specify the name, some optional information like the version, the maintainers, the description etc. But most importantly you write your dependencies by just adding their name in the list and it looks like this:

{
	"name": "pain-perdu",
	"version": "0.0.1",
	"maintainers": [
		"Baduit"
	],
	"description": "Implementation of an exotic and esoteric programming language named pain perdu",
	"dependencies": [
		"pegtl",
		"nlohmann-json",
		"nameof",
		"cpp-httplib",
		"brigand",
		"magic-enum",
		"rang"
	]
}

If you want to have more control over the version or some options, it is possible and if you are interested I recommend you to look at the documentation here.

Integrate with cmake

The next step now is to tell cmake to use vcpkg, it is really simple, you just need to use the vcpkg toolchain and there is an option in cmake to specify it: DCMAKE_TOOLCHAIN_FILE

cmake -DCMAKE_TOOLCHAIN_FILE="<INSTALLATION_PATH>/vcpkg/scripts/buildsystems/vcpkg.cmake" .

Obviously <INSTALLATION_PATH> is the path where you cloned the vcpkg project.

But what is I need to use another toolchain for cross compilation or to use emscripten?
Don’t worry, there is an ther option for that: DVCPKG_CHAINLOAD_TOOLCHAIN_FILE

cmake -DCMAKE_TOOLCHAIN_FILE="<INSTALLATION_PATH>/vcpkg/scripts/buildsystems/vcpkg.cmake" -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=my_path_to_my_other_toolchain .

Now that cmake can use vcpkg toolchain and that vcpkg can download and install the libraries you need, you just need to tell cmake to use these libraries like you would if you used any other way to install this libraries. First you need to make a find_package or find_path for header only libraries not defined as an INTERFACE library in their CMakeLists.txt:

find_package(nameof CONFIG REQUIRED)
find_package(magic_enum CONFIG REQUIRED)
find_path(BRIGAND_INCLUDE_DIRS "brigand/adapted.hpp")
find_path(RANG_INCLUDE_DIRS "rang.hpp")
find_package(nlohmann_json CONFIG REQUIRED)
find_path(CPP_HTTPLIB_INCLUDE_DIRS "httplib.h")

And then link against these libraries or add their header to the path:

target_include_directories(my_target PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${CPP_HTTPLIB_INCLUDE_DIRS} ${BRIGAND_INCLUDE_DIRS} ${RANG_INCLUDE_DIRS})

target_link_libraries(my_target PRIVATE PainPerdu Threads::Threads nlohmann_json::nlohmann_json nameof::nameof magic_enum::magic_enum)   

Now you just need to build and voilà, it works:

cmake -B build -DCMAKE_TOOLCHAIN_FILE="<INSTALLATION_PATH>/vcpkg/scripts/buildsystems/vcpkg.cmake" .

cmake --build build

Article::~Article

Vcpkg is a package manager simple to use and cross platform, They are a lot of package available and a lot of feature I didn’t talk about in this article because I didn’t need them for this specific project.

I hope you enjoyed this article, next time I will talk about how I ditched my clunky homemade parser and instead used a simple yet powerful parser library (available on vcpkg obviously).

Sources

tags: cpp - vcpkg - cmake