0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MesonでFortran 2018をビルドする ― f2py・Ninja・OpenMPまでつなぐ開発環境

0
Posted at

MesonでFortran 2018をビルドする ― f2py・Ninja・OpenMPまでつなぐ開発環境

はじめに

Fortranのプログラムが小さいうちは、gfortranを直接実行するだけで十分です。ただ、ソースが増えてくると事情が変わります。計算モジュールの依存関係を考えたり、DebugとReleaseでオプションを切り替えたり、テストをまとめて実行したりと、コンパイル以外の作業も増えてきます。

こうしたビルド作業を整理するのに便利なのが Meson です。

前回は、NumPyに含まれるf2pyを使って、PythonからFortran 2018の数値計算を呼び出す方法を紹介しました。今回はその続きとして、Mesonそのものの使い方から、Fortranのビルド、f2pyとの連携、OpenMPの設定まで整理します。

例はWindows 11 + WSL2 Ubuntu 24.04を想定しています。Dockerで同じ構成を動かす方法も最後に紹介します。

この記事の確認時点:2026年10月15日
NumPyは2.5.4を例にしています。NumPy 2.5以降では、f2pyの旧distutilsバックエンドが削除され、Mesonが使われます。


1. Mesonとは

Mesonは、C、C++、Fortranなどのソースコードをビルドするためのツールです。ソースファイル、コンパイラ、依存ライブラリ、テストなどの設定をmeson.buildにまとめて記述します。

Meson自身がFortranのコンパイラになるわけではありません。Mesonがビルド手順を組み立て、通常はNinjaがその手順を実行し、実際のコンパイルをgfortranなどが担当します。

ツール 担当する処理
Meson ビルド設定、ソース・依存関係の管理
Ninja ビルド手順の実行、必要なファイルの再コンパイル
gfortran Fortranのコンパイルとリンク
f2py FortranをPythonから呼び出すためのラッパー生成
meson-python PythonパッケージやWheelのビルド

fortran_build_pipeline.png

ここで覚えておきたいのは、Mesonとf2pyは別の役割を持つという点です。f2pyがインターフェースを作り、Mesonがビルド全体を管理します。


2. WSL2 Ubuntu 24.04にインストールする

まずはWSL2のUbuntuで必要なパッケージを準備します。

sudo apt update

sudo apt install -y \
    python3 \
    python3-venv \
    python3-dev \
    build-essential \
    gfortran \
    ninja-build \
    pkg-config

開発用ディレクトリを作成し、Python仮想環境を有効にします。

mkdir -p ~/meson_fortran
cd ~/meson_fortran

python3 -m venv .venv
source .venv/bin/activate

python -m pip install --upgrade pip
python -m pip install meson numpy==2.5.4 pytest

確認します。

meson --version
ninja --version
gfortran --version
python -m numpy.f2py -v

UbuntuのaptからMesonをインストールする方法もありますが、上記ではプロジェクトのPython仮想環境にMesonを入れています。複数の開発環境でバージョンを揃えたい場合はこちらが扱いやすいです。

以降の操作は、特に断りのない限り.venvを有効にした状態で実行します。


3. 最初のmeson.buildを書く

まずはf2pyを使わず、FortranのプログラムをMesonでコンパイルします。

3.1 ディレクトリ構成

meson_fortran/
├── .venv/
├── meson.build
├── src/
│   └── add_vectors.f90
├── tests/
│   └── test_add.f90
└── build/              ← Mesonが生成する

3.2 Fortranの計算プログラム

2つの配列を足すだけの小さなサブルーチンです。

src/add_vectors.f90 を表示
subroutine add_vectors(n, a, b, c)
    implicit none

    ! 配列の要素数
    integer, intent(in) :: n

    ! 入力配列と出力配列
    double precision, intent(in) :: a(n), b(n)
    double precision, intent(out) :: c(n)

    integer :: i

    ! 要素ごとの加算処理
    do concurrent (i = 1:n)
        c(i) = a(i) + b(i)
    end do

