限定的だろうが vcpkg は救世主といえば救世主 (→ 2021-06-08追記: いやごめん、思ったよりちゃんと救世主)

こういうとても大事なものに気付くのが遅いとさすがに悔しい気分になる。

2021-06-08非常に恥ずかしい追記:
かなり筋違いな文句を書いてしまっていて。もちっと注意深くみれば良かったのだよなぁ。

たとえば下で「curl.exeがない」ことに対して文句を書いてしまったが、これは、RedHat 系 linux のパッケージマネージャに対するのと同じタイプの文句でないと正しくない。つまり「なんで -dev パッケージを別途インストールせにゃならんねん」と同列の文句だったら正しい。curl については「curl[tool]」というパッケージ名を指定すればインストール出来る。

既に皆に読まれてしまった文章は書き替えない。わーい、恥ずかしいやつだー、と笑ってくれれば良い。猛省。

すでに有名なんだろうか、それともワタシみたく全然気付いてなくて損し続けてる人の方がまだ多いんだろうか。ともあれワタシにとっては実に今「あー損してた」気分である。

誰にとって救世主なのか予め整理しておく必要がある。大まかには以下に該当するユーザである:

  1. ソフトウェア開発に比較的抵抗がない。
  2. Microsoft Visual C/C++ 開発環境を持っていて、それでビルドされるソフトウェアを作りたい/使いたい。
  3. 「標準的なライブラリ」として使える OSS を「標準的なやり方で」利用したい。
  4. おそらく CMake ユーザ。

要するに一言で言えば「パッケージマネージャ」なのであるが、Scoop とかとはっきり違うのは、ノリが「開発者だけのためのもの」であることである。(CMake は確証はない。ワタシは別件で必要だったので vcpkg 以前に導入済みなのだが、vcpkg の実行ログで cmake のキーワードがみえるので、前提にしてるのかもしれないと思った。)

正確な話を言えば、「開発者だけのためのもの」は、本当にそう思っているのかどうかがまずわからないし、そもそも私の持論として「開発者フレンドリがユーザフレンドリの源」となりうることが昔の Unix で存分に証明されていて、なのでこれも「結果的にエンドユーザのためにもなっている」。このことはあとで実例で示す。ただし、見出しにもした「限定的」に関係するが、結論的にはやはり「開発者だけのためのものだとの思い込みが結果的にエンドユーザのためにも開発者のためにもなってない」という側面もある。これも後述。

ともあれ vcpkg は要するにたとえば「JPEG にまつわる処理を自分で書くなんてバカげたことをせずに libjpeg をまるっと使って開発したい」といった場合に「~からソースをダウンロードしてきてこれをアーカイバで展開してお好きな場所に置いてREADMEに従ってプラットフォーム依存のビルドをしてビルドしたものをお好きな場所に置いて置いたそれをinclude、リンクして…」というのを、こうしたものが必要になるたびに「個々に個々」とするのではなく、こうした「誰がやっても同じであるべき作業」のオートメーションを目指すもの、である。「開発者だけのためのもの」と言った通り、エンドユーザが使うものではなくて、開発・ビルドに必要なヘッダファイル・共有ライブラリ(もしくは静的ライブラリ)だけをお取り寄せ、所定の場所で管理してくれる、そういうパッケージマネージャである。

導入は別に何も難しくない。公式説明に従って(必須かどうかはわからないがワタシは c:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Auxiliary/Build/vcvars64.bat を起動したコンソールから起動した bash で実行している):

 1 [me@host: ~]$ # ここでは c:/develop にしていてこれはもちろん任意だが、公式説明が推奨する通り
 2 [me@host: ~]$ # 「空白を含まないパスにしたほうがいい」には従ったほうが身のため。ワタシはつい
 3 [me@host: ~]$ # 最近まさにこの「空白を含むパスでトチるアホな OSS」に巡り合ったばかりである。
 4 [me@host: ~]$ mkdir /c/develop
 5 [me@host: ~]$ cd /c/develop
 6 [me@host: develop]$ # git を入れてない人は知らんよ、入れとけよ、使いたいなら。
 7 [me@host: develop]$ git clone https://github.com/microsoft/vcpkg
 8 [me@host: develop]$ cd vcpkg
 9 [me@host: vcpkg]$ cat ./bootstrap-vcpkg.bat
