これはMesonの当初の設計思想です。ここで説明されている構文は、リリースされたバージョンとは一致しません。

ソフトウェア開発者にとって最も重要なツールはエディターです。コーダーに彼らが使用しているエディターについて尋ねると、通常、非常に熱狂的な称賛の言葉が返ってきます。Emacsがいかに素晴らしいか、viがいかにエレガントか、Eclipseの統合機能がいかに生産性を高めるかを聞くでしょう。人々がこれらのプログラムに対して抱いている熱意と愛情を感じることができます。

コンパイラよりもさらに重要な、2番目に重要なツールはビルドシステムです。

それらはほぼ普遍的に軽蔑されています。

通常、得られるビルドシステムに関する最も肯定的な声明は(多少の説得が必要になるかもしれませんが)、「まあ、ひどいシステムだけど、他のすべての選択肢はさらに悪い」というものです。これがなぜそうなのかは簡単に理解できます。まず、一般的に使用されるフリーのビルドシステムは、難解な構文を持っています。それらはほとんどの場合、ランダムな場所に設定されたグローバル変数を使用するため、特定のコード行が何をするのかを実際に見つけることはできません。それらはあらゆる場面で奇妙で予測不可能なことを行います。

簡単な例でこれを説明しましょう。GNU AutotoolsでビルドされたプログラムをGDBで実行したいとします。直感的に行うべきことは、gdb programnameを実行することです。問題は、これが機能する場合もあれば、機能しない場合もあるということです。実行可能ファイルがバイナリである場合もあれば、隠されたサブディレクトリにある実際のバイナリを呼び出すラッパースクリプトである場合もあります。バイナリがスクリプトの場合、GDBの呼び出しは失敗しますが、そうでない場合は成功します。ユーザーは、デバッグできるようにするために、実行可能ファイルのタイプ(ビルドシステムの実装の詳細)をそれぞれ覚えておく必要があります。他のいくつかの問題点は、このブログ記事にあります。

これらの特異性を考えると、ほとんどの人がビルドシステムに関わりたくないと思うのも不思議ではありません。彼らは、ある場所で(ある程度)機能するコードを別の場所にコピーペーストし、最善を尽くすことを願います。彼らは、システムについて理解することを積極的に避けようとします。なぜなら、その考え自体が嫌悪感を抱かせるからです。これを行うことは、一種の逆の雇用保証を提供することにもなります。ツールXを知らなければ、組織でその使用を担当することになる可能性は低くなります。代わりに、もっと楽しいことに取り組むことができます。

これは悪循環につながります。人々がツールを避け、それらを扱いたくないため、それらの改善に取り組む人はほとんどいません。その結果、無関心と停滞が生じます。

もっとうまくできるでしょうか?

その核心では、CおよびC++コードのビルドはそれほど難しい作業ではありません。実際、テキストエディターを作成する方がはるかに複雑で、より多くの労力がかかります。それでも、非常に高品質なエディターはたくさんありますが、品質と使いやすさに疑問符が付くビルドシステムはわずかしかありません。

そこで、自らの不便を解消するという伝統の中で、私は科学的な実験を行うことにしました。この実験の目的は、「優れた」ビルドシステムを構築するには何が必要かを調査することでした。この問題に適した構文はどのようなものか?このアプリケーションはどのような問題を解決する必要があるか?どのような解決策が最も適切か?

始めるにあたり、最新のクロスプラットフォームビルドシステムが提供する必要のある要件のリストを以下に示します。

1. 使いやすいこと

Pythonの優れた点の1つは、非常に読みやすいことです。特定のコードブロックが何をするのかを簡単に見ることができます。それは簡潔で、明確で、理解しやすいです。提案されたビルドシステムは、構文的にも意味的にも明確である必要があります。副作用、グローバル状態、および相互関係は、最小限に抑えるか、可能であれば完全に排除する必要があります。

2. デフォルトで正しい動作をすること

