通过 cmake 使用#
就复杂性而言, cmake 介于 make 和 meson 之间.学习曲线更加陡峭,因为 CMake 语法不是 pythonic 的,并且更接近具有环境变量的 make .
但是,权衡是增强的灵活性以及对大多数架构和编译器的支持.本文档不介绍语法,但是这个 extensive CMake collection 的资源非常棒.
备注
cmake 在混合语言系统中非常流行,但是对 f2py 的支持不是特别原生或令人愉快.更自然的方法是考虑 通过 scikit-build 使用
Fibonacci 演练 (F77)#
返回到 三种封装方式 - 快速入门 部分中的 fib 示例.
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 -m numpy.f2py fib1.f 输出,即 fib1module.c ,这是有益的.有了这个;我们现在可以按如下方式初始化 CMakeLists.txt 文件:
cmake_minimum_required(VERSION 3.18) # Needed to avoid requiring embedded Python libs too
project(fibby
VERSION 1.0
DESCRIPTION "FIB module"
LANGUAGES C Fortran
)
# Safety net
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(
FATAL_ERROR
"In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\n"
)
endif()
# Grab Python, 3.8 or newer
find_package(Python 3.8 REQUIRED
COMPONENTS Interpreter Development.Module NumPy)
# Grab the variables from a local Python installation
# F2PY headers
execute_process(
COMMAND "${Python_EXECUTABLE}"
-c "import numpy.f2py; print(numpy.f2py.get_include())"
OUTPUT_VARIABLE F2PY_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Print out the discovered paths
include(CMakePrintHelpers)
cmake_print_variables(Python_INCLUDE_DIRS)
cmake_print_variables(F2PY_INCLUDE_DIR)
cmake_print_variables(Python_NumPy_INCLUDE_DIRS)
# Common variables
set(f2py_module_name "fibby")
set(fortran_src_file "${CMAKE_SOURCE_DIR}/fib1.f")
set(f2py_module_c "${f2py_module_name}module.c")
# Generate sources
add_custom_target(
genpyf
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}"
)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}"
COMMAND ${Python_EXECUTABLE} -m "numpy.f2py"
"${fortran_src_file}"
-m "fibby"
--lower # Important
DEPENDS fib1.f # Fortran source
)
# Set up target
Python_add_library(${CMAKE_PROJECT_NAME} MODULE WITH_SOABI
"${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_c}" # Generated
"${F2PY_INCLUDE_DIR}/fortranobject.c" # From NumPy
"${fortran_src_file}" # Fortran source(s)
)
# Depend on sources
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Python::NumPy)
add_dependencies(${CMAKE_PROJECT_NAME} genpyf)
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE "${F2PY_INCLUDE_DIR}")
上面定义的 CMakeLists.txt 文件的一个关键要素是, add_custom_command 用于生成包装器 C 文件,然后通过 add_custom_target 指令将其添加为实际共享库目标的依赖项,从而防止每次都运行该命令. 此外,用于获取 fortranobject.c 文件的方法也可用于在旧版本的 cmake 上获取 numpy 标头.
然后,这以与其他模块相同的方式工作,尽管命名约定不同,并且输出库不会自动以 cython 信息作为前缀.
ls .
# CMakeLists.txt fib1.f
cmake -S . -B build
cmake --build build
cd build
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.]
当现有工具链已经存在并且不鼓励使用 scikit-build 或其他附加的 python 依赖项时,这尤其有用.