2018/09/17

Mesonビルドシステムを使う

自由なソフトウェアのビルドにはGNUのautoconfautomakeなどを用いたビルドシステム(configureというシェルスクリプトを実行するもの)や、OSごとのビルド環境に対応したクロスプラットフォームなCMakeなどが広く用いられているが、GNOME系のソフトウェアを中心としてMesonというビルドシステムの利用が広がっている。

開発自体は2012年末から個人のプロジェクトとして始まったものだが、本記事公開時点では、既に多数の貢献者に支えられる規模となり、普及についても、Mesa, systemd, X.OrgのサーバなどGNU/Linuxのデスクトップ用途で欠かせないプロジェクトでのビルドに使用できるようになっており、Mesonのみにしか対応しないプロジェクトも出てきている。

Mesonは高レベルなビルドシステムで、前述した他のビルドシステムも含めて “メタビルドシステム” と分類されることもある。

  1. 特徴
    1. ビルドのみを行うユーザと開発者の両方からみた特徴
    2. ソフトウェアの開発者からみた特徴
  2. ビルドの流れ
    1. ビルド作業の例
    2. 設定の確認と再設定
  3. ビルド時の挙動を制御する
    1. ビルドの種類の指定
    2. コンパイラやオプションなどを変更する環境変数
    3. プロジェクトごとのビルド設定をオプションで指定する
    4. インストール先などの指定
  4. 開発者向けの機能
    1. gettextによる国際化に対応したプロジェクトにおける処理
    2. Ninja使用時の特殊なターゲット指定
      1. ビルド段階で生成されたファイルを削除
      2. gettextによる国際化への対応を統合したプロジェクトで.potファイルや.poファイルを更新
      3. 配布用のソース書庫ファイルを作成

特徴

ビルドのみを行うユーザと開発者の両方からみた特徴

  • CMakeと同様、OSごとのビルド環境に合わせた挙動をするため、クロスプラットフォームなソフトウェアの開発にも適する
  • autoconf/automakeなどのツール群(“autotools” とも呼ばれる)によるビルドシステムとは異なり(またCMakeと同様)、シェルを呼び出すことはなく、チェックやビルド設定の処理は高速で短時間で終了する
    • ソースツリーが大規模なプロジェクトではconfigureスクリプト内の色々な処理、特に多数のMakefileMakefile.inからそれぞれ生成するあたりの段階でかなり時間を食うが、そのような長い待ち時間はない
    • プロジェクトによっては開発版のソースを取得した場合などにautogen.shなどの名前のシェルスクリプトを通してautoconfなどを実行する必要がある場合があり、時間がかかる上に古いプロジェクトだとツールのバージョンの違いでエラーが出たりすることもあるが、そのようなことはない[1]
  • GNU/Linuxではビルド時にmakeより処理が速い低レベルビルドシステムNinjaがバックエンドとして用いられ、複数のCPUコアがある場合に既定ではその数に応じて同時に動かすプロセスの数が調整される
    • Ninjaは、ソースツリーが大規模なプロジェクトにおいてあるファイルが変更された後に必要な部分のみをビルドしようとするのに時間がかかるのを防ぐ設計なので、そのような意味での恩恵もある
    • ただしNinjaをバックエンドとして用いる機能はCMakeも搭載しており、-G Ninjaオプションを付ければCMakeを用いたプロジェクトでもビルドに使えるため、後述の “Unity build” 機能を使用しなければCMakeと比べてビルド時間が大幅に短くなるとは考えにくい
    • ビルド時に使用されるバックエンドとしてはmacOSやWindows向けのビルドツールにも対応しているが、各OS向けにビルドされたNinjaを用いることが推奨される
  • Python 3で書かれており、pip3でインストールできる
    • Python標準付属のモジュールのみで書かれているため、Python用のパッケージの依存関係が問題になることはなく、インストール自体もPythonが入っていればすぐに終わる
    • PyPyのPython 3互換バージョンでも動作可
    • Windowsでは環境変数PATHにPythonとそのスクリプト用の2つのディレクトリを追加してpip3mesonを指定してインストールするが、公式配布の.msiパッケージ(Ninjaも付属)をインストールしてPATHにインストール先を指定してmeson.exeを実行する形の使い方もできる
  • 複数のソースファイル群から成るターゲットのビルドにおいて、個別のファイルをコンパイルする代わりに一連のソースファイル群をまとめた中間ソースファイルを作ってそれをコンパイルすることで、ビルド時間の短縮やコンパイラのソースファイル単位の最適化の効果を出しやすくする “Unity build” と呼ばれる機能があり、標準では無効(--unity=onオプションで有効になる)
    • 後からソースを変更したときの追加コンパイルされる量が増えるため、開発者がソースを編集する段階で使用するのには向かない
    • ビルド時の必要メモリ量が増加するため、システムのメモリ量とプログラムの規模によってはメモリ使用量が多くなり過ぎて問題になる可能性がある
    • 同じ名前の変数や関数などが複数ファイルにそれぞれ存在した場合にそれらの衝突(再定義)によるビルドエラーなどの問題が発生する可能性がある
    • Unity buildが有効で正常に動作することが十分に確認されているプロジェクトでは問題はなく、使用が推奨される場合もある
    • CMakeでも手動でUnity buildの実装を行うこと自体は可能で、Web上にサンプルもあるが、書くのに手間がかかる上に記述量も多くなる
  • ビルド時に機能を有効もしくは無効にするなどの設定の定義は専用のファイルに書かれており、プログラムの利用者はそのファイルを参照するだけで設定可能項目について把握でき、開発者にとっても扱いやすい
  • クロスコンパイル用の定義ファイルを用意し、それをmesonコマンドに対して--cross-fileオプションで指定することで、クロスコンパイルの実行が簡単に行える(クロスコンパイラは別途用意する)
  • ccacheが利用可能な場合に自動的に使用する機能がある