10 @echo off
11 powershell.exe -NoProfile -ExecutionPolicy Bypass "& {& '%~dp0scripts\bootstrap.ps1' %*}"
12 [me@host: vcpkg]$ # ↑この bat を実行すればいいのだが、BATを実行するのがダルいならダイレクトに:
13 [me@host: vcpkg]$ powershell.exe -NoProfile -ExecutionPolicy Bypass scripts/bootstrap.ps1

ワタシのこの例の場合は「c:/develop/vcpkg/vcpkg.exe」というのが、以後使えるパッケージマネージャの本体である。さっそくやってみる:

 1 [me@host: ~]$ /c/develop/vcpkg/vcpkg install curl --triplet x64-windows
 2 Computing installation plan...
 3 The following packages will be built and installed:
 4     curl[core,non-http,schannel,ssl,sspi,winssl]:x64-windows -> 7.74.0#4
 5   * zlib[core]:x64-windows -> 1.2.11#10
 6 Additional packages (*) will be modified to complete this operation.
 7 Detecting compiler hash for triplet x64-windows...
 8 A suitable version of git was not found (required v2.26.0). Downloading portable git v2.26.0...
 9 Downloading git...
10   https://github.com/git-for-windows/git/releases/download/v2.26.2.windows.1/PortableGit-2.26.2-32-bit.7z.exe -> C:\develop\vcpkg\downloads\PortableGit-2.26.2-32-bit.7z.exe
11 Extracting git...
12 A suitable version of 7zip was not found (required v18.1.0). Downloading portable 7zip v18.1.0...
13 Downloading 7zip...
14   https://www.nuget.org/api/v2/package/7-Zip.CommandLine/18.1.0 -> C:\develop\vcpkg\downloads\7-zip.commandline.18.1.0.nupkg
15 Extracting 7zip...
16 A suitable version of nuget was not found (required v5.5.0). Downloading portable nuget v5.5.0...
17 Downloading nuget...
18   https://dist.nuget.org/win-x86-commandline/v5.5.1/nuget.exe -> C:\develop\vcpkg\downloads\22ea847d-nuget.exe
19 A suitable version of powershell-core was not found (required v7.1.0). Downloading portable powershell-core v7.1.0...
20 Downloading powershell-core...
21   https://github.com/PowerShell/PowerShell/releases/download/v7.1.3/PowerShell-7.1.3-win-x86.zip -> C:\develop\vcpkg\downloads\PowerShell-7.1.3-win-x86.zip
22 Extracting powershell-core...
23 Could not locate cached archive: C:\Users\hhsprings\AppData\Local\vcpkg\archives\75\75f57de7eda097529de64fc4fa13a07df7b42108.zip
24 Could not locate cached archive: C:\Users\hhsprings\AppData\Local\vcpkg\archives\c0\c0f65f22e5537d1fd37271c57a17aad5d62306fb.zip
25 Starting package 1/2: zlib:x64-windows
26 Building package zlib[core]:x64-windows...
27 -- Downloading http://www.zlib.net/zlib-1.2.11.tar.gz -> zlib1211.tar.gz...
28 -- Extracting source C:/develop/vcpkg/downloads/zlib1211.tar.gz
29 -- Applying patch cmake_dont_build_more_than_needed.patch
30 -- Applying patch 0001-Prevent-invalid-inclusions-when-HAVE_-is-set-to-0.patch
31 -- Applying patch add_debug_postfix_on_mingw.patch
32 -- Applying patch 0002-android-build-mingw.patch
33 -- Using source at C:/develop/vcpkg/buildtrees/zlib/src/1.2.11-e9a093319e.clean
34 -- Configuring x64-windows
35 -- Building x64-windows-dbg
36 -- Building x64-windows-rel
37 -- Downloading https://repo.msys2.org/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-2-any.pkg.tar.zst -> msys-mingw-w64-i686-pkg-config-0.29.2-2-any.pkg.tar.zst...
38 -- Downloading https://repo.msys2.org/mingw/i686/mingw-w64-i686-libwinpthread-git-8.0.0.5906.c9a21571-1-any.pkg.tar.zst -> msys-mingw-w64-i686-libwinpthread-git-8.0.0.5906.c9a21571-1-any.pkg.tar.zst...
39 -- Using msys root at C:/develop/vcpkg/downloads/tools/msys2/50864d9fae363d6e
40 -- Fixing pkgconfig file: C:/develop/vcpkg/packages/zlib_x64-windows/lib/pkgconfig/zlib.pc
41 -- Fixing pkgconfig file: C:/develop/vcpkg/packages/zlib_x64-windows/debug/lib/pkgconfig/zlib.pc
42 -- Installing: C:/develop/vcpkg/packages/zlib_x64-windows/share/zlib/copyright
43 -- Performing post-build validation
44 -- Performing post-build validation done
45 Stored binary cache: C:\Users\hhsprings\AppData\Local\vcpkg\archives\75\75f57de7eda097529de64fc4fa13a07df7b42108.zip
46 Building package zlib[core]:x64-windows... done
47 Installing package zlib[core]:x64-windows...
48 Installing package zlib[core]:x64-windows... done
49 Elapsed time for package zlib:x64-windows: 2.074 min
50 Starting package 2/2: curl:x64-windows
51 Building package curl[core,non-http,schannel,ssl,sspi,winssl]:x64-windows...
52 -- Downloading https://github.com/curl/curl/archive/e052859759b34d0e05ce0f17244873e5cd7b457b.tar.gz -> curl-curl-e052859759b34d0e05ce0f17244873e5cd7b457b.tar.gz...
53 -- Extracting source C:/develop/vcpkg/downloads/curl-curl-e052859759b34d0e05ce0f17244873e5cd7b457b.tar.gz
54 -- Applying patch 0002_fix_uwp.patch
55 -- Applying patch 0004_nghttp2_staticlib.patch
56 -- Applying patch 0005_remove_imp_suffix.patch
57 -- Applying patch 0006_fix_tool_depends.patch
58 -- Applying patch 0007_disable_tool_export_curl_target.patch
59 -- Applying patch 0009_fix_openssl_config.patch
60 -- Applying patch 0010_fix_othertests_cmake.patch
61 -- Applying patch 0011_fix_static_build.patch
62 -- Applying patch 0012-fix-dependency-idn2.patch
63 -- Using source at C:/develop/vcpkg/buildtrees/curl/src/e5cd7b457b-f116018cb9.clean
64 -- Configuring x64-windows
65 -- Building x64-windows-dbg
66 -- Building x64-windows-rel
67 -- Installing: C:/develop/vcpkg/packages/curl_x64-windows/share/curl/curl-config
68 -- Using msys root at C:/develop/vcpkg/downloads/tools/msys2/50864d9fae363d6e
69 -- Fixing pkgconfig file: C:/develop/vcpkg/packages/curl_x64-windows/lib/pkgconfig/libcurl.pc
70 -- Fixing pkgconfig file: C:/develop/vcpkg/packages/curl_x64-windows/debug/lib/pkgconfig/libcurl.pc
71 -- Installing: C:/develop/vcpkg/packages/curl_x64-windows/share/curl/vcpkg-cmake-wrapper.cmake
72 -- Installing: C:/develop/vcpkg/packages/curl_x64-windows/share/curl/copyright
73 -- Performing post-build validation
74 -- Performing post-build validation done
75 Stored binary cache: C:\Users\hhsprings\AppData\Local\vcpkg\archives\c0\c0f65f22e5537d1fd37271c57a17aad5d62306fb.zip
76 Building package curl[core,non-http,schannel,ssl,sspi,winssl]:x64-windows... done
77 Installing package curl[core,non-http,schannel,ssl,sspi,winssl]:x64-windows...
78 Installing package curl[core,non-http,schannel,ssl,sspi,winssl]:x64-windows... done
79 Elapsed time for package curl:x64-windows: 7.963 min
80 
81 Total elapsed time: 21.35 min
82 
83 The package curl:x64-windows provides CMake targets:
84 
85     find_package(CURL CONFIG REQUIRED)
86     target_link_libraries(main PRIVATE CURL::libcurl)

