はじめに
WindowsのC/C++ネイティブ開発環境でオープンソースライブラリを利用する場合、NuGetから取ってるくるのが手っ取り早いが、Nuget上にあるものの大半が非公式な野良パッケージであり、バージョンが古かったり、信頼性に乏しく利用に躊躇してしまう。
そこで、Microsoft C++ チームが保守しているという触れ込みの vcpkg を利用して、オープンソースライブラリをNuGetパッケージ化(.nupkg)して利用することにした。
スマートで良い方法だと思ったのだが、vcpkgには罠が多数仕掛けられていた。それらに全て嵌った体験記である。
作業環境
- Windows10 21H1
- Visual Studio Community 2019: Version 16.11.3
vcpkgとは
About vcpkg
vcpkg is a free C/C++ package manager for acquiring and managing libraries. Choose from over 1500 open source libraries to download and build in a single step or add your own private libraries to simplify your build process. Maintained by the Microsoft C++ team and open source contributors.
上記公式サイトの説明通り、C/C++ 用のパッケージマネージャである。
元々は Visual Studio(msbuild)用であったが、CMakeにも対応したことで linux,osx,mingw,android,ios 用にも使えるものになっているらしい。
有名なオープンソースを網羅しており、私が必要としていた zlib/minizip, sqlite3, libpng, freetype はすべて最新バージョンが揃っている。
対応パッケージが オンライン で検索できるのも素晴らしい。
Microsoft C++ チーム保守で安心感もある。
vcpkg本体のインストール
vcpkg公式サイトのGet startedの説明通り、CMDプロンプト上で github から clone して、指定のバッチを実行するだけ。
git clone https://github.com/microsoft/vcpkg
cd vcpkg
bootstrap-vcpkg.bat
あっさり成功した。
vcpkg help
でコマンド一覧が、
vcpkg help コマンド名
で各コマンドの詳細オプションがコンソールに表示されるので、つらつら眺めておく。
zlib/minizip パッケージのインストール
vcpkg環境が出来上がったので、zlib/minizipパッケージを取得する。
まずは正確なパッケージ名を search コマンドで調べる。
E:\build\vcpkg>vcpkg search minizip
minizip 1.2.11#9 Zip compression library
minizip[bzip2] Support compression using bzip2 library
minizip-ng 3.0.2#1 minizip-ng is a zip manipulation library written in C that is supported on...
minizip-ng[bzip2] Enables BZIP2 compression
minizip-ng[lzma] Enables LZMA compression
minizip-ng[openssl] Enables OpenSSL for encryption
minizip-ng[pkcrypt] Enables PKWARE traditional encryption
minizip-ng[signing] Enables zip signing support
minizip-ng[wzaes] Enables WinZIP AES encryption
minizip-ng[zlib] Enables ZLIB compression
minizip-ng[zstd] Enables ZSTD compression
quazip 0.9.1#1 Qt/C++ wrapper over minizip
The search result may be outdated. Run `git pull` to get the latest results.
If your library is not listed, please open an issue at and/or consider making a pull request:
https://github.com/Microsoft/vcpkg/issues
minizip で良いらしい。 minizip-ng という派生パッケージなら lzma圧縮 や zstd圧縮 も使えるようだ。
だがとりあえずは本家の minizip を install する。
E:\build\vcpkg>vcpkg install minizip
Computing installation plan...
A suitable version of cmake was not found (required v3.21.1). Downloading portable cmake v3.21.1...
Downloading cmake...
https://github.com/Kitware/CMake/releases/download/v3.21.1/cmake-3.21.1-windows-i386.zip -> E:\build\vcpkg\downloads\cmake-3.21.1-windows-i386.zip
Extracting cmake...
A suitable version of 7zip was not found (required v19.0.0). Downloading portable 7zip v19.0.0...
Downloading 7zip...
https://www.7-zip.org/a/7z1900-x64.msi -> E:\build\vcpkg\vcpkg\downloads\7z1900-x64.msi
Extracting 7zip...
The following packages will be built and installed:
minizip[core]:x86-windows -> 1.2.11#9
* zlib[core]:x86-windows -> 1.2.11#12
Additional packages (*) will be modified to complete this operation.
Warning: The following VS instances are excluded because the English language pack is unavailable.
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community
Please install the English language pack.
No suitable Visual Studio instances were found
★嵌りポイントその1: 英語言語パック
パッケージのダウンロードは成功したが、Please install the English language pack. と文句を言われる。
英語言語パックの追加
なぜ英語の言語パックが必要なのか疑問を感じるが、指示通りに入れることにする。
Visual Studio インストーラを起動し、言語パック>英語 をチェックしてインストール実行。
英語の言語パックを入れた後に再び install 実行
E:\build\vcpkg>vcpkg install minizip
Computing installation plan...
The following packages will be built and installed:
minizip[core]:x86-windows -> 1.2.11#9
* zlib[core]:x86-windows -> 1.2.11#12
Additional packages (*) will be modified to complete this operation.
Detecting compiler hash for triplet x86-windows...
Restored 2 packages from C:\Users\hkuno\AppData\Local\vcpkg\archives in 164.4 ms. Use --debug to see more details.
Starting package 1/2: zlib:x86-windows
Installing package zlib[core]:x86-windows...
Elapsed time for package zlib:x86-windows: 175.9 ms
Starting package 2/2: minizip:x86-windows
Installing package minizip[core]:x86-windows...
Elapsed time for package minizip:x86-windows: 59.27 ms
Total elapsed time: 8.773 s
The package minizip provides CMake targets:
find_package(minizip CONFIG REQUIRED)
target_link_libraries(main PRIVATE minizip::minizip)
minizip 1.2.11#9 と同時に zlib 1.2.11#12 も入った。
minizip パッケージには zlib 機能が入っておらず、別途 zlibパッケージ を参照するようだ。
従来は minizip と zlib が一体化した zlibstat.lib を使っていたので、vcpkg利用に伴いライブラリファイル指定の修正が必要となる。
★嵌りポイントその2:triplet 指定が必要
従来使っていた zlibstat.lib は、minizipとzlibを束ねたスタティックライブラリであり、/MD オプション(MSVCRT)でビルドしていた。だが、今回 vcpkg で install できた zlib.lib はスタティックライブラリではなく zlib1.dll へのインポートライブラリであった。従来と同じビルド構成の zlib が欲しい。
vcpkgは、様々なアーキテクチャとビルド構成に対応している。
その組み合わせを triplet と呼ぶ。その一覧を見てみよう。
E:\build\vcpkg>vcpkg help triplet
Available architecture triplets
VCPKG built-in triplets:
arm-uwp
arm64-windows
x64-linux
x64-osx
x64-uwp
x64-windows-static
x64-windows
x86-windows
VCPKG community triplets:
arm-android
arm-ios
arm-linux
arm-mingw-dynamic
arm-mingw-static
arm-neon-android
arm-windows-static
arm-windows
arm64-android
arm64-ios
arm64-linux
arm64-mingw-dynamic
arm64-mingw-static
arm64-osx-dynamic
arm64-osx
arm64-uwp
arm64-windows-static-md
arm64-windows-static
armv6-android
ppc64le-linux
s390x-linux
wasm32-emscripten
x64-android
x64-freebsd
x64-ios
x64-mingw-dynamic
x64-mingw-static
x64-openbsd
x64-osx-dynamic
x64-windows-static-md
x86-android
x86-freebsd
x86-ios
x86-mingw-dynamic
x86-mingw-static
x86-uwp
x86-windows-static-md
x86-windows-static
x86-windows-v120
従来使っていた zlibstat.lib と同等なのは、デフォルトの x86-windows
ではなく、x86-windows-static-md
のようだ。
vcpkg install パッケージ名:triplet名
とすることで、インストールするtripletを指示する。
triplet=x86-windows-static-md で install 実行
E:\test\vcpkg>vcpkg install minizip:x86-windows-static-md
Computing installation plan...
The following packages will be built and installed:
minizip[core]:x86-windows-static-md -> 1.2.11#9
* zlib[core]:x86-windows-static-md -> 1.2.11#12
Additional packages (*) will be modified to complete this operation.
Detecting compiler hash for triplet x86-windows-static-md...
Restored 2 packages from C:\Users\hkuno\AppData\Local\vcpkg\archives in 127.6 ms. Use --debug to see more details.
Starting package 1/2: zlib:x86-windows-static-md
Installing package zlib[core]:x86-windows-static-md...
Elapsed time for package zlib:x86-windows-static-md: 207.6 ms
Starting package 2/2: minizip:x86-windows-static-md
Installing package minizip[core]:x86-windows-static-md...
Elapsed time for package minizip:x86-windows-static-md: 56.58 ms
Total elapsed time: 3.799 s
The package minizip provides CMake targets:
find_package(minizip CONFIG REQUIRED)
target_link_libraries(main PRIVATE minizip::minizip)
ようやく、欲しいパッケージが手に入った。
zlib/minizip パッケージのNuGet化
vcpkgでインストールしたライブラリパッケージは、vcpkg integrate install
にて、ホストマシンの MSBuild / Visual Studio にて、オプション設定なしにincludeとlinkが出来る、つまり標準ライブラリと同等の扱いになるのだが、各ソリューション毎に使うパッケージを分けたい場合には不都合である。
そこで、ライブラリパッケージをNuGet化して、各プロジェクトに登録する方法をとる。
NuGet形式ファイルを作るコマンドは vcpkg export パッケージ名:Triplet名 --nuget
だ。
E:\build\vcpkg>vcpkg export minizip --nuget
The following packages are already built and will be exported:
minizip:x86-windows
* zlib:x86-windows
Additional packages (*) need to be exported to complete this operation.
Exporting package zlib:x86-windows...
Exporting package minizip:x86-windows...
Packing nuget package...
NuGet package exported at: E:\test\vcpkg\vcpkg-export-20211004-181805.1.0.0.nupkg
With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste:
Install-Package vcpkg-export-20211004-181805 -Source "E:\build\vcpkg"
※ 上記は triplet名を指定し忘れたので x86-windows 用が作成されてしまった。
★嵌りポイントその3:NuGetパッケージ名やバージョン番号がダメ
nupkg ファイルが出来上がったが、NuGet名が生成日付時刻で、バージョン番号も 1.0.0 固定である。
Visual Studio の NuGetパッケージ一覧でも、この日付時刻がパッケージ名として出てくるので使い辛い。
exportの詳細オプションを vcpkg help export
で確認する。
E:\build\vcpkg>vcpkg help export
Example:
vcpkg export zlib zlib:x64-windows boost --nuget
Options:
--dry-run Do not actually export
--raw Export to an uncompressed directory
--nuget Export a NuGet package
--ifw Export to an IFW-based installer
--zip Export to a zip file
--7zip Export to a 7zip (.7z) file
--x-chocolatey Export a Chocolatey package (experimental feature)
--prefab Export to Prefab format
--prefab-maven Enable maven
--prefab-debug Enable prefab debug
--x-all-installed Export all installed packages
--output=... Specify the output name (used to construct filename)
--output-dir=... Specify the output directory for produced artifacts
--nuget-id=... Specify the id for the exported NuGet package (overrides --output)
--nuget-description=... Specify a description for the exported NuGet package
--nuget-version=... Specify the version for the exported NuGet package
--ifw-repository-url=... Specify the remote repository URL for the online installer
--ifw-packages-directory-path=...
Specify the temporary directory path for the repacked packages
--ifw-repository-directory-path=...
Specify the directory path for the exported repository
--ifw-configuration-file-path=...
Specify the temporary file path for the installer configuration
--ifw-installer-file-path=... Specify the file path for the exported installer
--x-maintainer=... Specify the maintainer for the exported Chocolatey package
(experimental feature)
--x-version-suffix=... Specify the version suffix to add for the exported Chocolatey
package (experimental feature)
--prefab-group-id=... GroupId uniquely identifies your project according maven
specifications
--prefab-artifact-id=... Artifact Id is the name of the project according maven
specifications
--prefab-version=... Version is the name of the project according maven specifications
--prefab-min-sdk=... Android minimum supported sdk version
--prefab-target-sdk=... Android target sdk version
--triplet=<t> Specify the target architecture triplet. See 'vcpkg help triplet'
(default: %VCPKG_DEFAULT_TRIPLET%)
--host-triplet=<t> Specify the host architecture triplet. See 'vcpkg help triplet'
(default: %VCPKG_DEFAULT_HOST_TRIPLET%)
--overlay-ports=<path> Specify directories to be used when searching for ports
(also: %VCPKG_OVERLAY_PORTS%)
--overlay-triplets=<path> Specify directories containing triplets files
(also: %VCPKG_OVERLAY_TRIPLETS%)
--binarysource=<path> Add sources for binary caching. See 'vcpkg help binarycaching'
--x-asset-sources=<path> Add sources for asset caching. See 'vcpkg help assetcaching'
--downloads-root=<path> Specify the downloads root directory
(default: %VCPKG_DOWNLOADS%)
--vcpkg-root=<path> Specify the vcpkg root directory
(default: %VCPKG_ROOT%)
--x-buildtrees-root=<path> (Experimental) Specify the buildtrees root directory
--x-install-root=<path> (Experimental) Specify the install root directory
--x-packages-root=<path> (Experimental) Specify the packages root directory
--x-scripts-root=<path> (Experimental) Specify the scripts root directory
--x-builtin-ports-root=<path> (Experimental) Specify the packages root directory
--x-builtin-registry-versions-dir=<path>
(Experimental) Specify the versions root directory
--x-json (Experimental) Request JSON output
--nuget-id=...
--nuget-description=...
--nuget-version=...
この辺りのオプションを使えば良い感じだ。
パッケージ名とバージョン番号、説明を指定してNuGet化する
E:\build\vcpkg>vcpkg export minizip:x86-windows-static-md --nuget --nuget-id=vcpkg-zlib-minizip --nuget-version=1.2.11 --nuget-description="zlib 1.2.11 and minizip. x86-windows-static-md built"
The following packages are already built and will be exported:
minizip:x86-windows-static-md
* zlib:x86-windows-static-md
Additional packages (*) need to be exported to complete this operation.
Exporting package zlib:x86-windows-static-md...
Exporting package minizip:x86-windows-static-md...
Packing nuget package...
NuGet package exported at: E:\build\vcpkg\vcpkg-zlib-minizip.1.2.11.nupkg
With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste:
Install-Package vcpkg-zlib-minizip -Source "E:\build\vcpkg"
これで、望みのNuGetパッケージが出来た。
生成メッセージの最終行にあるように、Visual Studio の NuGetパッケージマネージャコンソールにて Install-Package vcpkg-zlib-minizip -Source "E:\build\vcpkg"
と打ち込めば、プロジェクトに登録される。
作成するNuGetパッケージが一個だけなら以上で終了だが、複数に増える場合は Visual Studio の IDE 上で一覧選択して、プロジェクトに登録したい。
vcpkg のフォルダをNuGetパッケージソースとして、Visual Studio に登録する
- Visual Studio の IDE を起動して、ツール>オプション>NuGetパッケージマネージャー>パッケージソース を開く
- 左上の + ボタンを押して、名前[vcpkg]、ソース[vcpkgインストールフォルダ] を設定する
vcpkgで生成したNuGetパッケージをプロジェクトにインストールする。
- Visual Studio の IDE でソリューションを開く
- ツール>NuGetパッケージマネージャー>ソリューションのNuGetパッケージマネージャの管理 を開く
- 参照タブ>パッケージソース:[vcpkg] を選ぶ
- vcpkgフォルダ下の *.nupkg が一覧表示されるので、そこから選んでプロジェクトにインストールする
- パッケージの名前と説明とバージョンは、*.nupkg作成時に指定した --nuget-id=、--nuget-description=、--nuget-version= の内容になっている
★嵌りポイントその4:triplet=x86-windows-static-md なvcpkgを使うには、構成プロパティに設定が必要
NuGet化してインストールしたのだから一発でビルド成功、とはならなかった。
ヘッダが見つからずコンパイルエラー、libが見つからずにリンクエラーが出る。
デフォルトの triplet=x86-windows なら問題ないが、triplet=x86-windows-static-md の場合は、プロジェクトプロパティに こっそり追加されたvcpkgプロパティに、Triplet: x86-windows-static-md を追加設定する必要がある。
プロジェクトフォルダの packages 下のファイルをしらみつぶしに調べたら、vcpkg.targets というファイルにて、ヘッダーサーチパス指定 AdditionalIncludeDirectories と ライブラリサーチパス指定 AdditionalLibraryDirectories へパッケージパスを追加登録する処理があり、そのパス名を決める元となる $(VcpkgTriplet)
の自動設定が、$(VcpkgPlatformTarget)-$(VcpkgOSTarget)
と $(VcpkgPlatformTarget)-$(VcpkgOSTarget)-static
にしか対応していなかった。つまり今回の場合は x86-windows
と x86-windows-static
である。
それ以外の triplet については vcxproj 内で $(VcpkgTriplet)
マクロをユーザが手動設定して使う設計らしい。これ、エラーメッセージに明示して欲しかった。
<PropertyGroup>
<_ZVcpkgLinkage />
<_ZVcpkgLinkage Condition="'$(VcpkgUseStatic)' == 'true'">-static</_ZVcpkgLinkage>
<VcpkgTriplet Condition="'$(VcpkgTriplet)' == ''">$(VcpkgPlatformTarget)-$(VcpkgOSTarget)$(_ZVcpkgLinkage)</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup>
<_ZVcpkgCurrentInstalledDir>$(_ZVcpkgInstalledDir)\$(VcpkgTriplet)\</_ZVcpkgCurrentInstalledDir>
<_ZVcpkgNormalizedConfiguration Condition="$(VcpkgConfiguration.StartsWith('Debug'))">Debug</_ZVcpkgNormalizedConfiguration>
<_ZVcpkgNormalizedConfiguration Condition="$(VcpkgConfiguration.StartsWith('Release')) or '$(VcpkgConfiguration)' == 'RelWithDebInfo' or '$(VcpkgConfiguration)' == 'MinSizeRel'">Release</_ZVcpkgNormalizedConfiguration>
<_ZVcpkgConfigSubdir Condition="'$(_ZVcpkgNormalizedConfiguration)' == 'Debug'">debug\</_ZVcpkgConfigSubdir>
<_ZVcpkgExecutable>$(_ZVcpkgRoot)vcpkg.exe</_ZVcpkgExecutable>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(VcpkgEnabled)' == 'true'">
<Lib>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories);$(_ZVcpkgCurrentInstalledDir)$(_ZVcpkgConfigSubdir)lib;$(_ZVcpkgCurrentInstalledDir)$(_ZVcpkgConfigSubdir)lib\manual-link</AdditionalLibraryDirectories>
</Lib>
<Link>
<AdditionalDependencies Condition="'$(VcpkgAutoLink)' != 'false'">%(AdditionalDependencies);$(_ZVcpkgCurrentInstalledDir)$(_ZVcpkgConfigSubdir)lib\*.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories);$(_ZVcpkgCurrentInstalledDir)$(_ZVcpkgConfigSubdir)lib;$(_ZVcpkgCurrentInstalledDir)$(_ZVcpkgConfigSubdir)lib\manual-link</AdditionalLibraryDirectories>
</Link>
<ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(_ZVcpkgCurrentInstalledDir)include</AdditionalIncludeDirectories>
</ClCompile>
<ResourceCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(_ZVcpkgCurrentInstalledDir)include</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemDefinitionGroup>
まとめ
vcpkg は良いオープンソースプロジェクトであるが、まだまだ情報が足らない。
ビルド成功に辿り着くまで長い時間を費やした。
この記事が、特殊な triplet でビルドが出来ずに悩んでいる諸氏の助けになれば幸いである。