Portable Python C-API build with CMake - c++

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

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
)

Related

CMake & CodeSynthesis Header-only library not compiling

I have been trying to implement the runtime library which is a header-only library of CodeSynthesis. But I only get a linking compiling error whenever I try to run the generated files which are made by the XSD executable.
Here is the error to show you that I have a linking problem with the runtime library:
[100%] Linking CXX executable XercesGebeuren.exe
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles\XercesGebeuren.dir/objects.a(hello.cxx.obj): in function `xsd::cxx::xml::initialize()':
C:/Users/husey/Desktop/XercesGebeuren/xsd-4.0.0-i686-windows/libxsd/xsd/cxx/xml/elements.hxx:84: undefined reference to `xercesc_3_2::XMLPlatformUtils::Initialize(char const*, char const*, xercesc_3_2::PanicHandler*, xercesc_3_2::MemoryManager*)'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles\XercesGebeuren.dir/objects.a(hello.cxx.obj): in function `xsd::cxx::xml::terminate()':
C:/Users/husey/Desktop/XercesGebeuren/xsd-4.0.0-i686-windows/libxsd/xsd/cxx/xml/elements.hxx:90: undefined reference to `xercesc_3_2::XMLPlatformUtils::Terminate()'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles\XercesGebeuren.dir/objects.a(hello.cxx.obj): in function `xsd::cxx::xml::sax::std_input_stream::std_input_stream(std::istream&)':
C:/Users/husey/Desktop/XercesGebeuren/xsd-4.0.0-i686-windows/libxsd/xsd/cxx/xml/sax/std-input-source.hxx:27: undefined reference to `xercesc_3_2::BinInputStream::BinInputStream()'
---- [And so on] ----
collect2.exe: error: ld returned 1 exit status
mingw32-make[3]: *** [CMakeFiles\XercesGebeuren.dir\build.make:122: XercesGebeuren.exe] Error 1
mingw32-make[2]: *** [CMakeFiles\Makefile2:95: CMakeFiles/XercesGebeuren.dir/all] Error 2
mingw32-make[1]: *** [CMakeFiles\Makefile2:102: CMakeFiles/XercesGebeuren.dir/rule] Error 2
mingw32-make: *** [Makefile:137: XercesGebeuren] Error 2
I have followed the guide given by CodeSynthesis to generate my hello.cxx & hello.hxx files with the command: xsd cxx-tree --std c++11 hello.xsd
CMake
cmake_minimum_required(VERSION 3.17)
project(XercesGebeuren)
set(CMAKE_CXX_STANDARD 20)
set(Xsd_DIR ./cmake)
find_package(XercesC REQUIRED)
find_package(Xsd REQUIRED)
add_executable(XercesGebeuren main.cpp src/hello.cxx)
add_library(mylib INTERFACE)
target_include_directories(mylib PUBLIC INTERFACE "./libxsd/")
target_include_directories(XercesGebeuren PUBLIC ${XSD_INCLUDE_DIR})
target_link_libraries(XercesGebeuren PUBLIC mylib)
Thanks to 'Asteroids With Wings' I figured out that a header-only library should not be added as a library in CMake.
Including the library and linking the XercesC library was enough.
CMake solution
cmake_minimum_required(VERSION 3.17)
project(XercesGebeuren)
set(CMAKE_CXX_STANDARD 20)
set(Xsd_DIR ./cmake)
find_package(XercesC REQUIRED)
find_package(Xsd REQUIRED)
add_executable(XercesGebeuren main.cpp src/hello.cxx )
target_include_directories(XercesGebeuren PUBLIC ${XSD_INCLUDE_DIR})
target_link_libraries(XercesGebeuren PUBLIC XercesC::XercesC)

How to use CMake to link the "numpy/arrayobject.h"

I am doing FRVT 1:1 verification. So I need to use the program provided by FRVT. I have connected to the program I wrote, and completed implementation.
But I want to transplant what I wrote in cython step by step in the NullImp Example provided by FRVT. But I got this result:
nullimplfrvt11.cpp
....
#include <Python.h> //(is ok)
#include "numpy/arrayobject.h" //(error)
....
CMakelists.txt
cmake_minimum_required(VERSION 2.8)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
include_directories (${CMAKE_CURRENT_SOURCE_DIR}/../include ${CMAKE_CURRENT_SOURCE_DIR}/../../../common/src/include)
# Configure built shared libraries in top-level lib directory
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../lib)
find_package(numpy REQUIRED)
find_package(PythonLibs REQUIRED)
include_directories(${PYTHON_INCLUDE_DIRS})
target_link_libraries(${PYTHON_LIBRARIES})
# Build the shared libraries
add_library (frvt_11_null_001 SHARED nullimplfrvt11.cpp)
output:
[root#4d3eca5735a2 11]# bash run_validate_11.sh
Checking installation of required packages [SUCCESS]
Looking for core implementation library in /frvt/11/lib.[SUCCESS] Found core implementation library /frvt/11/lib/libfrvt_11_null_001.so.
Attempting to compile and link /frvt/11/lib/libfrvt_11_null_001.so against test harness.
Scanning dependencies of target validate11
[ 50%] Building CXX object src/testdriver/CMakeFiles/validate11.dir/frvt/common/src/util/util.cpp.o
[100%] Building CXX object src/testdriver/CMakeFiles/validate11.dir/validate11.cpp.o
Linking CXX executable ../../../bin/validate11
../../../lib/libfrvt_11_null_001.so: undefined reference to `PyErr_Format'
../../../lib/libfrvt_11_null_001.so: undefined reference to `PyCObject_AsVoidPtr'
../../../lib/libfrvt_11_null_001.so: undefined reference to `PyExc_RuntimeError'
../../../lib/libfrvt_11_null_001.so: undefined reference to `PyObject_GetAttrString'
../../../lib/libfrvt_11_null_001.so: undefined reference to `PyExc_AttributeError'
../../../lib/libfrvt_11_null_001.so: undefined reference to `PyImport_ImportModule'
../../../lib/libfrvt_11_null_001.so: undefined reference to `PyErr_SetString'
../../../lib/libfrvt_11_null_001.so: undefined reference to `PyCObject_Type'
collect2: error: ld returned 1 exit status
make[2]: *** [../bin/validate11] Error 1
make[1]: *** [src/testdriver/CMakeFiles/validate11.dir/all] Error 2
make: *** [all] Error 2
[ERROR] There were errors during compilation of your library with the validation test harness. Please investigate and re-compile.
There are a few issues with the CMake file. The use of find_package(PythonLibs ...) has been deprecated since CMake 3.12. You should consider using the newer commands, such as find_package(Python2 ...). Also, CMake does not provide a find module specifically for NumPy, you have to specify NumPy as a COMPONENT when you call find_package(Python2 ...). This way, you can use the imported target Python2::NumPy defined by the FindPython module to get the Numpy includes and library.
The call to target_link_libraries() must specify a target to link the libraries to. The only target defined in your CMake file is frvt_11_null_001, so that should be the first argument to target_link_libraries(). You should also prefer to use the target-specific variant of include_directories() so to not pollute the CMake directory scope with include directories.
With these fixes, your CMake can look something like this.
cmake_minimum_required(VERSION 2.8)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# Configure built shared libraries in top-level lib directory
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../lib)
find_package (Python2 COMPONENTS Interpreter NumPy)
# Build the shared libraries
add_library (frvt_11_null_001 SHARED nullimplfrvt11.cpp)
target_include_directories (frvt_11_null_001 PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../include
${CMAKE_CURRENT_SOURCE_DIR}/../../../common/src/include
)
target_link_libraries(frvt_11_null_001 PUBLIC Python2::NumPy)
This is how I did it:
find_package(Python3 3.7 COMPONENTS Interpreter NumPy REQUIRED)
include_directories(${PYTHON_INCLUDE_DIRS})
target_link_libraries(c__14 ${PYTHON_LIBRARIES} Python3::NumPy)
where 3.7 is your version and c__14 is the project name
You can use add_subdirectory() to add numpy library in project.
example add_subdirectory(your/library/path)

Why I cannot use boost correctly with CMake?

I will try to explain in detail the difficulties I encountered.
Recently I want to use boost libraries in Ubuntu Mint 18 x64. So I download the newest version of it which is 1.62.0. Sha256sum is fine. Then I started compiled it with "./bootstrap.sh" and "./b2". Finally copied then to "/usr/local" with "sudo ./b2 install". So far so good.
I am using Clion right now, so new job is to add libraries in Cmakelist.txt. Code below is what I have added.
set(BOOST_ROOT /usr/local)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.62.0)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(BoostTest main.cpp)
target_link_libraries(BoostTest ${Boost_LIBRARIES})
endif()
First thing I want to explain is that I know it is not good to set up a specific location in CMake, but without it Cmake cannot find it. So I just do that. And, at this moment, CMake did not post any error info. Here is my hello_world demo.
#include <iostream>
#include <boost/thread/testable_mutex.hpp>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
I just include a .hpp for test, nothing else. When I attemped to run it. I got these.
-- Boost version: 1.62.0
-- Configuring done
-- Generating done
-- Build files have been written to: /home/vita-nove/ClionProjects/BoostTest/cmake-build-debug
[ 50%] Building CXX object CMakeFiles/BoostTest.dir/main.cpp.o
[100%] Linking CXX executable BoostTest
CMakeFiles/BoostTest.dir/main.cpp.o: In function `__static_initialization_and_destruction_0(int, int)':
/usr/local/include/boost/system/error_code.hpp:221: undefined reference to `boost::system::generic_category()'
/usr/local/include/boost/system/error_code.hpp:222: undefined reference to `boost::system::generic_category()'
/usr/local/include/boost/system/error_code.hpp:223: undefined reference to `boost::system::system_category()'
collect2: error: ld returned 1 exit status
CMakeFiles/BoostTest.dir/build.make:94: recipe for target 'BoostTest' failed
make[3]: *** [BoostTest] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/BoostTest.dir/all' failed
make[2]: *** [CMakeFiles/BoostTest.dir/all] Error 2
CMakeFiles/Makefile2:79: recipe for target 'CMakeFiles/BoostTest.dir/rule' failed
make[1]: *** [CMakeFiles/BoostTest.dir/rule] Error 2
Makefile:118: recipe for target 'BoostTest' failed
make: *** [BoostTest] Error 2
Wired thing is that I do find some .hpp file in External Libraries. They are all located in /include/boost. In /usr/local/lib and /usr/local/include/boost everything shows up as I expect.
Sorry for my bad English. After spending nearly whole day searching and testing I cannot solve it. I don't know what dependencies I have missed or something else. Anyway, Thank you for reading this, hope someone will help.
You have to tell cmake that you need the thread library
set(BOOST_ROOT /usr/local)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.62.0 COMPONENTS thread system) # <-- here
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(BoostTest main.cpp)
target_link_libraries(BoostTest ${Boost_LIBRARIES})
endif()

Error while importing Boost 1.61.0 to C++ project

I tried to import Boost 1.61.0 (downloaded from SourceForge - Boost 1.61.0 as .7z), but failed.
Console:
"D:\Program Files (x86)\JetBrains\CLion 2016.2\bin\cmake\bin\cmake.exe" --build C:\Users\Marczak\.CLion2016.2\system\cmake\generated\WsServer-e351c9f9\e351c9f9\Debug --target WsServer -- -j 4
[ 50%] Linking CXX executable WsServer.exe
CMakeFiles\WsServer.dir\build.make:96: recipe for target 'WsServer.exe' failed
CMakeFiles\Makefile2:66: recipe for target 'CMakeFiles/WsServer.dir/all' failed
CMakeFiles\WsServer.dir/objects.a(main.cpp.obj): In function `_static_initialization_and_destruction_0':
C:/Users/Marczak/boost_1_61_0/boost/system/error_code.hpp:221: undefined reference to `boost::system::generic_category()'
C:/Users/Marczak/boost_1_61_0/boost/system/error_code.hpp:222: undefined reference to `boost::system::generic_category()'
C:/Users/Marczak/boost_1_61_0/boost/system/error_code.hpp:223: undefined reference to `boost::system::system_category()'
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe[3]: *** [WsServer.exe] Error 1
mingw32-make.exe[2]: *** [CMakeFiles/WsServer.dir/all] Error 2
mingw32-make.exe[1]: *** [CMakeFiles/WsServer.dir/rule] Error 2
CMakeFiles\Makefile2:78: recipe for target 'CMakeFiles/WsServer.dir/rule' failed
mingw32-make.exe: *** [WsServer] Error 2
Makefile:117: recipe for target 'WsServer' failed
CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(WsServer)
set(BOOST_ROOT "C:/Users/Marczak/boost_1_61_0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
set(SOURCE_FILES src/main.cpp)
find_package(Boost)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(WsServer ${SOURCE_FILES})
If I do find_package(Boost 1.61.0 COMPONENTS system filesystem REQUIRED) I get:
Error: Unable to find the requested Boost libraries.
Boost version: 1.61.0
Boost include path: C:/Users/Marczak/boost_1_61_0
Could not find the following static Boost libraries:
boost_system boost_filesystem
No Boost libraries were found. You may need to set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.
I tried to set Boost_USE_STATIC_LIBRARIES on, but it failed too. I use CLion 2016.2.
UPDATE: I tried older versions too. Same error. What's inside the .7z:
In other topics I see lib folder. But here I don't see it. What I should put in BOOST_LIBRARYDIR?
UPDATE 2: Installed binary from https://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/ . I noticed there's new folder: lib64-msvc-14.0. It contains many .dll and .lib files, e.g. boost_atomic-vc140-mt-1_61.dll.
Boost.org says:
If you plan to use your tools from the Windows command prompt, you're in the right place. If you plan to build from the Cygwin bash shell, you're actually running on a POSIX platform and should follow the instructions for getting started on Unix variants. Other command shells, such as MinGW's MSYS, are not supported—they may or may not work.
I'll try using Cygwin.
If you're new to C++, I suggest you to download MinGW distribution maintained by Stephan T. Lavavej (Microsoft C++ developer): https://nuwen.net/mingw.html. It, among other tools and libraries, contains pre-built boost binaries. Unpack it and specify the path to it via Settings | Build, Execution, Deployment | Toolchains.
After that you should be able to compile the program with the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(WsServer)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
set(SOURCE_FILES src/main.cpp)
find_package(Boost REQUIRED COMPONENTS filesystem)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(WsServer ${SOURCE_FILES})
target_link_libraries(WsServer ${Boost_LIBRARIES})
Don't forget to drop CMake cache as find_packages doesn't update successful results due to performance reasons (in CLion it can be done via Cmake toolbar | Cache | red arrows icon).
Some additional remarks:
Boost_USE_STATIC_LIBRARIES is not meant to be set manually, it is set by running find_package(Boost), which uses BOOST_ROOT or BOOST_INCLUDEDIR + BOOST_LIBRARYDIR, you should set those if required. You don't have to do it with the MinGW distro I've linked because it already has boost includes and libraries in accessible locations.
You can check that the paths to libraries are correct by looking at Boost_* variables in CMake cache.
libs directory inside boost sources is unrelated to the problem, it doesn't conitain any binaries
You've downloaded boost binaries built with Visual Studio toolchain, not MinGW, so they are incompatible with your setup. If you don't want to use MinGW package I've linked, you have to either find boost binaries built with correct MinGW version or build it yourself.

Create a CMakeFiles.txt file for newbies

I have a project with the following structure
/cmake_modules/
FindSFML.cmake
/includes/
car.hpp
motor.hpp
tires.hpp
/sources/
car.cpp
motor.cpp
tires.cpp
/main.cpp
/main.hpp
I have the following CMakeFiles.txt file:
cmake_minimum_required(VERSION 2.8)
project (MYGAME)
set (MYGAME_VERSION_MAJOR 1)
set (MYGAME_VERSION_MINOR 0)
set (EXECUTABLE_NAME "mygame")
include_directories ("${MYGAME_BINARY_DIR}")
include_directories ("${MYGAME_BINARY_DIR}/includes")
link_directories ("${MYGAME_BINARY_DIR}/sources")
add_executable(${EXECUTABLE_NAME} main.cpp)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
find_package(SFML 2.0 REQUIRED system window graphics network audio)
target_link_libraries(${EXECUTABLE_NAME} ${SFML_LIBRARIES})
When I try to execute make I get this:
[100%] Building CXX object CMakeFiles/mygame.dir/main.cpp.o
Linking CXX executable mygame
CMakeFiles/mygame.dir/main.cpp.o: In function `main':
main.cpp:(.text+0x11): undefined reference to `mynamespace::Car::Instance()'
main.cpp:(.text+0x21): undefined reference to `mynamespace::Car::start()'
collect2: error: ld returned 1 exit status
make[2]: *** [mygame] Error 1
make[1]: *** [CMakeFiles/mygame.dir/all] Error 2
make: *** [all] Error 2
How do I fix it?
You need to include the rest of your sources (car.cpp, motor.cpp and tires.cpp) in the build in some way.
You can either add them along with main.cpp in the executable directly:
set(MySources sources/car.cpp sources/motor.cpp sources/tires.cpp main.cpp)
add_executable(${EXECUTABLE_NAME} ${MySources})
or you can make these into a library and link that:
set(MyLibSources sources/car.cpp sources/motor.cpp sources/tires.cpp)
add_library(MyLib ${MyLibSources})
add_executable(${EXECUTABLE_NAME} main.cpp)
...
target_link_libraries(${EXECUTABLE_NAME} MyLib ${SFML_LIBRARIES})
A couple of other points to note:
You should avoid the use of link_directories if possible (its own documentation discourages its use), and it's often helpful to include the headers in the list of files added via add_executable or add_library since these then show up in IDEs like MS Visual Studio.