明らかに curl とは本来無関係なものをたくさんお取り寄せてて、ゆえに時間もかかるが、最初の実行だから。

これの実行によって配置されることになったのはこういう構造である:

2021-06-07追記: あとで気付いたが、「vcpkg/installed」を使う方が便利と思う。
 1 [me@host: ~]$ pushd c:/develop/vcpkg/packages/
 2 [me@host: packages]$ find curl_x64-windows -type f
 3 curl_x64-windows/bin/libcurl.dll
 4 curl_x64-windows/bin/libcurl.pdb
 5 curl_x64-windows/BUILD_INFO
 6 curl_x64-windows/CONTROL
 7 curl_x64-windows/debug/bin/libcurl-d.dll
 8 curl_x64-windows/debug/bin/libcurl-d.pdb
 9 curl_x64-windows/debug/lib/libcurl-d.lib
10 curl_x64-windows/debug/lib/pkgconfig/libcurl.pc
11 curl_x64-windows/include/curl/curl.h
12 curl_x64-windows/include/curl/curlver.h
13 curl_x64-windows/include/curl/easy.h
14 curl_x64-windows/include/curl/mprintf.h
15 curl_x64-windows/include/curl/multi.h
16 curl_x64-windows/include/curl/options.h
17 curl_x64-windows/include/curl/stdcheaders.h
18 curl_x64-windows/include/curl/system.h
19 curl_x64-windows/include/curl/typecheck-gcc.h
20 curl_x64-windows/include/curl/urlapi.h
21 curl_x64-windows/lib/libcurl.lib
22 curl_x64-windows/lib/pkgconfig/libcurl.pc
23 curl_x64-windows/share/curl/copyright
24 curl_x64-windows/share/curl/curl-config
25 curl_x64-windows/share/curl/CURLConfig.cmake
26 curl_x64-windows/share/curl/CURLConfigVersion.cmake
27 curl_x64-windows/share/curl/CURLTargets-debug.cmake
28 curl_x64-windows/share/curl/CURLTargets-release.cmake
29 curl_x64-windows/share/curl/CURLTargets.cmake
30 curl_x64-windows/share/curl/vcpkg-cmake-wrapper.cmake
31 curl_x64-windows/share/curl/vcpkg_abi_info.txt

