w64devkitのこの1年間
What's been going on in w64devkit the past year (nullprogram.com)
要約
w64devkitは過去1年間で多くの進化を遂げました。新たに共同メンテナーのPeter0x44氏が加わり、プロジェクトは新たな方向へと進んでいます。リリースセキュリティの強化、32ビット/64ビット両方をサポートするマルチリブツールチェーンへの移行、CMakeやCcacheなどの新ツールの追加、そしてパフォーマンス向上のための最適化が行われました。
全文翻訳
w64devkitのこの1年間の進捗
2026年9月20日
nullprogram.com/blog/2026/09/20/
過去1年間はw64devkitにとってエキサイティングな年でした。他のソフトウェアディストリビューションと同様に、w64devkitも決して完成することはありません。Peter0x44氏が共同メンテナーとして参加し、私が考えもしなかったようなアイデアでプロジェクトを良い、新しい方向へと推し進めてくれました。彼の改善の多くはアップストリームにも取り込まれており、w64devkitを使っていなくても恩恵を受けることができるでしょう。ここでは、この1年間の様々な細かな進捗について触れたいと思います。
リリースのセキュリティ
4月に、リリースパッケージへの署名を開始したことを発表しました。現在、w64devkit内のすべてのEXEおよびDLLは私のキーでコード署名されています。私の署名キーは、約10万ホストで観測された数千のユニークな署名のおかげで良好な評判を確立しました。これは、リリースに約300のバイナリを含めることの嬉しい副産物です。ユーザーは最近、セキュリティソフトウェアに関する問題を少なく感じるはずです。MSYS2も私の署名ツールであるaas-signを採用しました。このツールは現在w64devkitのリリースに含まれています。
ビルドはGitHub Actionsによって自動化されるようになり、私が(そして私だけが)新しいタグをプッシュしたときにトリガーされます。このプロセスはコード署名を行い、リリースを作成します。リリースの全工程は透明であり、リポジトリ内のソースから厳密に派生しています。どこにもカーテンの裏側はなく、秘密裏の改ざんを許しません。
しかし、それだけではありません。私はリリース不変性を有効にしました。公開時に、リリース成果物はロックされ、私自身でさえ変更することはできません。これは、成果物リストの末尾にあるリリース証明書の存在で確認できます。プロジェクトに関わる誰も、将来的に不正行為をして古いリリースに何かをこっそり追加することはできません。
ツールチェインの変更
x64リリースは「マルチリブ」ツールチェインになりました。つまり、x86リリースのスーパーセットとして、32ビットWindows用のプログラムもコンパイルできます。x86リリースの唯一の目的は、古いハードウェアや古いオペレーティングシステムでw64devkitを実行することです。x86リリースと同様に、デフォルトでWindows XPをターゲットとし、SSE2をサポートするCPU(つまりPentium 4以上)が必要です。
x86をターゲットにするには、コンパイルとリンク時に-m32を渡します。あるいは、i686-w64-mingw32アーキテクチャトリプルで始まるツールを使用するのがより良いでしょう。後者は一般的に簡単です。なぜなら、異なるツールは異なるスイッチを必要とし、プレフィックス付きのツールは適切な処理を自動的に行うエイリアスだからです。
$ x86_64-w64-mingw32-gcc -o hello64.exe hello.c
$ i686-w64-mingw32-gcc -o hello32.exe hello.c
マルチリブの追加は、予想よりも安価で簡単でした。これはPeter0x44氏のおかげです。すでに私自身のいくつかのプロジェクトで便利に使っています。
以前は、コンパイラを使用するためにw64devkitのbin/を$PATHに置く必要がありました。GCCが他の必要なツールを見つける方法がそれだったからです。これはもはや必要なく、パスを変更せずにスクリプトなどからpath/to/gcc.exeを呼び出すことができます。これには小さなGCCパッチが必要でした。
標準のCOFFオブジェクトフォーマットは最大65,535セクションをサポートします。このフォーマットが設計されたとき、これは十分すぎるほどだと思われたでしょうが、現代のC++プログラムは、特に多数のラムダ関数を使用するプログラムのデバッグビルドでは、これを簡単に超えることがあります。C++プロジェクトが成長すると、bigobj COFFフォーマット(40億以上のセクションをサポート)を要求しない限りビルドが失敗する閾値に達することがあります。これは煩わしく、ツールはこれを自動的に処理すべきです。
数年前に、Binutilsにデフォルトでbigobjを生成するようにパッチを当てたため、古い制限がビルドに影響することはありませんでした。ただし、Binutilsには標準COFFを要求するインターフェースがないため、必要に応じてダウングレードできません。なぜ必要になるのでしょうか?公式のGoツールチェイン(gc)のような、一部の単純なリンカーと連携する場合です!一部のリンカーはbigobjを消費できません。また、ツールチェインのオブジェクトフォーマットを自動検出する一部のビルド(当時libbacktraceなど)では、bigobjを知らないため、検出スクリプトが壊れることもあります。
理想的には、Binutilsは必要に応じてquietly bigobjにアップグレードするべきです。大きなプログラムだけがbigobjを必要とします。単純なリンカーや検出スクリプトは標準COFFのみを扱います。さて、Peter0x44氏のおかげで、それがBinutilsのアップストリームでの動作になりました!私のbigobjハックはもはや不要になり、cgoが再び動作するようになりました。
この1年間、w64devkitを可能な限り小さくすることへの重点を減らし、価値のある機能のためならインストールサイズや配布サイズが増加しても構わないと判断しました。これは、静的ランタイムや計算負荷の高いツールで-Osから-O2に切り替えたことで具現化されています。すべてが少し大きくなり、少し速くなりました。標準ライブラリで多くの時間を費やすプログラムは、少し速くなるでしょう。すでにパフォーマンスのために設計されたプログラムには違いはありません。
新しいツール
CMake、Ninja、そして新しいグラフィカルなCMakeデバッガがすべて含まれるようになりました。これらはWindows 7以降が必要です。CMakeをNinjaにデフォルト設定するようにパッチを当てました。CMakeのデフォルトはVisual Studioのインストールを探すことだからです。GNU Makeの代わりにNinjaを使用するために-G MinGW Makefilesを渡すこともできますが、推奨しません。Ninjaはより高速で堅牢です。
$ cmake -B build # Ninja用に設定
$ cmake --build build # Ninjaでビルド
ccmake(Peter0x44氏の提案)が含まれています。これはCMakeの設定を検査・変更するためのTUIフロントエンドです。予想以上に役立っています。並列ビルドのために-jを渡すことができますが、代わりに環境変数を使用することを強く推奨します。例えば、.profileに以下のように記述します。
export CMAKE_BUILD_PARALLEL_LEVEL="$(nproc)"
export CTEST_PARALLEL_LEVEL=$(nproc)
はい、並列テストも可能です!もし適切であれば、マルチコンフィグも推奨します。
$ cmake -B build -G 'Ninja Multi-Config'
$ cmake --build build --config Debug
$ cmake --build build --config Release
これをデフォルトにしようかと思いましたが、ビルドツリーのレイアウト(Debug/とRelease/ディレクトリ)が変更され、現実世界のCMakeLists.txtの驚くべき割合がマルチコンフィグで壊れます。なぜなら、それらは正しく書かれていないからです。世界には、静的にほとんどのケースを検出できる、優れたCMakeリンターが必要です。
補完するものとしてCcacheがあります。Peter0x44氏によるWindowsサポートを改善するための特別なパッチも適用されています。頻繁にブランチを切り替える場合、ccacheを使用すると(再)ビルドが速くなります。また、従来のCcache lib/ccache/ ディレクトリも設定しました。これはw64devkit内のディレクトリで、$PATHに追加すると、すべてのビルドを透過的にCcacheでバックアップします。典型的なLinuxディストリビューションでは、これは/usr/lib/ccache/になります。w64devkit.iniでpath typeを使用して有効にすることもできます。
path type = minimal+ccache
私の場合、Ccacheを追加したときほど有用ではありませんでした。代わりにGit worktreesワークフローを採用したため、各ブランチは独自のビルドツリーを持ったまま静止しています。
COMプログラミングを支援するため、キットには現在、Interface Definition Language(IDL)ファイルを生成するためのMicrosoft MIDLのオープンソース代替であるwidlとuuidgenが含まれています。uuidgenは、Microsoftのツールの私によるミニマリストな書き直しです。
G. Berthiaume氏が新しいツールmake2compdbを書き、MakeビルドからJSON Compilation Databaseファイルcompile_commands.jsonを抽出します。これはUnixフィルターとして動作します。
$ make -Bwn | make2compdb >compile_commands.json
私はrecycleというツールを作成しました。これはファイルやフォルダをリサイクルビンに送ります。フォルダの場合、rm -rfで削除するよりも速いことが多いです。他のシステムではtrashツールに相当します。私はファイルを削除することはほとんどなくなり、代わりにこのコマンドを使ってリサイクルビンに数週間置いておき、ストレージセンサーが自動的に削除するのを待っています。
数ヶ月前に、古いスタイルのパッチ管理ツールであるquiltの追加を発表しました。オリジナルのquiltはWindowsをサポートしていないため、これはC++での完全な書き直しです。今やすべてのプラットフォームでQuiltが利用可能になったため、w64devkitのパッチはQuiltで管理されています。
インストーラー作成ツールのNSISが現在含まれているため、独自のアプリケーションインストーラーを作成できます。コマンドラインツールであるmakensisのみです。これも「マルチリブ」構成になっており、x64 w64devkitは32ビットおよび64ビットのインストーラーを作成できます。(私がNSISを追加した動機は、私の代替alt-tabスイッチャーが、正しくインストールするためにインストーラーを必要としたことです。)
Zstandard zstd/unzstdが現在含まれています。ソースtarballは.tar.zst形式であることが多いためです。これは優れた圧縮フォーマットであり、圧縮が必要な場合は(gzipではなく)デフォルトとして使用すべきです。busybox-w32のtarは、.tar.zstにはzstdを使用するように認識します。
ランタイム
w64devkit固有の機能として、C11スレッドがMingw-w64ランタイムの一部になりました。これは私による新しい実装で、winpthreadsとは独立しており、winpthreadsよりも小さく、特別なコンパイラまたはリンカースイッチを必要としません。(注意すれば、CRTフリーのプログラムでも使用できます。)ただし、Windows 7以降が必要となります。なぜなら、SRWロックに基づいて構築したためです。XPではかなりの複雑さが必要だったでしょう。
実装を書くことで、C11スレッドは仕様が不十分で設計が悪いということを学びました。これは、再帰ロックの存在からすでに明らかだったかもしれませんが!このC標準の追加機能は、必要とされるほどの注意を払われず、カットされるべきでした(いつものこと)。私の実装は再帰ロックを除外しています。それを作成しようとすると常に失敗します。また、セマンティクスがWindowsスレッドモデルにマッピングしないため、thrd_currentは弱化されています。
std::terminateはexitを呼び出す代わりに実際に終了(トラップ)するため、GDBで検査するためにトラップします。これには未捕捉の例外も含まれます。また、不完全なスタックトレースを表示しなくなり、ほとんどのC++プログラムから約100KBのデッドウェイトを削減しました。これは1年かけて2段階で行われ、2番目の段階はごく最近でした。最初の変更でトラップを誤った場所に配置し、本来削減できたほどの重さを削減できませんでした。
将来の方向性
数年前、GCCとBinutilsのバグが原因でリンク時最適化(LTO)を無効にしました。ツールチェインで利用可能であるだけでLTOのバグがトリガーされました。これらの問題により、LTOが正しいプログラムを生成できるという私の信頼は低下したため、無効にしました。
しかし、LTOを再有効化するだけでなく、「FatLTO」ランタイム、特にC++とFortran向けに配布することも検討しています。つまり、ランタイムオブジェクトファイルには、ネイティブコード(上記の-O2レベル)とLTOバイトコードの両方が含まれます。LTOを要求しない場合、リンク時のコストなしに、今日と同じようにプリコンパイルされたネイティブコードが得られます。LTOを有効にすると、リンク時に基本的にランタイム自体を再ビルドします。計算負荷は非常に高いですが、完全な制御が得られ、好みに応じて古い-Os(あるいは-Oz)にさえ戻すことができます。
私の知る限り、誰もFatLTOランタイムを配布しているツールチェインを実際に配布していません。そのため、w64devkitが最初になるかもしれません。GCCでFatLTOオブジェクトのクロスコンパイルを行った最初の人物が私であることは知っています(w64devkitのすべてはクロスコンパイルされていることを思い出してください)。なぜなら、GCCのリリースではそれが正しく機能したことがないからです。私の最初の試みでは動作しないツールチェインが生成されました。
今回はより自信があります。なぜなら、LTOバグの修正が非常に容易になったからです。最先端のAIを優れたコーディングハーネスに接続し、何が壊れているかを示し、すべてのソース(source.tar)を与えれば、約15分後にパッチが得られます。私たちはSFの世界に生きています。私は既知のLTOバグを修正することから始め、FatLTOを有効にし、次に新しく発見されたLTOバグを修正しました。私はそれをドッグフーディングしており、しばらくの間新しい問題は発生していませんが、自信を持つにはまだ時間がかかるでしょう。Fortranランタイムのバグを発見する能力については、それほど自信がありません。アップストリームのGCCはこれらの修正を却下するため、私の選択ではなく、w64devkitはこの分野で独自の優位性を持つことになります。
新しい機能のいくつかはWindows 7以降を必要としますが、私はx86リリースでWindows XPをベースラインとしてサポートすることに引き続きコミットしています。私は古いXPラップトップを手元に置いており、そこでw64devkitをテストして楽しんでいます。古いツールで我慢する必要がありますが、それはあなたがすでにしていたことでもあります。