ほとんどのビルドは、コードに取り組む開発者によって行われます。したがって、デフォルトは、そのユースケースに合わせて調整する必要があります。例として、システムは最適化なしで、デバッグ情報付きでオブジェクトをビルドする必要があります。リンカーのトリック、シェルスクリプト、またはマジックな環境変数なしで、ビルドディレクトリから直接実行できるバイナリを作成する必要があります。

3. 確立されたベストプラクティスを適用すること

-Wallと同等のものなしにソースコードをコンパイルする理由はありません。そのため、デフォルトで有効にします。別の種類のベストプラクティスは、ソースディレクトリとビルドディレクトリの完全な分離です。すべてのビルド成果物は、ビルドディレクトリに保存する必要があります。いかなる状況においても、ソースディレクトリに不要なファイルを作成することは許可されません。

4. 一般的に使用されているプラットフォームのネイティブサポートを備えていること

多くのフリーソフトウェアプロジェクトは、WindowsやOSXなどの非フリープラットフォームで使用できます。システムは、これらのプラットフォームで選択されたツールのネイティブサポートを提供する必要があります。実際には、これはVisual StudioとXCodeのネイティブサポートを意味します。IDEに外部ビルダーバイナリを呼び出させることは、ネイティブサポートとはみなされません。

5. 廃止されたプラットフォームによる複雑さを追加しないこと

このビルドシステムの作業は、2012年のクリスマス休暇中に始まりました。これにより、2012/12/24という自然な締め切り線が設定されます。当時アクティブに使用されていなかったプラットフォーム、ツール、ライブラリは、明示的にサポートされていません。これには、IRIX、SunOS、OSF-1などのUnix、12/10より古いUbuntuバージョン、4.7より古いGCCバージョンなどが含まれます。これらの古いバージョンがたまたま機能する場合は、問題ありません。そうでない場合は、それらのバグを回避するために、システムに1行のコードも追加されません。

6. 高速であること

中規模のプロジェクトで構成手順を実行するのに5秒以上かからないようにする必要があります。1000個のソースファイルで完全に最新のツリーでコンパイルコマンドを実行するのに0.1秒以上かからないようにする必要があります。

7. 最新のソフトウェア開発機能を簡単に使用できるサポートを提供すること

例としては、プリコンパイル済みヘッダーがあります。現在、フリーソフトウェアのビルドシステムは、それに対するネイティブサポートを提供していません。その他の例としては、Valgrindやユニットテスト、テストカバレッジレポートなどの簡単な統合が考えられます。

8. デフォルト値のオーバーライドを許可すること

特定のコンパイラフラグのみを使用してファイルをコンパイルし、他のフラグを使用しない場合や、ファイルを奇妙な場所にインストールする必要がある場合があります。システムは、ユーザーが本当にそうしたい場合に、これを許可する必要があります。

ソリューションの概要

これらの要件を確認すると、実現可能な唯一のアプローチは、CMakeが採用したアプローチとほぼ同じであることが明らかになります。つまり、ビルドシステムを宣言するためのドメイン固有言語(DSL)を用意します。この宣言から、バックエンドビルドシステム用の構成が生成されます。これは、Makefile、Visual Studio、XCodeプロジェクト、またはその他の任意の形式にすることができます。

提案されたDSLと既存のDSLの違いは、新しいDSLが宣言型であるということです。また、既存のシステムよりも高レベルの抽象化で動作しようとします。例として、現在のビルドシステムで外部ライブラリを使用するということは、コンパイラフラグとリンカーフラグを手動で抽出して渡すことを意味します。提案されたシステムでは、ユーザーは特定のビルドターゲットが特定の外部依存関係を使用することを宣言するだけです。その後、ビルドシステムは、すべてのフラグと設定を適切な場所に渡す処理を行います。これは、ユーザーが、コマンドライン引数をある場所から別の場所に編成するのではなく、自分のコードに集中できることを意味します。

DSLは、システムをPythonライブラリとして提供するというSConsが採用したアプローチよりも手間がかかります。しかし、これにより、構文をより表現力豊かにし、特定のオブジェクトを真に不変にするなどして、特定の種類のバグを防ぐことができます。最終的な結果はまた同じです。ユーザーの作業が少なくなります。

