仕事では久しく C++ のコードを書いていませんが、最近 iEdit で JSON を扱えるようにしようと思って C++ の REST 関連ライブラリを物色していたところ、Microsoft が C++ REST SDK という OSS ライブラリを開発しているのを知りました。
Java でいうと Jersey と Jackson が一緒になったようなライブラリです。JSON の読み書きしか使いませんが、なかなかよいので採用することにしました。
最初は NuGet でパッケージをインストールして開発してました。必要な C++ のヘッダーファイルと DLL が降ってくるので開発は問題なくできるのですが、配布の際に DLL を同梱する必要があります。利用者の利便性を考えると極力ワンバイナリで提供したいので static リンクする方法を調べました。
昔は C/C++ のライブラリは、個別にソースコードをダウンロードして make / make install してました。Linux だと APT などのパッケージマネージャでバイナリを取得・インストールできますし、Mac の brew のようにソースコードベースのパッケージ管理システムもあります。
今回知ったのですが、Microsoft も近年クロスプラットフォームのソースコードベースパッケージ管理システムを OSS として開発しています。
ということで、まず vcpkg をビルド (PowerShell で実行します)。
PS> .¥bootstrap-vcpkg.bat
vcpkg.exe が生成されたディレクトリが vcpkg のルートディレクトリとなり、配下にパッケージを取得してビルド・インストールしていきます。
この時点で Visual Studio とのインテグレートをやっておきます。
PS> .¥vcpkg integrate install
あとは、導入したいパッケージを指定してインストールするだけです。
C++ REST SDK を公式 README 通りに以下のコマンドでインストールしました。
PS> .¥vcpkg install cpprestsdk cpprestsdk:x64-windows
C++ REST SDK は Boost C++ ライブラリに依存しているので、2コアの Core i5 マシンだとビルドに48分ぐらいかかりました。
その後、自プロジェクトから NuGet 版を削除してリビルド。エラーなく完了しました。
これは便利。integrate install しているため Visual C++ のプロジェクト設定で include パスの指定や lib の追加をしなくても大丈夫になっています。OSS のライブラリを気軽に試せますね。
ところで、生成されたバイナリのフォルダを確認したところ削除したはずの cpprest_2_10.dll がいました。ダイナミックリンク版になってしまっているようです。これだと時間かけてビルドしたのに NuGet と同じ結果です。
StackOverflow に QA がありました。
static link 版はパッケージ名が違いました。しかも static link 版は integrate install していても自プロジェクト側で include パスなどを設定する必要があるそうです。
ということでまず static link 版をインストールしました。
PS> .¥vcpkg install cpprestsdk:x86-windows-static
その上で自プロジェクトの構成プロパティを設定(後述)。しかし、自プロジェクトをビルドするとやっぱり DLL を取ってきてしまいます。どうも DLL 版のパッケージがインストールされているとそちらを優先するようです。そこで DLL 版パッケージを削除しました。
PS> .¥vcpkg remove cpprestsdk:x64-windows
この状態でビルドしたところ、無事に static link されたバイナリが生成されました。
自プロジェクト側の構成プロパティ設定です。
C/C++ → プリプロセッサ → プリプロセッサの定義に _NO_ASYNCRTIMP
を追加します。
C/C++ → コード生成 → ランタイムライブラリで マルチスレッド(MT)
を選択します。
VC++ ディレクトリ → 全般 → ライブラリディレクトリに vcpkg のインストール先の lib ディレクトリを追加します。
リンカー → 入力 → 追加の依存ファイルに静的リンクしたいライブラリのファイル名を追加します。
C/C++ → 全般 → 追加のインクルードディレクトリに vcpkg のインストール先の include ディレクトリを追加します。
ディレクトリは相対パス指定がうまくできなかったので $(HOMEDRIVE)$(HOMEPATH)
で逃げました。
ちょっと面倒ですが、NuGet で DLL 取得するのではなく vcpkg でビルドすることで好みの設定で使うことが可能ということですね。