end subroutine add_vectors

この例では、f2pyに公開しやすいよう、サブルーチンの入出力にdouble precisionを使っています。計算コアを大きくする場合にはreal(real64)などを使う設計もできますが、f2pyのKINDの解釈には別途注意が必要です。

3.3 テスト用のFortranプログラム

期待する配列と計算結果を比較するテストを用意します。

tests/test_add.f90 を表示
program test_add
    use, intrinsic :: iso_fortran_env, only: real64
    implicit none

    integer, parameter :: n = 3
    real(real64) :: a(n), b(n), c(n)

    ! 検証しやすい値を用意する
    a = [1.0_real64, 2.0_real64, 3.0_real64]
    b = [10.0_real64, 20.0_real64, 30.0_real64]

    call add_vectors(n, a, b, c)

    ! 計算結果を許容誤差付きで比較する
    if (any(abs(c - [ &
        11.0_real64, 22.0_real64, 33.0_real64 &
        ]) > 1.0e-12_real64)) then

        error stop 'add_vectors: NG'
    end if

    print *, 'PASS: add_vectors'
end program test_add

3.4 ビルド設定

プロジェクトのルートにmeson.buildを作成します。

project(
  'fortran_demo',
  'fortran',
  version: '0.1.0',
  default_options: [
    'fortran_std=f2018',
    'warning_level=2'
  ]
)

# Fortranの計算処理とテスト処理をリンクする
check_add = executable(
  'check_add',
  [
    'src/add_vectors.f90',
    'tests/test_add.f90'
  ]
)

# Mesonから実行できるテストとして登録する
test('vector-addition', check_add)

project()では使用する言語と基本設定を指定します。executable()は実行ファイルの作成、test()はテストの登録です。Fortran 2018への準拠を確認するため、fortran_std=f2018を指定しています。


4. Mesonの基本操作

最初にビルドディレクトリを作成します。

meson setup build

次にコンパイルします。

meson compile -C build

成功すると、buildディレクトリにcheck_addが生成されます。

./build/check_add

期待する出力は次のとおりです。

 PASS: add_vectors

登録したテストをまとめて実行する場合は、次のコマンドを使います。

meson test -C build --print-errorlogs

meson.buildやソースコードを変更した後も、基本的にはmeson compile -C buildを実行すれば必要な部分が再ビルドされます。

よく使うコマンドをまとめておきます。

コマンド 内容
meson setup build 初回のビルド設定
meson compile -C build コンパイル
meson test -C build テスト実行
meson configure build 現在の設定を確認
meson setup --reconfigure build 設定の再構成
meson setup --wipe build ビルドディレクトリの設定を初期化して再構成
meson install -C build インストール対象を配置

meson installを利用するには、インストールするターゲットや配置先をmeson.buildで定義しておく必要があります。今回の単純なテストプログラムではインストールを設定していません。


5. DebugとReleaseを切り替える

数値計算プログラムでは、デバッグ用と実行速度を重視するビルドを分けておくと便利です。Mesonはソースとビルドディレクトリを分離しているため、同じソースに対して複数のビルド設定を持てます。

ビルドタイプ 最適化の目安 主な用途
debug 最適化なし 不具合の調査
debugoptimized -O2相当 デバッグ情報を残した性能確認
release -O3相当 本番計算

Debugビルド

meson setup build-debug \
    --buildtype=debug \
    -Dfortran_args=-fcheck=all

meson compile -C build-debug
meson test -C build-debug --print-errorlogs

-fcheck=allはgfortranの実行時チェックを有効にします。配列の範囲外参照などを調査するときに役立ちます。

Releaseビルド

meson setup build-release --buildtype=release
meson compile -C build-release
meson test -C build-release --print-errorlogs

CPU固有の命令を使う場合は、以下のように指定できます。

meson setup build-native \
    --buildtype=release \
    -Dfortran_args=-march=native

meson compile -C build-native

ただし、-march=nativeを付けたバイナリは別のCPUで動作しない場合があります。配布用ビルドでは移植性を優先します。また、水収支などの検証が必要な計算で-ffast-mathを無条件に有効にするのは避けた方がよいです。


