ベストプラクティスとヒント
プロジェクトのMesonビルド定義を作成する際には、考慮すべき点がいくつかあります。これは、プロジェクトがサブプロジェクトとして使用される場合に特に当てはまります。このページでは、定義を作成する際に考慮すべき点をいくつか示します。
外部検索パスにconfig.hを含めないでください
多くのプロジェクトでは、プロジェクトの内部設定に使用される`config.h`ヘッダーファイルを使用しています。これらのファイルはシステムヘッダーファイルにはインストールされないため、インクルードの競合はありません。ただし、サブプロジェクトではこの限りではありません。プロジェクトツリーには任意の数の設定ファイルが存在する可能性があるため、それらが競合しないようにする必要があります。
基本的な問題は、サブプロジェクトのユーザーが、その`config.h`ファイルを見ずにサブプロジェクトのヘッダーをインクルードできる必要があることです。最も適切な解決策は、`config.h`ファイルを`foobar-config.h`などの一意の名前に変更することです。これは、問題のサブプロジェクトのメンテナンス担当者でない限り、通常は実行可能ではありません。
実際的な解決策は、他のヘッダーファイルのないディレクトリにconfigヘッダーを配置し、それを他のすべての人から隠すことです。1つの方法は、`internal`という最上位のサブディレクトリを作成し、それを自分のソースのビルドに使用することです。例:
subdir('internal') # create config.h in this subdir
internal_inc = include_directories('internal')
shared_library('foo', 'foo.c', include_directories : internal_inc)
多くのプロジェクトでは、他のソースファイルのない最上位ディレクトリに`config.h`を保持しています。その場合、移動する必要はなく、代わりにこれを行うことができます。
internal_inc = include_directories('.') # At top level meson.build
ライブラリを静的と共有の両方でビルド可能にする
一部のプラットフォーム(例:iOS)では、メインアプリ内のすべてを静的にリンクする必要があります。他のケースでは、共有ライブラリが必要になる場合があります。Mesonのリリンク最適化により、開発中はより高速でもあります。しかし、すべてのビルドで両方のライブラリタイプをビルドすることは遅く、無駄です。
プロジェクトでは、`default_library`組み込みオプションを使用して共有と静的を切り替えることができる`library`メソッドを使用する必要があります。
mylib = library('foo', 'foo.c')
生成されたヘッダーを明示的に宣言する
MesonのNinjaバックエンドは、Makeやその他のシステムとは異なる動作をします。ディレクトリごとに処理するのではなく、ビルド定義全体を一度に見て、外部からはランダムな順序に見える個々のコンパイルジョブを実行します。
これは、はるかに効率的であるため、ビルドがより高速に完了するためです。欠点は、依存関係に注意する必要があることです。ここで最も一般的な問題は、たとえばコードジェネレーターを使用してコンパイル時に生成されるヘッダーです。これらのヘッダーがこれらのライブラリを使用するコードのビルドに必要な場合、コード生成ステップの前にコンパイルジョブが実行される可能性があります。修正方法は、依存関係を次のように明示的にすることです。
myheader = custom_target(...)
mylibrary = shared_library(...)
mydep = declare_dependency(link_with : mylibrary,
include_directories : include_directories(...),
sources : myheader)
そして、通常どおり依存関係を使用できます。
executable('dep_using_exe', 'main.c',
dependencies : mydep)
Mesonは、`main.c`をコンパイルする前にヘッダーファイルがビルドされていることを確認します。
declare_dependencyでコンパイル可能なソースファイルを公開しない
`declare_dependency`の`sources`引数の主な用途は、前のセクションで示したように、バックエンドの正しい依存関係グラフを作成することです。コンパイル可能なソース(`.c`、`.cpp`など)を直接公開するために使用すべきではなく、ヘッダー/設定ファイルのみに使用すべきであることに注意することが非常に重要です。次の例は、コンパイル可能なソースファイルを誤って公開した場合に何が起こるかを示しています。
ユニティビルドとMesonがネイティブにサポートする方法について読みました。依存関係のソースを公開して、依存関係を含むユニティビルドを行うことにします。サポートライブラリでは、次のようにします。
my_support_sources = files(...)
mysupportlib = shared_library(
...
sources : my_support_sources,
...)
mysupportlib_dep = declare_dependency(
...
link_with : mylibrary,
sources : my_support_sources,
...)
そして、メインプロジェクトでは、次のようにします。
mylibrary = shared_library(
...
dependencies : mysupportlib_dep,
...)
myexe = executable(
...
link_with : mylibrary,
dependencies : mysupportlib_dep,
...)
これは非常に危険です。ビルド時に、`mylibrary`はビルドされ、サポートソース`my_support_sources`を結果の共有ライブラリにリンクします。次に、`myexe`では、これらの同じサポートソースが再びコンパイルされ、`mylibrary`に既に存在することに加えて、結果の実行可能ファイルにリンクされます。これは、シンボルの定義が複数あるため、未定義の動作をもたらすC++のワンデフィニションルール(ODR)にすぐに反する可能性があります。Cには厳格なODRルールはありませんが、標準にはそのような動作が機能することを保証する言語はありません。ODRの違反は、セグメンテーション違反などの奇妙な特異な障害につながる可能性があります。圧倒的多数のケースでは、`declare_dependency`の`sources`引数を使用してライブラリソースを公開することは、したがって正しくありません。完全なクロスライブラリのパフォーマンスを得たい場合は、代わりに`mysupportlib`を静的ライブラリとしてビルドし、LTOを使用することを検討してください。
このルールには例外があります。ライブラリの使用方法に自然な制約がある場合は、ソースを公開できます。たとえば、GoogleTestのWrapDBモジュールは、GTestとGMockのソースを直接公開します。これは有効です。GTestとGMockは、ターミナルリンクターゲットでのみ使用されるためです。ターミナルターゲットは、依存関係リンクチェーンの最終ターゲットです(最後の例では`myexe`)。一方、`mylibrary`は中間リンクターゲットです。ただし、ほとんどのライブラリでは、他の人がどのようにライブラリを使用するかを一般的に制御できないため、このルールは適用されません。したがって、ソースを公開しないでください。
検索の結果は次のとおりです。