プログラミング
Show HN: 静的型付け、クロスプラットフォーム、容易にブートストラップ可能なビルドシステム
Show HN: A statically typed, cross-platform, easily bootstrappable build system (github.com)
要約
BUSYは、GCC、Clang、MSVCツールチェーン向けの軽量でクロスプラットフォームなビルドシステムです。静的型付けされたビルド仕様言語と、ホストシステムにほとんど依存せず容易にブートストラップできる点が特徴です。CMakeやMesonなどの既存システムと比較して、よりシンプルで柔軟な開発を目指しています。
全文翻訳
BUSYビルドシステムへようこそ
BUSY(BUild SYstemの略)は、GCC、Clang、MSVCツールチェーン向けの軽量でクロスプラットフォームなビルドシステムであり、システム要件が非常に少なく、容易にブートストラップできます。
CMake、QMake、Meson、GNなどの他のビルドシステムと比較して、BUSYは静的型付けされたビルド仕様言語と、ホストシステムにそれ以上の要件なしにプロジェクトを直接スクラッチからビルドできる可能性によって特徴付けられます。BUSYは非常に軽量であるため、プロジェクトのソースツリーに直接統合するのにも適しています。
BUSYを使用するプロジェクトの例を次に示します: https://github.com/rochus-keller/nappgui/
NAppGUIは、Francisco GarcíaによってC89(一部C++98およびObjective-C)で書かれた、包括的なクロスプラットフォームGUIライブラリです。プロジェクトのビルド方法についてはREADMEを参照し、ルートディレクトリおよびsrcディレクトリ(およびサブディレクトリ)にあるBUSYファイルを確認してください。
仕様言語の詳細については、以下およびsyntaxディレクトリを参照してください。
GNを知っている人は、BUSYに様々な概念を認識するでしょう。便宜上、いくつかの抜粋を以下に示します:
# トップレベルのBUSYファイルから
submod src
let shared_lib* = src.shared_lib
let static_lib* = src.static_lib
let all! : Group {
.deps = [ shared_lib static_lib ]
# 'let'が使用されているため、'deps'フィールドはここで'.'プレフィックスを使用してのみ設定できます
# 代わりに'var'が使用された場合、'all.deps += xyz'で他の場所で変更できます
}
# src BUSYファイルから
let main_config - : Config {
.include_dirs += [ ./geom2d ./osbs ./sewer /* いくつかのみ表示 */ ]
.defines += [ "NAPPGUI_LIBRARY"
"NAPPGUI_BUILD_DIR=\"" + tostring(root_build_dir) + "\""
"NAPPGUI_BUILD=\"" + readstring('../prj/build.txt') + "\""
]
}
submod core
submod draw2d
# その他多数
let all_lib_sources : Group {
# このBUSYファイルでのみ可視
.deps = [ core.sources draw2d.sources # その他多数
]
}
let static_lib* : Library {
.name = "NAppGUI"
# そうしないと、バイナリ名は"static_lib"になる
.lib_type = `static
.deps = [ all_lib_sources ]
}
# draw2d BUSYファイルから
let sources * : SourceSet {
.sources = [ ./draw2d.cpp ./drawg.cpp ./btext.c # その他多数
]
.configs += ^main_config # これはsrc BUSYファイル内のConfigを参照します
if target_os == `linux {
.deps += gtk3.sources
} else if target_os == `win32 {
# その他多数
} else {
error("target os not supported")
}
}
パスカルスタイルを好む人のために、この構文バージョンも有効です:
let sources * : SourceSet
begin
.sources := [ ./draw2d.cpp ./drawg.cpp ./btext.c ]
if target_os == `linux then
.deps += gtk3.sources
elsif target_os == `win32 then
# その他多数
else
error("target os not supported")
end
end
より複雑なBUSYの使用例として、Oberon+コンパイラおよびIDEがあります。ビルドの実行方法については、https://github.com/rochus-keller/LeanQt/blob/main/Readme.md を参照してください。また、Qtのmocおよびrccツールに対するBUSYの特別なサポートも実証しています。
BUSYはLua仮想マシンに基づいており、それに統合されています(ただし、LuaではなくC89で書かれています)。Luaは、すべてのプラットフォームでビルドするのが最も簡単なコードベースの1つです。唯一の要件はC89コンパイラです。BUSYはこの伝統に従い、Luaの作者の素晴らしい作品から恩恵を受けています。
BUSYと統合された、マルチコアビルドをサポートする強力なC/C++ IDEを探している場合は、LeanCreatorをチェックしてください。
なぜ別のビルドシステムなのか?
私は長年ビルドシステムを使用し、研究してきました。QMakeは過去20年間で最も多く扱ってきたビルドシステムですが、Qtを必要とし、大規模プロジェクトではGNほど柔軟ではありません。
私もいくつかのプロジェクトでCMakeを使用しており、20年前にVisualization Toolkitに関わるようになって以来、その開発を追ってきました。また、MesonやGNのような新しいプロジェクトも追跡しています。後者はDart VMのソースツリーを探索中に見つけ、それ用の分析ツール(https://github.com/rochus-keller/GnTools)さえ開発しました。その調査結果は、いくつかの記事の材料になるほどですが、ここでは簡単な概要のみを示します。
CMakeは、完全に装備された、そして残念ながらやや時代遅れのスクリプト言語です。「モダンCMake」は現在、個々のビルドステップがどのように進むべきかの命令的な詳細仕様ではなく、ターゲットとプロパティ(GNに似ています)にも焦点を当てています。それは正しい方向ですが、依然としてこの文字列型の動的スクリプト言語の限界を探求するもう一つのレイヤーに過ぎず、他のすべてのレイヤーがまだ透けて見えています。CMake自体は、一人の開発者がもはや完全に理解するのが難しい、巨大で複雑なシステムになっています。また、私が通常それを使ってビルドしたいほとんどのプロジェクトよりも大きく、複雑です。
Mesonは興味深いアプローチをもたらし、明示的にチューリング完全なプログラミング言語を回避しています。これは私の見解では正しい道です。抽象オブジェクトへの参照を介して結果にアクセスし、ファイルシステムのパスを抽象化するというアイデアも気に入っています。しかし残念ながら、言語はやや独特で、明らかにPythonに触発されており、依然として動的型付けです(文字列型以上の型があるとしても)。
ビルドシステムは常に、ソフトウェア開発者が可能な限り時間をかけたくないもののように思えます。それらは実際のソフトウェアエンジニアリングの芸術の焦点ではなく、副産物と見なされているようです。このことによってのみ、過去50年間のモジュール化、構造化プログラミング、オブジェクト指向プログラミング、静的型チェックといったソフトウェアエンジニアリングの成果が、なぜこれほどわずかな影響しか与えなかったのかを説明できます。ソフトウェアシステムが大きくなり、プラットフォームの独立性が必須となるにつれて、これは代償を払っています。
Chromiumは印象的な例であり、長年にわたり信じられないほど大きなシステムに成長し、現在では約4000万行のコードがあります。開発者はいくつかのビルドシステムを導入し、破棄してきました。数年前からGNが使用されており、これはChromiumのニーズのために特別に開発されたものです。GNは非常に強力ですが、かなり複雑でもあります。Chromiumよりもはるかに小さいDart VMのビルドプロセスを理解することさえ、容易ではありません。私のツールはこの点で役立ちましたが、静的分析に関して動的言語が提供できるものには本質的に限界があります。したがって、大規模なソフトウェアシステムのビルドシステムの開発、分析、保守は、動的言語で知られているのと同じような欠点に悩まされており、それが最終的にTypeScriptやDartのような開発につながりました。私の見解では、ビルドシステムが静的型付けと効果的なモジュール化手段を持つことは、とっくに必要とされていました。
しかし、私の見解では、以前のビルドシステムであまり考慮されてこなかった他の側面もあります。まず、ビルドシステム自体がどのように構築されるのか、そしてそれがどれほど高価で複雑になりうるのかという問題提起が必要です。ビルドシステムが既存のシステムとプリインストールされたコンポーネントに要求する要件は最小限であるべきです。GN、Meson、QMakeはこの点ではあまり良くありません。なぜなら、コンパイラと最近のバージョンのC++やPythonのためのかなり大きなライブラリが必要だからです。私はかつて、最小限の、最小限に削減されたQtクラスのセットを持つQMakeのスタンドアロンバージョンを作成しました。しかし、それはまだ200近くのソースコードファイルで構成されており、g++ *.cppのようなものでコンパイルしようとすると、利用可能なメモリをすべて消費し、数秒でクラッシュします。GNとMesonはどちらもNinjaを必要としますが、これはさらに別のC++コードベースであり、ビルドする必要があります。
では、C89互換コンパイラのみを必要とし、数百キロバイトの単一バイナリを生成するLuaのようなものはいかがでしょうか?そして、gcc *.cはLuaで完全に機能します。また、C89で静的型付け言語のパーサーを書くことも可能です。
これが、私が自分の運を試してみようと思った理由です。
BUSYの実行
BUSYは通常、build.luaスクリプトの後に引数を追加して開始されます。build.luaは、Cで実装されたLua関数への単なるファサードであり、ビルドを拡張または変更することを可能にします。