6. f2pyからMesonを使う

ここからPythonとの連携に進みます。

f2pyを使うだけなら、meson.buildを自分で書く必要はありません。NumPy 2.5系では次のコマンドだけでMesonを使って拡張モジュールをビルドできます。

python -m numpy.f2py \
    -c \
    -m fastcalc \
    src/add_vectors.f90

正常に完了すると、Linuxではfastcalc.cpython-312-...soのような共有ライブラリが生成されます。ファイル名は使用するPythonのバージョンや実行環境によって変わります。

生成したモジュールがあるディレクトリで、Pythonから呼び出します。

import numpy as np
import fastcalc

a = np.array([1.0, 2.0, 3.0], dtype=np.float64)
b = np.array([10.0, 20.0, 30.0], dtype=np.float64)

result = fastcalc.add_vectors(a, b)
print(result)

期待する出力は次のとおりです。

[11. 22. 33.]

f2pyが内部で生成するMesonの設定を確認したければ、--build-dirを指定します。

python -m numpy.f2py \
    -c \
    -m fastcalc \
    src/add_vectors.f90 \
    --build-dir f2py_generated

この方法は、まずf2pyを動かしてみたい場合に適しています。小規模な計算モジュールなら、これだけでも十分です。


7. meson.buildでf2pyのラッパー生成まで管理する

Fortranソースが増えたり、Pythonへの公開APIを継続して保守したりする場合は、ラッパー生成もMesonに組み込む方法があります。

以下は前節とは別の新しいプロジェクトとして作成します。

f2py_meson/
├── .venv/
├── meson.build
├── src/
│   └── add_vectors.f90
└── tests/
    └── test_fastcalc.py

src/add_vectors.f90には前のサブルーチンをコピーします。新しいプロジェクトでPython仮想環境を使用する場合も、NumPyとMesonのインストールが必要です。

7.1 meson.buildを作る

この方法では、Mesonのcustom_target()でf2pyを起動し、py.extension_module()で共有ライブラリを生成します。

meson.build(f2py連携用)を表示
project(
  'f2py_meson',
  ['c', 'fortran'],
  version: '0.1.0',
  meson_version: '>=1.1.0',
  default_options: [
    'buildtype=release',
    'fortran_std=f2018'
  ]
)

# Pythonの拡張モジュールをビルドするための機能
py = import('python').find_installation(pure: false)

# NumPyのCヘッダーを取得する
np_dep = dependency('numpy')

# f2py付属のfortranobject.c / fortranobject.hを探す
f2py_inc = run_command(
  py,
  [
    '-c',
    'import numpy.f2py; print(numpy.f2py.get_include())'
  ],
  check: true
).stdout().strip()

f2py_dep = declare_dependency(
  include_directories: f2py_inc
)

# Fortranソースからf2pyのCラッパーを生成する
wrapper = custom_target(
  'fastcalc_wrapper',
  input: 'src/add_vectors.f90',
  output: [
    'fastcalcmodule.c',
    'fastcalc-f2pywrappers.f'
  ],
  command: [
    py,
    '-m', 'numpy.f2py',
    '@INPUT@',
    '-m', 'fastcalc',
    '--lower'
  ]
)

# Fortran本体とラッパーを一緒にビルドする
py.extension_module(
  'fastcalc',
  [
    'src/add_vectors.f90',
    wrapper,
    f2py_inc / 'fortranobject.c'
  ],
  dependencies: [
    np_dep,
    f2py_dep
  ],
  install: false
)

custom_target()は、ソースから別のソースを生成するときに便利な機能です。ここではFortranファイルをf2pyに渡して、Cラッパーなどを作っています。

今回の例はモジュール外に定義した単純なサブルーチンを対象にしています。Fortranのmoduleや複雑なシグネチャを扱う場合には、追加の.f90ラッパーが生成される場合があるため、outputの定義も合わせて確認してください。

