Использование через meson#

Примечание

Большая часть этого документа теперь устарела, можно запустить f2py с --build-dir чтобы получить каркас meson проект с базовыми зависимостями настройка.

Изменено в версии 1.26.x: Система сборки по умолчанию для f2py теперь meson, см. Статус numpy.distutils и рекомендации по миграции для некоторых дополнительных деталей.

Ключевое преимущество, полученное за счет использования meson над методами, описанными в Использование через numpy.distutils заключается в том, что это легко интегрируется в существующие системы и более крупные проекты. meson имеет довольно питонический синтаксис, что делает его более удобным и подходящим для расширения python пользователи.

Пошаговое руководство по Фибоначчи (F77)#

Нам понадобится сгенерированный C обёртку, прежде чем мы сможем использовать универсальную систему сборки, такую как meson. Мы получим это следующим образом:

python -m numpy.f2py fib1.f -m fib2

Теперь рассмотрим следующее meson.build файл для fib и scalar примеры из Три способа обертки - начало работы раздел:

project('f2py_examples', 'c',
  version : '0.1',
  license: 'BSD-3',
  meson_version: '>=0.64.0',
  default_options : ['warning_level=2'],
)

add_languages('fortran')

py_mod = import('python')
py = py_mod.find_installation(pure: false)
py_dep = py.dependency()

incdir_numpy = run_command(py,
  ['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'],
  check : true
).stdout().strip()

incdir_f2py = run_command(py,
    ['-c', 'import os; os.chdir(".."); import numpy.f2py; print(numpy.f2py.get_include())'],
    check : true
).stdout().strip()

inc_np = include_directories(incdir_numpy, incdir_f2py)

py.extension_module('fib2',
  [
    'fib1.f',
    'fib2module.c',  # note: this assumes f2py was manually run before!
  ],
  incdir_f2py / 'fortranobject.c',
  include_directories: inc_np,
  dependencies : py_dep,
  install : true
)

На этом этапе сборка завершится, но импорт не удастся:

meson setup builddir
meson compile -C builddir
cd builddir
python -c 'import fib2'
Traceback (most recent call last):
File "", line 1, in 
ImportError: fib2.cpython-39-x86_64-linux-gnu.so: undefined symbol: FIB_
# Check this isn't a false positive
nm -A fib2.cpython-39-x86_64-linux-gnu.so | grep FIB_
fib2.cpython-39-x86_64-linux-gnu.so: U FIB_

Напомним, что исходный пример, воспроизведенный ниже, был в SCREAMCASE:

C FILE: FIB1.F
      SUBROUTINE FIB(A,N)
C
C     CALCULATE FIRST N FIBONACCI NUMBERS
C
      INTEGER N
      REAL*8 A(N)
      DO I=1,N
         IF (I.EQ.1) THEN
            A(I) = 0.0D0
         ELSEIF (I.EQ.2) THEN
            A(I) = 1.0D0
         ELSE 
            A(I) = A(I-1) + A(I-2)
         ENDIF
      ENDDO
      END
C END FILE FIB1.F

При стандартном подходе подпрограмма, предоставляемая python является fib и не FIB. Это означает, что у нас есть несколько вариантов. Один подход (где возможно) - привести исходный файл Fortran к нижнему регистру, например, с помощью:

tr "[:upper:]" "[:lower:]" < fib1.f > fib1.f
python -m numpy.f2py fib1.f -m fib2
meson --wipe builddir
meson compile -C builddir
cd builddir
python -c 'import fib2'

Однако это требует возможности изменять исходный код, что не всегда возможно. Самый простой способ решить это — позволить f2py разберитесь с этим:

python -m numpy.f2py fib1.f -m fib2 --lower
meson --wipe builddir
meson compile -C builddir
cd builddir
python -c 'import fib2'

Автоматизация генерации обёрток#

Основная проблема в описанном выше рабочем процессе — ручное отслеживание входных данных. Хотя для определения фактических выходных данных потребовалось бы больше усилий по причинам, обсуждаемым в F2PY и системы сборки.

Примечание

Из NumPy 1.22.4 и далее, f2py детерминированно генерирует файлы-обёртки на основе стандарта Fortran входного файла (F77 или выше). --skip-empty-wrappers может быть передан в f2py для восстановления предыдущего поведения генерации обёрток только при необходимости входными данными.

Однако мы можем расширить наш рабочий процесс простым способом, чтобы учесть файлы, для которых выходные данные известны при настройке системы сборки.

project('f2py_examples', 'c',
  version : '0.1',
  license: 'BSD-3',
  meson_version: '>=0.64.0',
  default_options : ['warning_level=2'],
)

add_languages('fortran')

py_mod = import('python')
py = py_mod.find_installation(pure: false)
py_dep = py.dependency()

incdir_numpy = run_command(py,
  ['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'],
  check : true
).stdout().strip()

incdir_f2py = run_command(py,
    ['-c', 'import os; os.chdir(".."); import numpy.f2py; print(numpy.f2py.get_include())'],
    check : true
).stdout().strip()

fibby_source = custom_target('fibbymodule.c',
  input : ['fib1.f'],  # .f so no F90 wrappers
  output : ['fibbymodule.c', 'fibby-f2pywrappers.f'],
  command : [py, '-m', 'numpy.f2py', '@INPUT@', '-m', 'fibby', '--lower']
)

inc_np = include_directories(incdir_numpy, incdir_f2py)

py.extension_module('fibby',
  ['fib1.f', fibby_source],
  incdir_f2py / 'fortranobject.c',
  include_directories: inc_np,
  dependencies : py_dep,
  install : true
)

Это может быть скомпилировано и запущено, как и раньше.

rm -rf builddir
meson setup builddir
meson compile -C builddir
cd builddir
python -c "import numpy as np; import fibby; a = np.zeros(9); fibby.fib(a); print (a)"
# [ 0.  1.  1.  2.  3.  5.  8. 13. 21.]

Ключевые моменты#

Стоит иметь в виду следующее:

  • Невозможно использовать SCREAMCASE в этом контексте, поэтому либо содержимое .f файл или сгенерированная обёртка .c необходимо понизить до обычных букв; что может быть облегчено с помощью --lower опция F2PY