Unixのバックエンドには、もう少し考慮が必要です。デフォルトの選択はMakeです。しかし、それは非常に遅いです。大規模なコードベースでは、何もする必要がないことを判断するためにMakeが数分かかることも珍しくありません。Makeの代わりに、非常に高速なNinjaを使用します。バックエンドコードはコアから抽象化されているため、比較的少ない労力で他のバックエンドを追加できます。

サンプルコード

設計の話は十分にして、コードに取り掛かりましょう。例を見る前に、これが決して最終的なコードではないことを強調しておきたいと思います。これは、現在(2013年2月)のシステムで機能する概念実証コードですが、いつでも変更される可能性があります。

簡単に始めましょう。単一の実行可能バイナリをコンパイルするコードを以下に示します。

project('compile one', 'c')
executable('program', 'prog.c')

これは、可能な限り単純なものです。まず、プロジェクト名と使用する言語を宣言します。次に、ビルドするバイナリとそのソースを指定します。残りはすべてビルドシステムが行います。適切なサフィックス(Windowsでは「.exe」など)を追加し、デフォルトのコンパイラフラグなどを設定します。

通常、プログラムには複数のソースファイルがあります。関数呼び出しにそれらをすべてリストすると、扱いにくくなる可能性があります。そのため、システムはキーワード引数をサポートしています。それらはこのようになります。

project('compile several', 'c')
sourcelist = ['main.c', 'file1.c', 'file2.c', 'file3.c']
executable('program', sources : sourcelist)

外部依存関係は簡単に使用できます。

project('external lib', 'c')
libdep = find_dep('extlibrary', required : true)
sourcelist = ['main.c', 'file1.c', 'file2.c', 'file3.c']
executable('program', sources : sourcelist, dep : libdep)

他のビルドシステムでは、外部依存関係からのコンパイルフラグとリンクフラグをターゲットに手動で追加する必要があります。このシステムでは、extlibraryが必須であり、生成されたプログラムがそれを使用することを宣言するだけです。ビルドシステムがすべての配管作業を行います。

以下は、少し複雑な定義です。それでも理解できるはずです。

project('build library', 'c')
foolib = shared_library('foobar', sources : 'foobar.c',\
install : true)
exe = executable('testfoobar', 'tester.c', link : foolib)
add_test('test library', exe)

まず、foobarという名前の共有ライブラリをビルドします。これはインストール可能としてマークされているため、meson installを実行すると、ライブラリディレクトリにインストールされます(システムはどれがどれかを認識しているため、ユーザーが気にする必要はありません)。次に、ライブラリにリンクされているテスト実行可能ファイルをビルドします。これはインストールされませんが、代わりにユニットテストのリストに追加され、コマンドmeson testで実行できます。

上記で、他のビルドシステムではサポートされていない機能としてプリコンパイル済みヘッダーについて言及しました。以下に、それらをどのように使用するかを示します。

project('pch demo', 'cxx')
executable('myapp', 'myapp.cpp', pch : 'pch/myapp.hh')

他のビルドシステムがこれほど簡単にPCHサポートを提供できない主な理由は、それらが特定のベストプラクティスを適用していないためです。インクルードパスの動作方法のため、ソース内ビルドとソース外ビルドの両方で常に機能するPCHサポートを提供することは不可能です。ビルドディレクトリとソースディレクトリを分離することを義務付けると、これや他の多くの問題がはるかに簡単になります。

コードを入手する

この実験のコードは、Mesonリポジトリにあります。(執筆時点では)これはビルドシステムではないことに注意してください。それは1つの提案にすぎません。まだ確実には機能しません。おそらく、プロジェクトのビルドシステムとして使用すべきではありません。

そうは言っても、この実験が最終的には本格的なビルドシステムに発展することを願っています。そのためには皆様の協力が必要です。コメントや特にパッチは大歓迎です。

検索結果は以下の通りです。