MesonでC++プロジェクトをクロスプラットフォームにビルドできるようにしたときのメモです。C++以外の事は分かりません・・・
- 基本
- コンパイラを検出する
- VC++プロジェクトの癖
- VC++プロジェクトにヘッダを含める
- あるフォルダ内のファイルの列挙、ファイル名にワイルドカードを使う、等
- 依存ライブラリをダウンロードしてもらう
- CI(Travis AppVeyar)
- 参考文献
基本
基本的なビルドスクリプトは以下のようになります。
# 必ずproject()から始める project('test_project', 'cpp', default_options : ['warning_level=3', 'werror=true', 'cpp_std=c++17'], meson_version : '>=0.50.0') # インクルードディレクトリ指定 include_dir = include_directories('include', 'oher/include') # 実行可能ファイルを出力 executable('test_project', 'test.cpp', include_directories : include_dir)
公式のリファレンスとか
コンパイラを検出する
meson.get_compiler('cpp')
でコンパイラオブジェクト?を取得して、そこからget_id()
でコンパイラ文字列を取得します。
あとはif
で分岐するだけです。
if cppcompiler == 'msvc' # msvc用の処理 elif cppcompiler == 'gcc' # gcc用の処理 elif cppcompiler == 'clang' # clang用の処理 endif
ちなみに、コンパイルオプションを主要3コンパイラで分けたいだけならば、get_argument_syntax()
を使うと便利です。これによって得られる文字列は、オプションの互換性があるコンパイラで同一になります。
project('test_project', 'cpp', default_options : ['warning_level=3', 'werror=true', 'cpp_std=c++17'], meson_version : '>=0.50.0') cppcompiler = meson.get_compiler('cpp').get_argument_syntax() if cppcompiler == 'msvc' # MSVC,clang-cl,icc(windows)用 options = ['/std:c++latest'] elif cppcompiler == 'gcc' # gcc,clang,icc(linux)用 options = ['-std=c++2a'] else # その他 options = [] endif include_dir = include_directories('include', 'oher/include') executable('test_project', 'test.cpp', include_directories : include_dir, cpp_args : options)
例えばこうしておくと、それぞれのコンパイラで言語バージョンの指定ができます。
(ただし、デフォルトオプションとして指定している言語バージョンもそのままになってしまうので、MSVC等では警告が出ます・・・)
以下のページにこれらの関数で取得できるコンパイラ文字列の一覧があります。
- Reference tables - The Meson Build System
Argument syntax
列はget_argument_syntax()
によって得られる文字列
VC++プロジェクトの癖
仕方ないことなのかもしれませんが、Mesonの出力するVC++プロジェクトは少し変わっています・・・
- VS同梱の開発者コマンドプロンプトから
meson build --backend vs
を実行しないといけない- 普通のコマンドプロンプトやpowershellではダメ
- 64bitでビルドしたければx64 Native Tools Command Promptを使う必要がある
- 出力されたVC++メインのプロジェクトのプロパティはほぼ空(デフォルト)
- プロジェクトプロパティの変更は、ビルド時に
meson.build
が変更されていてプロジェクト再出力が自動で行われた場合にリセットされる- 基本的にはこれ便利なんですけどもね・・・
VC++プロジェクトにヘッダを含める
出力されるVC++プロジェクトには指定したソースファイルは含まれていますが、インクルードディレクトリ内のヘッダは含まれていません。
例えばそれらのファイルを編集したくてVS上で開いたとしても、プロジェクト外のファイルに対してはインテリセンスがうまく働きません。
そのため、プロジェクトにそれらのヘッダを含めたいことがあるでしょう・・・
その場合は、ソースファイルと同じようにヘッダファイルを指定してやれば出力プロジェクトに含めることができます。
executable()
のextra_files
にプロジェクトに含めたいファイルを指定してやると含めておくことができます。
project('test_project', 'cpp', default_options : ['warning_level=3', 'werror=true', 'cpp_std=c++17'], meson_version : '>=0.50.0') cppcompiler = meson.get_compiler('cpp').get_argument_syntax() files = ['include/header1.hpp', 'include/header2.hpp'] include_dir = include_directories('include', 'oher/include') executable('test_project', 'test.cpp', extra_files : files, include_directories : include_dir)
あるフォルダ内のファイルの列挙、ファイル名にワイルドカードを使う、等
できません。
ビルド高速化のために、この様な曖昧な書き方ができないようになっているようです。
残念ながら、プロジェクトに含めるファイルは1つづつ明示的に指定する必要があります。
依存ライブラリをダウンロードしてもらう
依存ライブラリの指定はsubproject()
を使えば出来ます。これはインストール済みCMake(もしくはパッケージマネージャ)を検出して、そこから依存ライブラリ情報を取得してダウンロードして・・・と自動でやってくれる様子です。
でもWindowsだとそんなの入ってないし、githubから引っ張ってきたリポジトリとかでもよろしくやってほしいものです。
そのままだとこれは出来ない様子ですが、ラップファイルを用意してやることでやってもらえます。
meson.build
があるフォルダにsubprojects
というフォルダを作り、その中にライブラリ名.wrap
というファイルを用意しておきます。
例えば、doctestというライブラリを使いたいとしますと。
subprojects/doctest.wrap
は以下のように書きます。
[wrap-git] directory=doctest url=https://github.com/onqtam/doctest.git revision=2.3.4 clone-recursive=true
意味はなんとなくわかると思います。directory=
の所を変えるとダウンロードされるディレクトリ名が変わるようです。revision=
はダウンロードしてくるものの指定です。HEAD
とかコミットハッシュが使えるようです。
そして、meson.build
を以下のようにします。
project('test_project', 'cpp', default_options : ['warning_level=3', 'werror=true', 'cpp_std=c++17'], meson_version : '>=0.50.0') #サブプロジェクトの指定 doctest_proj = subproject('doctest') #依存オブジェクトの取得(名前が決まっている) doctest_dep = doctest_proj.get_variable('doctest_dep') files = ['test.cpp', 'include/header1.hpp', 'include/header2.hpp'] include_dir = include_directories('include', 'oher/include', 'subprojects/doctest') executable('test_project', files, include_directories : include_dir, cpp_args : options, dependencies : doctest_dep)
subproject('プロジェクト名')
で依存ライブラリを指定し(多分ここでダウンロード等がなされる)、その戻り値からget_variable('ライブラリ名_dep')
で依存オブジェクト?を取得します。
この依存オブジェクトは、対象ライブラリの持つmeson.build
に書かれている名前を指定しなければなりません(慣例的にライブラリ名_dep
となっているようです)。
最後に、executable()
に依存オブジェクトを指定してあげます。もし静的ライブラリ等の出力がある場合はここで自動的に取り込まれるようです(対象ライブラリの持つmeson.build
が適切に書かれていれば)。
この方法、git submoduleで対象のライブラリを管理していても、なんだかよろしくやってくれます。
ちなみにこれらの時、ダウンロードしてきたプロジェクトのトップにmeson.build
が無いとたぶん上手くいきません・・・。
ただ、ヘッダーオンリーライブラリならインクルードパスの指定だけしてやればいい気がします(get_variable()
してexecutable()
で依存関係指定をしないで、subproject()
だけしておく)
CI(Travis AppVeyar)
これはまだ試していないのでどうなるのかわかりませんが、公式サイトにTravisとAppVeyarに対するymlのサンプルがあります。この通りにやれば出来そうです。