140 lines
6.7 KiB
CMake
140 lines
6.7 KiB
CMake
# Python bindings for PdfEngine using pybind11
|
|
#
|
|
|
|
# The .pyd ABI tag (cp312 vs cp313) must match the interpreter that runs
|
|
# the FastAPI gateway. Prefer gateway/.venv when it exists so `import pdfengine`
|
|
# works in uvicorn without a second Python install.
|
|
if(WIN32)
|
|
set(_gateway_python "${CMAKE_SOURCE_DIR}/gateway/.venv/Scripts/python.exe")
|
|
else()
|
|
set(_gateway_python "${CMAKE_SOURCE_DIR}/gateway/.venv/bin/python")
|
|
endif()
|
|
if(EXISTS "${_gateway_python}")
|
|
set(Python_EXECUTABLE "${_gateway_python}" CACHE FILEPATH "Python for pybind11" FORCE)
|
|
set(Python3_EXECUTABLE "${_gateway_python}" CACHE FILEPATH "Python3 for pybind11" FORCE)
|
|
set(PYTHON_EXECUTABLE "${_gateway_python}" CACHE FILEPATH "Python for pybind11 (legacy)" FORCE)
|
|
message(STATUS "pybind11: using gateway venv ${_gateway_python}")
|
|
endif()
|
|
|
|
# Drop stale FindPythonLibsNew cache from a previous configure (it can keep
|
|
# PYTHON_MODULE_EXTENSION=.cp313-*.pyd and python313.lib even after we switch
|
|
# the interpreter to the gateway 3.12 venv).
|
|
unset(PYTHON_MODULE_EXTENSION CACHE)
|
|
unset(PYTHON_LIBRARIES CACHE)
|
|
unset(PYTHON_INCLUDE_DIRS CACHE)
|
|
unset(PYTHON_VERSION CACHE)
|
|
unset(PYTHON_VERSION_MAJOR CACHE)
|
|
unset(PYTHON_VERSION_MINOR CACHE)
|
|
unset(PYTHON_IS_DEBUG CACHE)
|
|
unset(PYTHON_MODULE_PREFIX CACHE)
|
|
unset(PYTHON_MODULE_DEBUG_POSTFIX CACHE)
|
|
|
|
if(Python_EXECUTABLE)
|
|
execute_process(
|
|
COMMAND "${Python_EXECUTABLE}" -c "import sys, sysconfig, pathlib; base=pathlib.Path(sys.base_prefix); ver=f'{sys.version_info.major}{sys.version_info.minor}'; print(sysconfig.get_config_var('EXT_SUFFIX') or ''); print(sysconfig.get_path('include')); print(base / 'libs' / f'python{ver}.lib')"
|
|
OUTPUT_VARIABLE _pdfengine_py_info
|
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
ERROR_QUIET
|
|
)
|
|
string(REPLACE "\r" "" _pdfengine_py_info "${_pdfengine_py_info}")
|
|
string(REPLACE "\n" ";" _pdfengine_py_info_lines "${_pdfengine_py_info}")
|
|
list(LENGTH _pdfengine_py_info_lines _pdfengine_py_info_len)
|
|
if(_pdfengine_py_info_len GREATER_EQUAL 3)
|
|
list(GET _pdfengine_py_info_lines 0 _pdfengine_py_ext)
|
|
list(GET _pdfengine_py_info_lines 1 _pdfengine_py_inc)
|
|
list(GET _pdfengine_py_info_lines 2 _pdfengine_py_lib)
|
|
file(TO_CMAKE_PATH "${_pdfengine_py_ext}" _pdfengine_py_ext)
|
|
file(TO_CMAKE_PATH "${_pdfengine_py_inc}" _pdfengine_py_inc)
|
|
file(TO_CMAKE_PATH "${_pdfengine_py_lib}" _pdfengine_py_lib)
|
|
if(_pdfengine_py_ext)
|
|
set(PYTHON_MODULE_EXTENSION "${_pdfengine_py_ext}" CACHE INTERNAL "Python extension suffix")
|
|
message(STATUS "pybind11: extension suffix ${_pdfengine_py_ext}")
|
|
endif()
|
|
# vcpkg ships a static python312.lib; linking the .pyd against that
|
|
# crashes when uvicorn loads it (no python312.dll import). Use the
|
|
# interpreter's own import lib / headers instead.
|
|
if(EXISTS "${_pdfengine_py_lib}" AND EXISTS "${_pdfengine_py_inc}/Python.h")
|
|
set(Python_LIBRARY "${_pdfengine_py_lib}" CACHE FILEPATH "Python import library" FORCE)
|
|
set(Python_LIBRARIES "${_pdfengine_py_lib}" CACHE FILEPATH "Python import library" FORCE)
|
|
set(Python_INCLUDE_DIR "${_pdfengine_py_inc}" CACHE PATH "Python headers" FORCE)
|
|
set(Python3_LIBRARY "${_pdfengine_py_lib}" CACHE FILEPATH "Python3 import library" FORCE)
|
|
set(Python3_INCLUDE_DIR "${_pdfengine_py_inc}" CACHE PATH "Python3 headers" FORCE)
|
|
get_filename_component(_pdfengine_py_root "${_pdfengine_py_inc}" DIRECTORY)
|
|
set(Python_ROOT_DIR "${_pdfengine_py_root}" CACHE PATH "CPython install root" FORCE)
|
|
message(STATUS "pybind11: CPython lib ${_pdfengine_py_lib}")
|
|
message(STATUS "pybind11: CPython include ${_pdfengine_py_inc}")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
set(PYBIND11_FINDPYTHON ON)
|
|
|
|
find_package(pybind11 CONFIG REQUIRED)
|
|
|
|
# Declare the python module target. We name the target pdfengine_py to avoid
|
|
# target collision with the static C++ library pdfengine, but we set the
|
|
# OUTPUT_NAME to pdfengine to produce the correct importable module.
|
|
pybind11_add_module(pdfengine_py python/pdfengine_py.cpp $<TARGET_OBJECTS:pdfengine>)
|
|
|
|
set_target_properties(pdfengine_py PROPERTIES
|
|
OUTPUT_NAME "pdfengine"
|
|
ARCHIVE_OUTPUT_NAME "pdfengine_py_import"
|
|
)
|
|
if(_pdfengine_py_ext)
|
|
set_target_properties(pdfengine_py PROPERTIES SUFFIX "${_pdfengine_py_ext}")
|
|
endif()
|
|
|
|
target_link_libraries(pdfengine_py PRIVATE pdfengine::pdfengine)
|
|
|
|
# OBJECT-library usage requirements are not always enough on MSVC for the
|
|
# python module (it also consumes $<TARGET_OBJECTS:pdfengine>). Repeat the
|
|
# libraries the engine objects reference so the .pyd link line is complete.
|
|
target_link_libraries(pdfengine_py PRIVATE qpdf::libqpdf ZLIB::ZLIB JPEG::JPEG)
|
|
if(PDFENGINE_WITH_PDFIUM)
|
|
target_link_libraries(pdfengine_py PRIVATE pdfium::pdfium)
|
|
endif()
|
|
if(_pdfengine_py_lib AND EXISTS "${_pdfengine_py_lib}")
|
|
target_link_libraries(pdfengine_py PRIVATE "${_pdfengine_py_lib}")
|
|
endif()
|
|
|
|
target_include_directories(pdfengine_py PRIVATE
|
|
"${CMAKE_SOURCE_DIR}/engine/src"
|
|
)
|
|
|
|
if(MSVC)
|
|
target_link_options(pdfengine_py PRIVATE "/FORCE:MULTIPLE")
|
|
endif()
|
|
|
|
# vcpkg's find_package(Python) wrapper still injects its static python312.lib.
|
|
# A pybind module must link only the host interpreter's import lib (python312.dll).
|
|
foreach(_py_tgt IN ITEMS Python::Module Python::Python Python3::Module Python3::Python)
|
|
if(TARGET ${_py_tgt} AND _pdfengine_py_lib)
|
|
set_property(TARGET ${_py_tgt} PROPERTY INTERFACE_LINK_LIBRARIES "${_pdfengine_py_lib}")
|
|
set_property(TARGET ${_py_tgt} PROPERTY IMPORTED_LOCATION "${_pdfengine_py_lib}")
|
|
set_property(TARGET ${_py_tgt} PROPERTY IMPORTED_IMPLIB "${_pdfengine_py_lib}")
|
|
endif()
|
|
endforeach()
|
|
get_target_property(_pdfengine_py_link pdfengine_py LINK_LIBRARIES)
|
|
if(_pdfengine_py_link)
|
|
set(_pdfengine_py_kept "")
|
|
foreach(_lib IN LISTS _pdfengine_py_link)
|
|
if(_lib MATCHES "vcpkg_installed" AND _lib MATCHES "[Pp]ython")
|
|
message(STATUS "pybind11: dropping vcpkg CPython ${_lib}")
|
|
continue()
|
|
endif()
|
|
list(APPEND _pdfengine_py_kept "${_lib}")
|
|
endforeach()
|
|
set_property(TARGET pdfengine_py PROPERTY LINK_LIBRARIES "${_pdfengine_py_kept}")
|
|
endif()
|
|
|
|
# Set warnings and sanitizers for the bindings module
|
|
pdfengine_set_warnings(pdfengine_py)
|
|
pdfengine_enable_sanitizers(pdfengine_py)
|
|
|
|
# Copy the compiled .pyd (or .so) file to the gateway/ directory so the
|
|
# Python FastAPI app and its tests can import it immediately after building.
|
|
add_custom_command(TARGET pdfengine_py POST_BUILD
|
|
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pdfengine_py> "${CMAKE_SOURCE_DIR}/gateway/"
|
|
COMMENT "Copying compiled Python extension to gateway/ directory"
|
|
)
|