使用CMake构建可移植的Python C-API

时间:2018-08-12 01:35:44

标签: c++ c cmake linker python-c-api

让我们考虑以下琐碎的代码,将C连接到python

// main.cpp

#include <Python.h>

int main()
{
    Py_Initialize();
    return 0;
}

以下CMake代码适用于

CMakeLists.txt

cmake_minimum_required(VERSION 2.8.9)

project (myapp)

find_package(Threads)

set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(Python_ADDITIONAL_VERSIONS 3.5)
find_package(Threads REQUIRED)
find_package(PythonLibs REQUIRED)

include_directories(${PYTHON_INCLUDE_DIRS})

add_executable(myapp
    main.cpp
)

target_link_libraries(myapp rt)
target_link_libraries(myapp ${CMAKE_DL_LIBS})
target_link_libraries(myapp "-L/usr/lib/python3.5/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.5m -Bsymbolic-functions")
#target_link_libraries(myapp ${PYTHON_LIBRARIES})
target_link_libraries(myapp ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(myapp ${PYTHON_LIBRARIES})

问题是,当我将代码移至另一台计算机时,链接失败,因为我在那里有python 3.6而不是3.5。因此,我应该将每个3.5替换为3.6。是否有用于链接python的任何便携式解决方案?

我已经尝试过此方法,而不是冗长的行,但它不起作用:

target_link_libraries(myapp ${PYTHON_LIBRARIES})

显示以下消息:

/usr/local/lib/libpython3.5m.a(posixmodule.o): In function `os_forkpty_impl':
/usr/src/Python-3.5.2/./Modules/posixmodule.c:5972: undefined reference to `forkpty'
/usr/local/lib/libpython3.5m.a(posixmodule.o): In function `os_openpty_impl':
/usr/src/Python-3.5.2/./Modules/posixmodule.c:5878: undefined reference to `openpty'
/usr/local/lib/libpython3.5m.a(dynload_shlib.o): In function `_PyImport_FindSharedFuncptr':
/usr/src/Python-3.5.2/./Python/dynload_shlib.c:82: undefined reference to `dlsym'
/usr/src/Python-3.5.2/./Python/dynload_shlib.c:95: undefined reference to `dlopen'
/usr/src/Python-3.5.2/./Python/dynload_shlib.c:126: undefined reference to `dlsym'
/usr/src/Python-3.5.2/./Python/dynload_shlib.c:95: undefined reference to `dlopen'
/usr/src/Python-3.5.2/./Python/dynload_shlib.c:101: undefined reference to `dlerror'
collect2: error: ld returned 1 exit status
CMakeFiles/myapp.dir/build.make:96: recipe for target 'myapp' failed
make[2]: *** [myapp] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/myapp.dir/all' failed
make[1]: *** [CMakeFiles/myapp.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

1 个答案:

答案 0 :(得分:0)

分发与python动态链接的独立应用程序

通常来说,由于次要版本的python之间的API和ABI发生了变化,因此您必须为要支持的每个python版本分发一个可执行文件版本。

例如,您的其他应用程序可以命名为myapp-cpython-34m-linux_x86_64myapp-cpython-35m-linux_x86_64,...或myapp-py34m,...

稳定的应用程序二进制接口

Stable Application Binary Interface文档中所述,您可以使用Py_LIMITED_API定义编译程序(这意味着仅使用API​​的一个子集),这将允许您创建与任何版本的python兼容的二进制文件从python 3.2开始。

也就是说,这仅适用于在cpython解释器中导入的cpython二进制扩展名。实际上,在这种情况下,python符号已经可用。

在您的照料下,myapp必须找到任何共享的python 3.x库,并且我相信操作系统库加载程序不支持该库。

可能的解决方案

  • 重新分发自己的python共享库

  • 针对CPython库的静态链接

请注意,如果您的项目使用的编译器不同于给定版本的CPython的officially associated编译器,则可以使用https://github.com/python-cmake-buildsystem/python-cmake-buildsystem

轻松地编译CPython本身。

调整和改进您的项目

这是您的项目的改进版本,为目标指定了Usage requirements

cmake_minimum_required(VERSION 3.12)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

project(myapp)

# dependencies
set(Python_ADDITIONAL_VERSIONS 3.5)
find_package(PythonLibs REQUIRED)
find_package(Threads REQUIRED)

# executable
add_executable(myapp
  main.cpp
  )
target_compile_definitions(myapp
  PRIVATE
    Py_LIMITED_API
  )
target_include_directories(myapp
  PRIVATE
    ${PYTHON_INCLUDE_DIRS}
  )
target_link_libraries(myapp
  PRIVATE
    ${CMAKE_DL_LIBS}
    Threads::Threads
    ${PYTHON_LIBRARIES}
    rt
  )