MSVC では環境変数 INCLUDE、LIB をこれらを指すようにセットしつつ、dll の場所を PATH に加えることで、すぐに「curl をリンクするアプリケーション」を作り始めることが出来ることになる。

一義的にはこれはまさに「開発者が開発時に」使うものである、ということがわかるだろう。Windows の通常のノリでは「一般ユーザにとっては知ったこっちゃない」内部事情である「べきだ」と考えてしまうのが残念ながら一般的。けれども、少なくとも昔の Unix 文化の考え方では「エンドユーザが vcpkg を使って何が悪い」という発想になる。現実にそれを選んだ場合、まずは開発者は「curl.dll をどこそこに置いて使ってください」といった説明をする必要もないし、インストーラを作って curl.dll を強制的に自アプリケーションフォルダにこっそり詰め込む必要もなくなり、開発者に必要なのは「あなたも vcpkg ユーザであれ」とお願いすることだけとなり、エンドユーザはそれに従うことで「なんかあちこちに curl.dll が入っててわちきのおぅいんどーずに curl.dll が100個入ってるよ気持ち悪い」とイライラする必要もなくなり、結果的にはエンドユーザもハッピーになるのだ。

そう、問題はまさにそれ。上の curl の例を真剣に凝視して欲しい。curl を使い慣れている人にとってはこの事実はなかなかに衝撃的である。わかる? 「curl.exe が対象外」。

curl 以外も試したが万事が万事このノリで、つまり、「エンドユーザが開発するわけねーじゃんゔぁーかゔぁーか」と丁度正反対の立場、「開発者がエンドユーザであるわけなかろーが、頭おかしいのか?」という賢い決断がなされちゃってるのだ。こりゃあかん…。

すなわち、「開発者とエンドユーザの境界がないことが結果的にはウィンウィンをもたらす」というメリットがちゃんと理解されていないことの弊害、てこと。今の例で言えば、「curl の開発者は curl.exe に依存するわけねーじゃん」と意味のわからない思い込みがなされている、てこと。開発者にとって。もちろんエンドユーザは「curl.exe 欲しさに vcpkg を使うことは出来ない」ということである。

そういうわけで、vcpkg のうまみてのは実に限定的で、まぁ相当に残念なシロモノなのではある。バージョニングの問題もクリアじゃない気がするしね。いつでも全部最新を使いたいわけではないわけよ、使いたい OSS の事情で、古いバージョンを維持する必要性ってのは結構発生するもんである。

だけどね。

curl を例にしたのは「curl.exe」の件だけが理由ではなくてね。「curl が依存するライブラリ」に注目ね。ssl が含まれてるよな? これ、自力でまかなうの、かなり大変よ。アセンブラまで駆使しちゃってる今どき珍しいレガシーなものだったりすることもあってね。結構厄介なんだわ。