PyPyについては

を参照。

ソフトウェアの開発者からみた特徴

Mesonを用いているプロジェクトはソースツリーにmeson.buildというファイルがあり、サブディレクトリの処理にも対応しているため、開発者はこの名前のファイルを複数記述することになる。以下はそのファイルの内容の記述についての特徴。

  • 依存パッケージなどのチェック処理の部分とビルドやインストールに関係する記述の部分のいずれも読み書きがしやすく、シンプルに書けて読む人にも分かりやすい
  • 特定のGUIツールキットやOSに固有の、プログラムのビルドに関係した操作を楽に扱うモジュール群が存在し、それらに関係する記述をシンプルにできる
  • 書き方を全く習得していない状態では、公式のドキュメントや既存のプロジェクトのファイルなどを参考にして調べる必要がある
    • ただし、autotoolsでの記述を新しく習得するのに比べればかなり楽

ビルドの流れ

  1. ソースツリーの最上位ディレクトリでmesonコマンドを実行
  2. 必要に応じて再設定を行う
  3. 指定済みのビルドディレクトリの中でビルド用のコマンドを実行してビルドとインストールを行う
  4. ビルド用ディレクトリが必要なくなったらこれを消す

はじめに、このビルドシステム固有のコマンドmesonを実行する。ここでは各種チェックやビルド時の設定などの処理が行われ、エラーがなければシェルに戻った後で続けてビルド用のコマンド(既定ではninja)を実行する。このような流れはautotoolsによるビルドシステムやCMakeと近い。

Mesonビルド用の一時ディレクトリを必須とし

  • ソースツリーの最上位ディレクトリ内ビルド用ディレクトリの場所を引数に指定
  • 空のビルド用ディレクトリ内ソースツリーの最上位ディレクトリの場所を引数に指定
  • それら両方のディレクトリの場所を示す2つの引数を(先にソースツリーの最上位ディレクトリが来る順番で)指定

のいずれかの形でmesonコマンドを実行する。ビルド用のディレクトリとして引数に指定した場所がまだ存在しなければmesonが自動的にその場所にディレクトリを作るため、事前に手動で作成する必要はない。

ビルド作業の例

GNU/Linux上でビルドを行う典型的な流れとしては下のようになり、下の例ではソースツリーの最上位ディレクトリから新しく作成するビルド用ディレクトリの場所を指定する形をとっている。

(ビルドの準備)
$ meson --prefix=/opt/program_name _build

(ビルド)
$ ninja -C _build

(管理者権限でインストール)
$ sudo ninja -C _build install

meson実行時の作業ディレクトリと引数の指定には複数の方法があるということは既に書いているが、下の例では空のビルド用ディレクトリの中からソースツリーの最上位ディレクトリの場所を指定する形をとっている(手動でのディレクトリ作成があるため、おすすめはしない)。