7.2 ビルドする

meson setup build
meson compile -C build

ビルドに成功すると、buildディレクトリ以下にfastcalcの拡張モジュールが生成されます。

7.3 Pythonから動作確認する

テストプログラムを作成します。

tests/test_fastcalc.py を表示
"""f2pyで公開した配列加算処理の動作確認。"""

import numpy as np
import fastcalc


def test_add_vectors() -> None:
    """Fortranの加算結果が期待値と一致することを確認する。"""
    a = np.array([1.0, 2.0, 3.0], dtype=np.float64)
    b = np.array([10.0, 20.0, 30.0], dtype=np.float64)

    # 配列長nはf2pyが入力配列から求める
    actual = fastcalc.add_vectors(a, b)
    expected = np.array([11.0, 22.0, 33.0])

    np.testing.assert_allclose(actual, expected, rtol=0.0, atol=1.0e-12)

Linux上では、拡張モジュールのあるディレクトリをPYTHONPATHに加えてテストします。

PYTHONPATH=build python -m pytest -q tests/test_fastcalc.py

テスト時にModuleNotFoundErrorが出る場合は、buildディレクトリ内の.soファイルの場所と、実行しているPythonのバージョンを確認してください。

なお、Mesonとf2pyを自分で連携させる方法は、公式ドキュメントにある構成を参考にしています。複数モジュールへの拡張や追加ライブラリのリンクは、扱うFortranコードに合わせて調整が必要です。


8. OpenMPを有効にする

MesonではOpenMPの依存関係を次のように指定できます。

openmp_dep = dependency('openmp', required: true)

Fortranの実行ファイルなら、次のように設定します。

solver = executable(
  'solver',
  'src/solver.f90',
  dependencies: openmp_dep
)

f2pyの拡張モジュールに組み込む場合には、py.extension_module()のdependenciesにopenmp_depを追加します。

dependencies: [
  np_dep,
  f2py_dep,
  openmp_dep
]

OpenMPのコンパイルとリンクに必要なオプションを、コンパイラに応じてMesonが扱います。もちろん、これだけで計算が自動的に並列化されるわけではありません。Fortran側にOpenMPの指示行が必要です。

!$omp parallel do private(i) schedule(static)
do i = 1, n
    c(i) = a(i) + b(i)
end do
!$omp end parallel do

また、do concurrentとOpenMPは別の機能です。どちらを使う場合でも、実際の並列化状況や結果の再現性を確認します。


9. Dockerでビルド環境を揃える

開発用PCとCI環境でコンパイラやPythonパッケージのバージョンが異なると、同じソースでも結果が変わったり、ビルドに失敗したりすることがあります。そのため、MesonとDockerの組み合わせも便利です。

以下は第3章のFortran単独ビルド用プロジェクトをコンテナ内でテストする例です。

Dockerfile を表示
FROM python:3.12-slim

