vcpkg+cmakeでC++開発環境を整える

に公開

初めに

C++のプロジェクトを作る度にゴミみたいなC++の環境設定に悩まされていました。GLFWみたいなバイナリを使うのが楽だけどあんまgitに入れたくない、でもビルドはやりたくない面倒くさい外部ライブラリを組み込む際はかなり悩みます。

最近またプロジェクトを作ることにしたのでいつも通り環境でう~んと悩んでいましたが、(どなただったか覚えてないんですが)以前にvcpkgというC++パッケージマネージャが良いと教えられたのを思い出しました。

それで今回試しにやってみたところ、悩みの種だった外部ライブラリの管理がとてもシンプルになり、とても良い開発体験になりました。

今回の記事はその備忘録も兼ねて、簡単なサンプルプロジェクトの例でvcpkgの環境構築について解説していこうと思います。

vcpkgとは

Microsoftが提供しているオープンソースのC++向けパッケージマネージャーです。
https://github.com/microsoft/vcpkg

vcpkgのチュートリアルは以下にあります。今回の記事はこのチュートリアルを基づいて解説します。
https://learn.microsoft.com/ja-jp/vcpkg/get_started/overview

vcpkgのインストール

まずはvcpkgをGitHubから任意の場所にcloneします。

git clone https://github.com/microsoft/vcpkg.git

中身としてはこんな感じです

これがパッケージマネージャーの本体となりますので、認識されるように色々と設定をしていきます。最初にやることはbootstrap-vcpkg.batというbatファイルの実行です。これは環境チェックをしたうえで、vcpkgの実行ファイルのインストールをするものです。

実行後はこんな感じでexeファイルの他にも様々なファイルがインストールされます

vcpkgは基本的にこれらの実行ファイルを使って操作していきます。

ただし、環境パスは自動的に設定されるわけではないので、実用上は手動でVCPKG_ROOTPATHの二つの環境設定をしておくと便利です。これらはvcpkgのパスを通してあげればOKです(Pathは追加する形で)。私はC:\vcpkg\vcpkgにあるのでこんな感じに設定しています。


めんどくさかったら以下のPower Shellコマンドを設定すると楽です(PowerShellのコマンドって怖いので自己責任でお願いします)。hoge/vcpkgの部分を自分のvcpkgのパスに置き換えて貰えればOKです。

[Environment]::SetEnvironmentVariable("VCPKG_ROOT", "C:\hoge\vcpkg", "User")
[Environment]::SetEnvironmentVariable("PATH", "C:\hoge\vcpkg;" + [Environment]::GetEnvironmentVariable("PATH", "User"), "User")

これでvcpkgのセットアップは終了です。他の場所でvcpkgコマンドを打ってusageが出れば上手くいっています。もし出なかったら環境パスのチェックをお勧めします。

vcpkg

vcpkgとcmakeでプロジェクトを作る

ここからはプロジェクト制作に入ります。今回は最もミニマルな構成のプロジェクトにGLFWとglmを導入するという例で解説していこうと思います。

project/
├── src/
  └── main.cpp

vcpkgはプロジェクト単位でパッケージの管理を行います。vcpkgの環境設定はパッケージの設定ファイルであるマニフェストファイルを作成し、必要なパッケージ名を記述、それをもとにインストールするというような流れで行われます。

まず最初にやることはマニフェストファイルの作成です。プロジェクトフォルダで以下のコマンドを実行するとマニフェストファイルとなるvcpkg.jsonが生成されます(+vcpkgのコンフィグファイル)。

vcpkg new --application
project/
├── vcpkg.json // マニフェストファイル
├── vcpkg-configuration.json
└── src/
    └── main.cpp

次はこのvcpkg.jsonにパッケージを記述し、依存関係を記載します。パッケージ名hogeに対して、次のコマンドでパッケージを追加することができます。

vcpkg add port hoge

今回はGLFWとglmを入れます。それぞれのコマンドは以下のような感じです。

vcpkg add port glfw3
vcpkg add port glm