(ビルド用ディレクトリを作成)
$ mkdir _build && cd _build

(ビルドの準備)
[_build]$ meson --prefix=/opt/program_name ..

(ビルド)
[_build]$ ninja

(管理者権限でインストール)
[_build]$ sudo ninja install

設定の確認と再設定

既存のビルド用ディレクトリを引数に指定してサブコマンドconfigureを実行することで、現在の設定の状態と設定可能な項目の一覧を確認することができる。更に-Dname=value(名前と値のペア)形式のオプションを付け加えてこれを実行することでそのビルド用ディレクトリにおける設定を後から変更することもできる。もちろん、このようなオプションは最初にmesonコマンドを実行したときに設定することもできる。

一覧では

  • オプションの設定名
  • 現在の値
  • 設定可能な値(特定の値のみ受け付ける場合)
  • 説明文

のセットが種類ごとに並んで表示される。将来のバージョンでこれが見やすくなる可能性はあるが、1行がかなり長くなるため、端末の横が80桁の環境ではとても見づらい。その場合は必要な応じて端末ソフトを一時的に最大化して出力を確認してから元に戻すなどする。

(現在の設定を確認)
$ meson configure _build | less

(コンパイラの警告レベルを指定するwarning_levelを2に変更)
$ meson configure -Dwarning_level=2 _build

ビルド時の挙動を制御する

ビルドの種類の指定

--buildtypeオプションを指定することで、デバッグ用やリリース用のオプション指定が自動的に設定される。よく使われるのが以下。プロジェクトによっては既定値としてdebugoptimizedが設定されている。

  • buildtype=debugoptimized: 広く使われる最適化レベルとデバッグ情報生成の指定
  • buildtype=release: 高めの最適化レベル指定
  • buildtype=plain: 最適化レベルやデバッグ情報生成に関するコンパイラオプションを付けない

コンパイラやオプションなどを変更する環境変数

コンパイラやそのオプションなどをビルド時に変更したい場合にはautotoolsによるビルドシステムにおけるconfigureスクリプトの実行時と同様にmesonコマンドの実行時に特定の環境変数を指定する。

buildtype=plainオプションとこれらを同時に指定するとオプション指定のカスタマイズに役立つ。

(コンパイラ関係の環境変数を指定する例)
$ CC="gcc" CFLAGS="-O2 -march=native" LDFLAGS="-Wl,-O1" meson --buildtype=plain _build

下の一覧は一部で、対応する全ての環境変数を示すものではない。CCCXXccacheの使用を回避するのにも使える。

環境変数説明
CCCコンパイラのコマンド名もしくは絶対パス
CXXC++コンパイラのコマンド名もしくは絶対パス
CFLAGSCコンパイラに付けるオプション
CXXFLAGSC++コンパイラに付けるオプション
VALAFLAGSValaコンパイラに付けるオプション
CPPFLAGSプリプロセッサのオプション
LDFLAGSリンカに付けるオプション

ディストリのパッケージをビルドするときなどに使われるDESTDIRninja installの実行時に環境変数として指定することでMeson内のコードがbuild.ninja経由で呼び出されてこれが処理され、この値のディレクトリをルートディレクトリとしてその下の階層にファイルが配置される。

(DESTDIRを指定してインストール処理を行う例)
$ DESTDIR=~/temp/dest ninja install

プロジェクトごとのビルド設定をオプションで指定する

プロジェクトによっては-Dname=value(名前と値のペア)形式のオプション指定でプロジェクトに固有な機能の有効/無効などの設定をビルド前に行うことができる。

指定する名前や指定可能値, 既定値, 説明はmeson_options.txtというファイルに記述される。Meson自体もこのファイルを参照して必要な処理を行っている。

Mesaはバージョン18.1系時点でMesonとautotoolsの両方に対応しており、

でMesa 18.1.x系のソースツリーでIntel用のVulkanドライバのみのビルドを行う作業があるが、これをMesonで行うと下のようになる(将来のバージョンでは指定の形式が変わる可能性があるため、オプション指定の形を示す例として載せる)。