# FortranとMeson/Ninjaのビルドに必要なツール
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
      build-essential \
      gfortran \
      ninja-build \
      pkg-config && \
    rm -rf /var/lib/apt/lists/*

# f2pyにも対応できるようにNumPyを導入
RUN python -m pip install --no-cache-dir \
    meson \
    numpy==2.5.4 \
    pytest

WORKDIR /work
COPY meson.build ./
COPY src/ ./src/
COPY tests/ ./tests/

# ビルドとテストをイメージ作成時に確認する
RUN meson setup build --buildtype=debug && \
    meson compile -C build && \
    meson test -C build --print-errorlogs

CMD ["/bin/sh"]

同じ階層にDockerfileを保存した後、次のように実行します。

docker build -t meson-fortran-demo .
docker run --rm -it meson-fortran-demo

この例は、ローカル側で作成したmeson.build、src、testsをコンテナへコピーする構成です。開発中に毎回イメージを作り直したくない場合は、Docker Composeでソースをマウントする構成へ拡張できます。


10. meson-pythonとの違い

Mesonと名前が似ていますが、meson-pythonは用途が少し異なります。

  • Meson:CやFortranなどをコンパイルするビルドシステム
  • meson-python:Mesonで作るPython拡張を、pip installやWheelの仕組みに接続するビルドバックエンド

例えば、f2pyの拡張モジュールをPythonパッケージとして配布する場合は、pyproject.tomlを用意します。

[build-system]
requires = [
    "meson-python>=0.15.0",
    "numpy>=2.0"
]
build-backend = "mesonpy"

[project]
name = "hydraulic-core"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["numpy>=2.0"]

ただし、これを記述するだけでWheelが完成するわけではありません。meson.build側でも拡張モジュールのインストール先(py.extension_module(..., install: true, ...)など)を設定する必要があります。

パッケージ構成まで用意したら、次のようにWheelを作成できます。

python -m pip install build meson-python
python -m build --wheel

プログラムを手元で実行するだけなら、ここまでの仕組みは必須ではありません。複数のPCへ配布したい場合や、Pythonパッケージとして管理したい場合に検討するとよいです。


11. よくあるエラー

状況 確認するところ
meson: command not found 仮想環境の有効化とMesonのインストール
Unknown compiler(s) gfortran --versionやgcc --version
ninja: command not found ninja-buildのインストール
NumPyが見つからない Mesonが使うPythonと仮想環境が一致しているか
ModuleNotFoundError: fastcalc .soの場所、Pythonのバージョン、PYTHONPATH
undefined symbol Fortran本体・ラッパー・外部ライブラリのリンク漏れ
ラッパー生成ファイルが不足 f2pyの出力名とcustom_target()のoutput
OpenMPでリンクエラー dependency('openmp')の設定とコンパイラの対応

特に、f2pyとMesonを組み合わせる際は、Pythonの仮想環境が混在していないかを最初に確認した方がよいです。

which python
which meson
python -c "import numpy; print(numpy.__version__)"
python -c "import numpy; print(numpy.get_include())"
python -c "import numpy.f2py; print(numpy.f2py.get_include())"

12. 実際の数値計算ソルバーに取り入れるなら

河川の1次元流れ計算や2次元氾濫計算など、ある程度大きなFortranプログラムでは、数値計算とPythonとの接続部分を分けておくと管理しやすくなります。

hydraulic_solver/
├── meson.build
├── pyproject.toml
├── src/
│   ├── continuity.f90
│   ├── momentum.f90
│   ├── boundary.f90
│   ├── coupling.f90
│   └── hydraulic_api.f90
├── python/
│   └── hydraulic/
│       └── __init__.py
└── tests/
    ├── test_mass_balance.py
    ├── test_boundary.py
    └── test_solver.py

Fortran側は計算コアとして独立させ、Pythonから呼び出すためのhydraulic_api.f90を別に用意します。この境界部分をf2pyで公開する形です。

こうしておけば、数値計算の内部実装を変更しても、Python側への影響を抑えやすくなります。また、水収支や境界条件のテストを自動実行すれば、変更による計算結果の差も追跡しやすくなります。

個人的には、最初から複雑なビルド設定にする必要はないと思います。まずはMesonでFortranを単独実行し、次にf2pyでPythonから呼び出し、必要になったところでOpenMPやWheel配布を追加していくのが分かりやすい進め方です。


13. 動作確認について

掲載した配列加算のFortranソースとテストプログラムは、gfortran 14.2.0で-std=f2018 -Wall -Wextra -fcheck=allを指定してコンパイルし、PASS: add_vectorsを確認しました。また、NumPy 2.3.5に含まれるf2pyで、同じFortranソースからCラッパー(fastcalcmodule.c)を生成できることを確認しています。

一方、この記事の作成環境にはMesonがインストールされておらず、ネットワーク制限のため追加導入もできませんでした。そのため、Mesonを通した最終ビルド、f2py拡張モジュールの実行、Dockerビルドは未検証です。Mesonの設定は公式資料を基に記述していますが、実行環境に合わせて確認してください。掲載した結果をMesonの実行結果として扱わないようにしています。

参考資料


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?