これをするとvcpkg.jsonにはdependenciesという項目が追加され、GLFWとglmが追加されていることが確認できます。

{
  "dependencies": [
    "glfw3",
    "glm"
  ]
}

これをもとにパッケージのインストールを次のコマンドで行います。

vcpkg install

インストールが成功するとvcpkg_installedというフォルダにパッケージのデータが生成されます。基本的には開発するPCのプラットフォーム環境でのデータがインストールされます(プラットフォームを指定する場合はトリプレットで指定できるらしいのですがここでは割愛)。

project/
├── vcpkg.json
├── vcpkg-configuration.json
├── vcpkg_installed/          # ← vcpkg が自動生成したインストール内容
│   └── x64-windows/
│       ├── include/
│       └── lib/
└── src/
    └── main.cpp

cmakeとの連携

ここからはcmakeと連携してプロジェクトを生成してみます。プリセットだとか色々出ますが、ここではcmake自体の解説はしませんのでご了承ください。

CMakeLists.txtを追加し、以下のように編集します。vcpkgで取得したパッケージはfind_packageを通してcmakeに伝えることができます。後は通常の外部ライブラリと同様のリンクの設定をしてあげれば問題ありません。

cmake_minimum_required(VERSION 3.16)
project(project)

set(CMAKE_CXX_STANDARD 17)

find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)

add_executable(project src/main.cpp)

target_link_libraries(project PRIVATE glfw glm::glm)

ただし、cmake側が自動的にvcpkgを判別することはないので、cmakeにcvpkgの情報を設定する必要があります。ドキュメントによるとCMAKE_TOOLCHAIN_FILEにvcpkg本体にある/scripts/buildsystems/vcpkg.cmakeというファイルの指定をすればよいとのことです。

これはコマンドでもパスを設定できますが、cmakeのプリセットで書いた方がシンプルにまとまると思います。なのでここでは以下のようなプリセットファイルCMakePresets.jsonを用意して実行することにします。

{
  "version": 3,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 16,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "default",
      "binaryDir": "${sourceDir}/build",
      "cacheVariables": {
        "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
      }
    }
  ]
}

最終的なファイル構成はこんな感じです

project/
├── CMakeLists.txt
├── CMakePresets.json
├── vcpkg.json
├── vcpkg-configuration.json
├── vcpkg_installed/
└── src/
    └── main.cpp

ビルドとテストコード

ではcmakeのビルドをやってみましょう。defaultプリセットを作ったので次のようなコマンドでcmakeのビルドができるはずです。

cmake --preset default

ビルドがうまく行けばbuildフォルダが作られてその中にプロジェクトのproject.slnファイルが作られているはずです。

ソリューションを開き、projectプロジェクトをスタートアッププロジェクトに設定し、次のようなmain.cppでデバッグ実行をしてみてください(コードはVulkan Tutorialからお借りしました)。

// main.cpp
#include <iostream>
#include <glm/glm.hpp>
#include <GLFW/glfw3.h>

int main()
{
    glfwInit();

    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    GLFWwindow* window = glfwCreateWindow(800, 600, "test", nullptr, nullptr);

    glm::mat4 matrix;
    glm::vec4 vec;
    auto test = matrix * vec;

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
    }

    glfwDestroyWindow(window);

    glfwTerminate();

    return 0;
}

これでエラーなく、ウィンドウが実行できれば成功です。こんな感じでvcpkgを使うと中々面倒くさいGLFWとかを簡単に導入することができます。

終わりに

噂に聞いていたvcpkgを使ってみましたが、環境設定がシンプルになりとても良いなと感じました。やはりパッケージマネージャーがあると色々と楽ですね(nugetとかどうしたんだろう)。今きつかったWindows環境での整備が簡単になりそうですWSL使えばいいんですが

どこまでパッケージを網羅しているかはちょっと未知数で心配ですが、しばらくはC++の環境設定には困らなさそうで嬉しいです。ぜひ良かったらvcpkgを使ってみてください。

Discussion