[mesa-18.1.x]$ meson -Dgbm=false -Dshared-glapi=false -Ddri3=true -Degl=false -Dglx=disabled -Dopengl=false -Dgles1=false -Dgles2=false -Dgallium-drivers= -Ddri-drivers= -Dvulkan-drivers=intel _build
[mesa-18.1.x]$ ninja -C _build

インストール先などの指定

autotoolsに基づいたビルドシステムと同様、インストール先(prefix)の標準は/usr/local/(WindowsではCドライブ直下)以下となっており、--prefixオプションを付けることでその場所を変更することができる。

オプション説明
--prefixインストール先(基本的にファイルはこのディレクトリ以下に配置される)
--bindir実行ファイルの配置場所(通常は--prefixのディレクトリの下のbin)
--libdirそのソフトウェアが提供するライブラリの配置場所

--libdirについては、基本的には--prefixのディレクトリの下のlib/などとなるが、ディストリによってバリエーションがあり、Debian系ではlib/[アーキテクチャ名]-linux-gnu/のような形となっており、ディストリに合った既定のディレクトリを検出できる。

同様の他のオプションはmeson --helpで確認できる。

開発者向けの機能

gettextによる国際化に対応したプロジェクトにおける処理

gettextによる国際化に対応したプロジェクトでは、Mesonはintltoolではなく新しめのバージョン(0.19.7以上)のgettextのツール群の機能を用いてpo/POTFILESに並べたファイルの翻訳可能文字列を取り出して.potファイル[2]を更新し

  • *.appdata.xml.in
  • *.desktop.in

などのテンプレートの形の翻訳可能ファイルはi18n.merge_file()という関数を呼び出す記述がmeson.buildにあれば、ビルド時にmsgfmtの機能によってテンプレートファイル(*.in)と.poファイル群から翻訳の含まれた出力ファイルが生成される。

Ninja使用時の特殊なターゲット指定

ビルド段階で生成されたファイルを削除

プログラムのソースをコンパイルした結果生成されるオブジェクトファイルなどの削除で、autotoolsによるビルドシステムにおけるmake cleanに相当するもの。Mesonはビルド用ディレクトリが必須なこともあり、(そのディレクトリ以下を消すことで生成されるファイルが全て消せるため)この操作の出番は少ないかもしれない。

meson.build内にconfigure_file()の記述がある場合はmesonコマンドの実行時にファイルが生成され、以下の操作を行ってもビルドディレクトリ内に残る。config.hが生成されるプロジェクトにおける同ファイルはその典型的な例。

(ビルド段階で生成されたファイルを削除)
$ ninja -C _build clean

gettextによる国際化への対応を統合したプロジェクトで.potファイルや.poファイルを更新

gettextによる国際化への対応を統合(i18n.gettext()のおかげで必要な記述自体は非常に少ない)したプロジェクトにおいて、翻訳可能文字列を取り出して.potファイルを更新するには[プロジェクト名]-potを、その更新を各.poファイルに適用するまでを行うには[プロジェクト名]-update-poのターゲットを指定する。

プロジェクト名部分に指定するのは実際のプロジェクト名ではなく、ソースツリー最上位ディレクトリのmeson.buildproject()の最初の引数に指定されている内部的な名前の文字列。

下は例だが、projnameの部分を実際のプロジェクトのものに置き換える。

(翻訳可能文字列を取り出して.potファイルを更新)
$ ninja -C _build projname-pot

(上に加えてmsgmergeによる各.poファイルの更新も行う)
$ ninja -C _build projname-update-po

配布用のソース書庫ファイルを作成

GitMercurialのリポジトリのソースツリーから作成したビルド用ディレクトリの中でdistというターゲットを指定すると、バージョン管理システム関係のファイルやディレクトリを除いた配布用のソース書庫ファイル(.tar.xz形式)が自動的に[ビルドディレクトリ]/meson-dist/以下に作成される。書庫に所有者のユーザ名やグループ名が入るのが気になる場合はfakerootと組み合わせる。

このファイルはPythonの機能のみで作成されるため、書庫の作成のための外部コマンドは不要。

(ソース書庫ファイルを作成)
$ fakeroot ninja -C _build dist
使用したバージョン:
  • Meson 0.47.2
[1]: ただしMesonのバージョンが古いと機能の実装状況の関係で動かない場合はある
[2]: プログラム用の翻訳ファイルのソースである.poファイルに対するテンプレートとなるファイル