だからつまりは「とてもビルドのハードルが高いものが vcpkg 化されてると、おっそろしく楽できる」ということであり、ゆえに、「ワタシが使いたいものが依存する 10 の OSS のうち 5個が「EXEが欲しいのに入れてくれないんでやんの」」みたいに不幸なことがあったとしても、残り5つが本来ビルドの手間がとてつもなく大変なものである限りは、相変わらず「vcpkg さま、ありがとぅ…ありがとぅ…ありがとぅ…」であることには違いはないのだ。(後日実際にこれで部分的に助けられたものについて書く予定。)

まぁとにかく、だな。この残念さも理解した上でてことにはなるけれど、それでもこれは結構オススメ。広まって欲しいと思う。OSS の作者が好んでこれに依存するようになってくればいいなと思う。


2021-08-19ちょっとだけ追記:
うーん、なんだかこの恥ずかしいネタに喰い付いてる人が多いみたいで、なんか全裸を見られてる気分。まぁいいか…、役に立ててるてことなのかもしれんのだから、少しは喜んでみるか…。それはともかく。

実例が面白くないものだったのが気になってたのよね。ちょっと個人的な用事に関係したものがたまたまあったので、紹介しとこうと思う。

「rar ファイルを手持ち」とする。これ、普段どうしてる? ワタシは、zip だの tar+gz/bzip2 等々の標準的なものはコマンドラインで展開してるけれど、マイナーなものは諦めて GUI の 7zip で展開してた。そんなに毎日毎日必要にはならんからね、「CUI でないとやっとれん」なんて気分にゃぁならん。けど、最近 Windows テーマで遊んでたなかで、フリーでカーソルとかを公開してるとこが、どういうわけだか「zip or DIErar」なのよね。ワタシのスクリプトの入力にするバリエーションのためにたくさんダウンロードしてたら、さすがに rar のアンパックも「GUI ではダルイ」くなってきた。

「コマンドラインから」の選択肢として、「unrar な CUI exe」を見繕うのもまぁいいんだけれど、どうせなら「いつでも python から使える状態がいいなぁ」と、unrar 出来る python パッケージを探してみたら、python-unrar を見つけた。ctypes でライブラリ(Windows の場合は unrar.dll)にインターフェイスする作りになってるので、「vcpkg が対応してるなら、それでいけんじゃね?」と思った、てこと。一応 python-unrar 自身の公式説明としては

For Windows you can also download the already compiled library (http://www.rarlab.com/rar/UnRARDLL.exe).

とのたまうているのだけれど、ABI さえ合ってるなら、vcpkg のものでだってイケるハズであろう?

やってみたら、なんの問題もなく:

1 [me@host: ~]$ /c/develop/vcpkg/vcpkg install unrar --triplet x64-windows
2    ...
3 [me@host: ~]$ py -3 -m pip install unrar
4    ...
5 [me@host: ~]$ export UNRAR_LIB_PATH=c:/develop/vcpkg/packages/unrar_x64-windows/bin/unrar.dll
6 [me@host: ~]$ py -3 -m unrar.rarfile -l 1627864425_crystallize_pointer_v2.rar
7    ...
8 [me@host: ~]$ py -3 -m unrar.rarfile -e 1627864425_crystallize_pointer_v2.rar .

「なんの問題もなく」とは言ったが、本当は python-unrar の作者がちょっと勘違い(もしくはお行儀違反)をやらかしているのが困るは困る。普通は「XXX_LIB_PATH」という環境変数を発明する場合は、これは「XXX が存在する場所(フォルダ)」を指定させるものであって、実体そのものを指す流儀は滅多にない。英語的になんの間違いもないのだが、伝統的に「XXX_LIB の PATH」でなく「XXX_LIB がある場所」なのだ、C 言語開発環境のためにこうしたものが使われ始めて以来ずっと。まぁ見なかったことにしてあげようと思う。

おそらく、「Windows では unixtools の(強烈に古い)EXE があるからそれ使えばいいらしいぜ、知らんけど」みたいに Windows ユーザを軽視してる OSS ってのは相当あるはず。そういうもの相手に、公式は説明していないけれど vcpkg のものを試みてみる、てのは、一度はやってみるのがいいんじゃなかろうか。(python 関係に関しては、python 2.7 と 3.6未満のものについては知らんよ。)