问题描述
我正在尝试使用 CMake 和 f2py 构建 Python 扩展模块。该模块构建得很好,但 setuptools 找不到它。
我的构建目录如下所示:
cmake/modules/FindF2PY.cmake
cmake/modules/FindPythonExtensions.cmake
cmake/modules/UseF2PY.cmake
cmake/modules/FindNumPy.cmake
cmake/modules/targetLinkLibrariesWithDynamicLookup.cmake
setup.py
CMakeLists.txt
f2py_test/__init__.py
f2py_test.f90
f2py_test/init.py 只是一个空文件。 cmake/modules 中的文件是从 scikit-build 复制的。
setup.py 基于来自 Martino Pilia
的博文from setuptools import setup,Extension
from setuptools.command.build_ext import build_ext
import os
import sys
class CMakeExtension(Extension):
def __init__(self,name,cmake_lists_dir='.',**kwa):
Extension.__init__(self,sources=[],**kwa)
self.cmake_lists_dir = os.path.abspath(cmake_lists_dir)
class cmake_build_ext(build_ext):
def build_extensions(self):
import subprocess
# Ensure that CMake is present and working
try:
out = subprocess.check_output(['cmake','--version'])
except OSError:
raise RuntimeError('Cannot find CMake executable')
for ext in self.extensions:
extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
cfg = 'Debug' if os.environ.get('disPTOOLS_DEBUG','OFF') == 'ON' else 'Release'
cmake_args = [
'-DCMAKE_BUILD_TYPE=%s' % cfg,# Ask CMake to place the resulting library in the directory
# containing the extension
'-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(),extdir),# Other intermediate static libraries are placed in a
# temporary build directory instead
'-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(),self.build_temp),# Hint CMake to use the same Python executable that
# is launching the build,prevents possible mismatching if
# multiple versions of Python are installed
'-DPYTHON_EXECUTABLE={}'.format(sys.executable),]
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
# Config
subprocess.check_call(['cmake',ext.cmake_lists_dir] + cmake_args,cwd=self.build_temp)
# Build
subprocess.check_call(['cmake','--build','.','--config',cfg],cwd=self.build_temp)
setup(
name="f2py_test",version='0.0.1',packages=['f2py_test'],ext_modules=[CMakeExtension(name='f2py_test_')],cmdclass={'build_ext':cmake_build_ext},)
CMakeLists.txt:
cmake_minimum_required(VERSION 3.10.2)
project(f2py_test)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/modules/")
enable_language(Fortran)
find_package(F2PY)
find_package(PythonExtensions)
set(f2py_test_sources f2py_test.f90)
add_library(f2py_test ${f2py_test_sources})
function(add_f2py_target)
set(options)
set(singleValueArgs)
set(multiValueArgs SOURCES DEPENDS)
cmake_parse_arguments(
PARSE_ARGV 1
F2PY_TARGET "${options}" "${singleValueArgs}"
"${multiValueArgs}"
)
set(F2PY_TARGET_MODULE_NAME ${ARGV0})
set(generated_module_file ${CMAKE_CURRENT_BINARY_DIR}/${F2PY_TARGET_MODULE_NAME}${PYTHON_EXTENSION_MODULE_SUFFIX})
message(${generated_module_file})
set(f2py_module_sources_fullpath "")
foreach(f ${F2PY_TARGET_SOURCES})
list(APPEND f2py_module_sources_fullpath "${CMAKE_CURRENT_SOURCE_DIR}/${f}")
endforeach()
add_custom_target(${F2PY_TARGET_MODULE_NAME} ALL
DEPENDS ${generated_module_file} ${generated_module_file}
)
if(F2PY_TARGET_DEPENDS)
add_dependencies(${F2PY_TARGET_MODULE_NAME} ${F2PY_TARGET_DEPENDS})
endif()
if(APPLE)
set(F2PY_ENV LDFLAGS='-undefined dynamic_lookup -bundle')
else()
set(F2PY_ENV LDFLAGS='$ENV{LDFLAGS} -shared')
endif()
add_custom_command(
OUTPUT ${generated_module_file}
DEPENDS ${F2PY_TARGET_SOURCES}
COMMAND env ${F2PY_ENV} ${F2PY_EXECUTABLE} --quiet
-m ${F2PY_TARGET_MODULE_NAME}
-c ${f2py_module_sources_fullpath}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_target_properties(
${F2PY_TARGET}
PROPERTIES
PREFIX ""
OUTPUT_NAME ${F2PY_TARGET_MODULE_NAME})
endfunction(add_f2py_target)
if(F2PY_FOUND)
add_f2py_target(f2py_test_ SOURCES ${f2py_test_sources} DEPENDS f2py_test)
endif()
f2py_test.f90:
module mod_f2py_test
implicit none
contains
subroutine f2py_test(a,b,c)
real(kind=8),intent(in)::a,b
real(kind=8),intent(out)::c
end subroutine f2py_test
end module mod_f2py_test
python setup.py develop
调用 cmake 来构建扩展模块,我可以在 ./build/temp.macosx-10.14-x86_64-3.8/f2py_test_.cpython-38-darwin.so
中看到。但是,setuptools 找不到该文件并打印消息 error: can't copy 'build/lib.macosx-10.14-x86_64-3.8/f2py_test_.cpython-38-darwin.so': doesn't exist or not a regular file
。
我该如何 1) 告诉 CMake 在 setuptools 期望的位置安装扩展模块,或者 2) 告诉 setuptools 在哪里可以找到扩展模块。
解决方法
setuptools 查找编译模块的目录可以通过build_ext.get_ext_fullpath(ext.name)获取。在上面的代码中,通过设置变量 CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE 将结果路径传递给 CMake。
由于 f2py 是通过自定义命令调用的,因此扩展模块不会自动复制到输出目录。这可以通过另一个调用 add_custom_command 来实现:
add_custom_command(TARGET "${F2PY_TARGET_MODULE_NAME}" POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/${generated_module_file}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE}/${generated_module_file}")