0

Let's consider the following trivial code for connecting C to python as follows

// main.cpp

#include <Python.h>

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

The following CMake code works fine for

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})

The problem is when I move the code to another computer, the linking fails as I have python 3.6 there instead of 3.5. Thus I should replace every 3.5 with 3.6. Is there any portable solution for linking python?

I have tried this instead of the lengthy line but it does not work:

target_link_libraries(myapp ${PYTHON_LIBRARIES})

With the following message:

/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
ar2015
  • 5,558
  • 8
  • 53
  • 110
  • Variable `Python_ADDITIONAL_VERSIONS` is used mainly for specify Python versions **not known for CMake** version you use. If you want to specify *minimum* version, set it as the second argument for `find_package()`: `find_package(PythonLibs 3.5 REQUIRED)`. As for the error messages you show, they are about non-python symbols: these symbols are from `dl` library and from `utils` one, which, probably, is linked into `rt` or `threads`. Note, that you should link these libraries **after** the python ones. – Tsyvarev Aug 12 '18 at 08:47
  • @Tsyvarev, I did that explicitly too via `target_link_libraries(myapp "-ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions")` but the error message is exactly the same. – ar2015 Aug 12 '18 at 09:06
  • Interestingly `target_link_libraries(myapp "-lpython3.5m -Wl,-Bsymbolic-functions")` is sufficient. – ar2015 Aug 12 '18 at 09:10

1 Answers1

0

Distributing standalone application dynamically linked against python

Generally speaking, since the API and ABI between minor version of python change, you would have to distribute one version of your executable for each python version you would like to support.

For example, your different application could be named myapp-cpython-34m-linux_x86_64, myapp-cpython-35m-linux_x86_64, ... or myapp-py34m, ...

The Stable Application Binary Interface

As described in Stable Application Binary Interface document, you could compile your program with Py_LIMITED_API definition (meaning only a subset of the API is used), that would allow you create a binary compatible with any version of python start with python 3.2.

That said, this only work well for cpython binary extension that are imported in the cpython interpreter. Indeed, in that case the python symbols are already available.

In your care, myapp would have to find any of the shared python 3.x library and I believe that is not supported by the operating system library loader.

Possible solutions

  • Redistribute your own python shared library

  • Statically link against CPython library

Note that if your project uses a compiler different from the one officially associated with a given version of CPython, you could easily compile CPython itself using https://github.com/python-cmake-buildsystem/python-cmake-buildsystem

Tweak and improvement to your project

Here is an improved version of your project specifying Usage requirements for the target.

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
  )
J-Christophe
  • 1,975
  • 15
  • 13