LoginSignup
5
2

More than 1 year has passed since last update.

vcpkg で x86-windows-static-md なNuGetパッケージを生成して使おうとして嵌った件

Last updated at Posted at 2021-10-04

はじめに

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 インストーラを起動し、言語パック>英語 をチェックしてインストール実行。

vcpkg-install-1-add-enpack.png

英語の言語パックを入れた後に再び 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 に登録する

  1. Visual Studio の IDE を起動して、ツール>オプション>NuGetパッケージマネージャー>パッケージソース を開く
  2. 左上の + ボタンを押して、名前[vcpkg]、ソース[vcpkgインストールフォルダ] を設定する

vcpkg-install-2-set-vcpkg-dir.png

vcpkgで生成したNuGetパッケージをプロジェクトにインストールする。

  1. Visual Studio の IDE でソリューションを開く
  2. ツール>NuGetパッケージマネージャー>ソリューションのNuGetパッケージマネージャの管理 を開く
  3. 参照タブ>パッケージソース:[vcpkg] を選ぶ
  4. vcpkgフォルダ下の *.nupkg が一覧表示されるので、そこから選んでプロジェクトにインストールする
  5. パッケージの名前と説明とバージョンは、*.nupkg作成時に指定した --nuget-id=、--nuget-description=、--nuget-version= の内容になっている

vcpkg-install-3-select-nuget.png

★嵌りポイントその4:triplet=x86-windows-static-md なvcpkgを使うには、構成プロパティに設定が必要

NuGet化してインストールしたのだから一発でビルド成功、とはならなかった。
ヘッダが見つからずコンパイルエラー、libが見つからずにリンクエラーが出る。
デフォルトの triplet=x86-windows なら問題ないが、triplet=x86-windows-static-md の場合は、プロジェクトプロパティに こっそり追加されたvcpkgプロパティに、Triplet: x86-windows-static-md を追加設定する必要がある。

vcpkg-install-4-set-triplet-vcxproj.png

プロジェクトフォルダの packages 下のファイルをしらみつぶしに調べたら、vcpkg.targets というファイルにて、ヘッダーサーチパス指定 AdditionalIncludeDirectories と ライブラリサーチパス指定 AdditionalLibraryDirectories へパッケージパスを追加登録する処理があり、そのパス名を決める元となる $(VcpkgTriplet) の自動設定が、$(VcpkgPlatformTarget)-$(VcpkgOSTarget)$(VcpkgPlatformTarget)-$(VcpkgOSTarget)-static にしか対応していなかった。つまり今回の場合は x86-windowsx86-windows-static である。
それ以外の triplet については vcxproj 内で $(VcpkgTriplet) マクロをユーザが手動設定して使う設計らしい。これ、エラーメッセージに明示して欲しかった。

scripts\buildsystems\msbuild\vcpkg.targets
  <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 でビルドが出来ずに悩んでいる諸氏の助けになれば幸いである。

参考記事

END

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2