From 711a06163c8bc0eeaa8aad513aa05401d64d9e72 Mon Sep 17 00:00:00 2001 From: Vincent PALANCHER Date: Fri, 9 Jun 2023 18:17:23 +0000 Subject: [PATCH 1/2] build: Rewrites project build definition. #SW-159 This patch rewrites the definition of the build of libqi-python in order to remove the dependency to `qibuild` and `qi toolchains` and instead use more standard tools and conventions. It completely refactors the `CMakeLists.txt` files to use an approach closer to what is referred to as "modern CMake". The project now requires CMake 3.23 or greater. The project defines a single install components, the `Module` component. Tests are now declared to CMake using `add_test` and the CTest module is used. This means that tests can now be executed with the `ctest` command line tool. This patch also adds the support of Conan, to download the project dependencies and to create a Conan package of the project. Please refer to the README for basic usage. It also rewrites the Python module project definition using PEP517 and `scikit-build-core` as a build backend. --- .gitignore | 3 +- CMakeLists.txt | 430 ++++++++++++++++-- MANIFEST.in | 6 + README.rst | 234 +++------- cmake/BuildType.cmake | 16 + .../{utils.cmake => ParsePythonVersion.cmake} | 58 --- cmake/add_targets.cmake | 105 ----- cmake/add_tests.cmake | 124 ----- cmake/install_runtime_dependencies.cmake | 117 ----- cmake/native.py.in | 1 - cmake/set_dependencies.cmake | 306 ------------- cmake/set_globals.cmake | 50 -- cmake/set_install_rules.cmake | 7 - cmake/set_install_rules_standalone.cmake | 57 --- cmake/set_install_rules_system.cmake | 35 -- cmake/set_libqi_dependency.cmake | 53 --- cmake/set_libqi_dependency_standalone.cmake | 71 --- cmake/set_libqi_dependency_system.cmake | 42 -- conanfile.py | 97 ++++ pyproject.toml | 60 ++- qi/__init__.py | 8 - qi/_version.py | 1 - qi/_version.py.in | 1 + qi/native.py.in | 1 + qiproject.xml | 13 - qipython.pml | 4 - setup.py | 67 --- 27 files changed, 635 insertions(+), 1332 deletions(-) create mode 100644 cmake/BuildType.cmake rename cmake/{utils.cmake => ParsePythonVersion.cmake} (50%) delete mode 100644 cmake/add_targets.cmake delete mode 100644 cmake/add_tests.cmake delete mode 100644 cmake/install_runtime_dependencies.cmake delete mode 100644 cmake/native.py.in delete mode 100644 cmake/set_dependencies.cmake delete mode 100644 cmake/set_globals.cmake delete mode 100644 cmake/set_install_rules.cmake delete mode 100644 cmake/set_install_rules_standalone.cmake delete mode 100644 cmake/set_install_rules_system.cmake delete mode 100644 cmake/set_libqi_dependency.cmake delete mode 100644 cmake/set_libqi_dependency_standalone.cmake delete mode 100644 cmake/set_libqi_dependency_system.cmake create mode 100644 conanfile.py delete mode 100644 qi/_version.py create mode 100644 qi/_version.py.in create mode 100644 qi/native.py.in delete mode 100644 qiproject.xml delete mode 100644 qipython.pml delete mode 100755 setup.py diff --git a/.gitignore b/.gitignore index cb55908..136d222 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ build* _skbuild dist -*.egg-info \ No newline at end of file +*.egg-info +CMakeUserPresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 58f3efc..b2a5f22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,39 +1,405 @@ -cmake_minimum_required(VERSION 3.16) +## Copyright (c) 2023 Aldebaran Robotics. All rights reserved. +## Use of this source code is governed by a BSD-style license that can be +## found in the COPYING file. +## +## qi_python CMake project +## ======================= +## +## Parameters: +## ----------- +## BUILD_TESTING +## ~~~~~~~~~~~~~ +## If set to true, enables building the tests. See the documentation of the +## `CTest` module for more details on this variable. + +cmake_minimum_required(VERSION 3.23) if(ANDROID) message(FATAL_ERROR "This project does not support Android, stopping.") endif() -# Find our project CMake files in the dedicated subdirectory. -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - -include(utils) -include(set_globals) +include(cmake/ParsePythonVersion.cmake) -# Read the version of the library directly from the python file and sets it +# Read the version of the library directly from the `pyproject.toml` file and sets it # as the version of the project. -# -# The file is expected to contain a line `__version__ = `. -set(_version_file ${CMAKE_CURRENT_SOURCE_DIR}/qi/_version.py) -file(STRINGS ${_version_file} _version_strings REGEX version) -set(_version_regex "__version__[ \t]*=[ \t]*['\"]([^'\"]+)['\"]") -if(NOT _version_strings MATCHES "${_version_regex}") - message(FATAL_ERROR "Could not find the package version in file '${_version_file}'.") -endif() -parse_python_version(_version ${CMAKE_MATCH_1}) - -project(libqi-python VERSION ${_version_VERSION_RELEASE}) - -option(QIPYTHON_STANDALONE - "If set, builds and installs the module as a standalone, ready to be \ -packaged as a wheel. Otherwise, builds and installs it, ready to be included \ -as part of a system (such as NAOqi) that contains its dependencies." - FALSE) - -# Including this package creates the common options/flags/functions of qi projects. -find_package(qibuild) - -include(set_dependencies) -include(add_targets) -include(set_install_rules) -include(add_tests) +set(pyproject_file "${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml") +file(STRINGS ${pyproject_file} version_strings REGEX version) +set(version_regex "version[ \t]*=[ \t]*\"([^\"]+)\"") +if(NOT version_strings MATCHES "${version_regex}") + message(FATAL_ERROR "Could not find the package version in file '${pyproject_file}'.") +endif() +parse_python_version(QI_PYTHON "${CMAKE_MATCH_1}") + +project(qi_python VERSION "${QI_PYTHON_VERSION_RELEASE}") + +include(cmake/BuildType.cmake) + +# Enable testing with CTest. This defines the BUILD_TESTING option. +# Disable tests by default when cross compiling. +set(build_testing_default TRUE) +if(CMAKE_CROSSCOMPILING) + set(build_testing_default FALSE) +endif() +option(BUILD_TESTING "Build the testing tree." "${build_testing_default}") +include(CTest) + +if(MSVC) + add_compile_options(/W3) +else() + add_compile_options(-Wall -Wextra) +endif() + +# Output everything directly in predefined directories of the build tree. +# This is required by the SDK layout implementation. +# Also write a "path.conf" file, which is required for executing some tests. +set(sdk_dir "${CMAKE_BINARY_DIR}/sdk") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${sdk_dir}/bin") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${sdk_dir}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${sdk_dir}/lib") +set(path_conf_dir "${sdk_dir}/share/qi") +set(path_conf_file_path "${path_conf_dir}/path.conf") +if(NOT EXISTS "${path_conf_file_path}") + file(MAKE_DIRECTORY "${path_conf_dir}") + file(TOUCH "${path_conf_file_path}") +endif() + +############################################################################## +# Find Python +############################################################################## +# First search for Python with Interpreter+Devel components, to allow the module +# to look for the interpreter that goes with the Python development library. +# Then if it could not find both, try looking for the development component +# only, but this time force the module to find it or fail (REQUIRED). +# Some of our toolchains (when crosscompiling for instance) have the Python +# library but not an interpreter that can run on the host. +find_package(Python COMPONENTS Development Interpreter) +if(NOT Python_FOUND) + find_package(Python REQUIRED COMPONENTS Development) +endif() + +############################################################################## +# Find Boost +############################################################################## +find_package( + Boost REQUIRED + COMPONENTS thread +) + +############################################################################## +# Find Pybind11 +############################################################################## +find_package(pybind11 REQUIRED) + +############################################################################## +# Find LibQi +############################################################################## +find_package(qi REQUIRED) + +############################################################################## +# Convenience library: cxx_standard +############################################################################## +add_library(cxx_standard INTERFACE) + +# The project requires at least C++17. +target_compile_features( + cxx_standard + INTERFACE + cxx_std_17 +) + +############################################################################## +# Library: qi_python_objects +############################################################################## +add_library(qi_python_objects OBJECT) + +target_sources( + qi_python_objects + + PUBLIC + qipython/common.hpp + qipython/pyapplication.hpp + qipython/pyasync.hpp + qipython/pyclock.hpp + qipython/pyexport.hpp + qipython/pyfuture.hpp + qipython/pylog.hpp + qipython/pymodule.hpp + qipython/pyobject.hpp + qipython/pypath.hpp + qipython/pyproperty.hpp + qipython/pysession.hpp + qipython/pysignal.hpp + qipython/pyguard.hpp + qipython/pytranslator.hpp + qipython/pytypes.hpp + qipython/pystrand.hpp + + PRIVATE + src/pyapplication.cpp + src/pyasync.cpp + src/pyclock.cpp + src/pyexport.cpp + src/pyfuture.cpp + src/pylog.cpp + src/pymodule.cpp + src/pyobject.cpp + src/pypath.cpp + src/pyproperty.cpp + src/pysession.cpp + src/pysignal.cpp + src/pystrand.cpp + src/pytranslator.cpp + src/pytypes.cpp +) + +target_include_directories( + qi_python_objects + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" +) + +target_link_libraries( + qi_python_objects + PUBLIC + cxx_standard + Boost::headers + Boost::thread + pybind11::pybind11 + qi::qi +) + +set_target_properties( + qi_python_objects + PROPERTIES + CXX_EXTENSIONS TRUE + CXX_VISIBILITY_PRESET hidden + # Use PIC as the library is linked into a shared library in the qi_python + # target. + POSITION_INDEPENDENT_CODE TRUE +) + +############################################################################## +# Library: qi_python +############################################################################## +pybind11_add_module( + qi_python + # Disable generation of an extension for the shared library. + WITHOUT_SOABI +) + +# Generate a Python file containing the version. +set(version_py_file "${CMAKE_CURRENT_BINARY_DIR}/qi/_version.py") +configure_file(qi/_version.py.in "${version_py_file}" @ONLY) + +# Generate a Python file containing information about the native part of the +# module. +set(native_py_file "${CMAKE_CURRENT_BINARY_DIR}/qi/native.py") +if(qi_VERSION) + set(NATIVE_VERSION "${qi_VERSION}") +else() + set(NATIVE_VERSION "unknown") +endif() +configure_file(qi/native.py.in "${native_py_file}" @ONLY) + +set( + qi_python_py_files + qi/__init__.py + qi/logging.py + qi/path.py + qi/translator.py + qi/_binder.py + qi/_type.py +) +set( + qi_python_test_py_files + qi/test/__init__.py + qi/test/conftest.py + qi/test/test_applicationsession.py + qi/test/test_async.py + qi/test/test_call.py + qi/test/test_log.py + qi/test/test_module.py + qi/test/test_promise.py + qi/test/test_property.py + qi/test/test_return_empty_object.py + qi/test/test_session.py + qi/test/test_signal.py + qi/test/test_strand.py + qi/test/test_typespassing.py +) + +target_sources( + qi_python + PUBLIC + ${qi_python_py_files} + PRIVATE + src/module.cpp + ${qi_python_test_py_files} +) + +target_link_libraries( + qi_python + PRIVATE + cxx_standard + qi_python_objects +) + +set_target_properties( + qi_python + PROPERTIES + CXX_EXTENSIONS TRUE + CXX_VISIBILITY_PRESET hidden + LIBRARY_OUTPUT_DIRECTORY qi + RUNTIME_OUTPUT_DIRECTORY qi + INSTALL_RPATH_USE_LINK_PATH TRUE +) + +############################################################################## +# Library: qipython_module_plugin +############################################################################## +add_library(qimodule_python_plugin SHARED) +target_sources( + qimodule_python_plugin + PRIVATE + src/qimodule_python_plugin.cpp +) +target_link_libraries( + qimodule_python_plugin + PRIVATE + cxx_standard + qi::qi +) + +############################################################################## +# Installation +############################################################################## +install( + TARGETS qi_python + LIBRARY DESTINATION qi COMPONENT Module + # On Windows, shared libraries (DLL) are considered `RUNTIME`. + RUNTIME DESTINATION qi COMPONENT Module +) + +# Install Python files. +install( + FILES ${qi_python_py_files} "${version_py_file}" "${native_py_file}" + DESTINATION qi + COMPONENT Module +) + +############################################################################## +# Tests +############################################################################## +if(BUILD_TESTING) + find_package(GTest REQUIRED) + + # Use the CMake GoogleTest module that offers functions to automatically + # discover tests. + include(GoogleTest) + + find_package(qimodule REQUIRED) + qi_create_module(moduletest NO_INSTALL) + target_sources( + moduletest + PRIVATE + tests/moduletest.cpp + ) + target_link_libraries( + moduletest + PRIVATE + cxx_standard + ) + + add_executable(service_object_holder) + target_sources( + service_object_holder + PRIVATE + tests/service_object_holder.cpp + ) + target_link_libraries( + service_object_holder + PRIVATE + cxx_standard + qi::qi + ) + + add_executable(test_qipython) + target_sources( + test_qipython + PRIVATE + tests/common.hpp + tests/test_qipython.cpp + tests/test_guard.cpp + tests/test_types.cpp + tests/test_signal.cpp + tests/test_property.cpp + tests/test_object.cpp + tests/test_module.cpp + ) + target_link_libraries( + test_qipython + PRIVATE + qi_python_objects + cxx_standard + Python::Python + Boost::headers + pybind11::pybind11 + GTest::gmock + ) + gtest_discover_tests(test_qipython) + + add_executable(test_qipython_local_interpreter) + target_sources( + test_qipython_local_interpreter + PRIVATE + tests/test_qipython_local_interpreter.cpp + ) + target_link_libraries( + test_qipython_local_interpreter + PRIVATE + Python::Python + pybind11::pybind11 + qi_python_objects + cxx_standard + GTest::gmock + ) + gtest_discover_tests(test_qipython_local_interpreter) + + if(NOT Python_Interpreter_FOUND) + message(WARNING "tests: a compatible Python Interpreter was NOT found, Python tests are DISABLED.") + else() + message(STATUS "tests: a compatible Python Interpreter was found, Python tests are enabled.") + + execute_process( + COMMAND "${Python_EXECUTABLE}" -m pytest --version + OUTPUT_QUIET + ERROR_QUIET + RESULT_VARIABLE pytest_version_err + ) + if(pytest_version_err) + message(WARNING "tests: the pytest module does not seem to be installed for this Python Interpreter, " + "the execution of the tests will most likely result in a failure.") + endif() + + # Copy python files so that tests can be run in the build directory, with the generated files. + foreach(file IN LISTS qi_python_py_files qi_python_test_py_files) + configure_file("${file}" "${file}" COPYONLY) + endforeach() + + add_test( + NAME pytest + COMMAND + Python::Interpreter + -m pytest + "${CMAKE_CURRENT_BINARY_DIR}/qi/test" + --exec_path "$ --qi-standalone --qi-listen-url tcp://127.0.0.1:0" + ) + set_tests_properties( + pytest + PROPERTIES + ENVIRONMENT "QI_SDK_PREFIX=${sdk_dir}" + ) + endif() +else() + message(STATUS "tests: tests are disabled") +endif() diff --git a/MANIFEST.in b/MANIFEST.in index 9491f80..cff072a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,8 @@ include README.rst include COPYING +include CMakeLists.txt +graft cmake +graft qi +graft examples +graft qipython +graft src diff --git a/README.rst b/README.rst index 1b65663..2383130 100644 --- a/README.rst +++ b/README.rst @@ -10,193 +10,113 @@ __ LibQi_repo_ Building ======== -This project supports the building of a *standalone* package (for instance as a -wheel that can be uploaded on PyPi_) or of a "system" archive. +The libqi-python project requires a compiler that supports C++17 to build. -.. _standalone: +It is built with CMake >= 3.23. -Standalone (wheel) ------------------- +.. note:: + The CMake project offers several configuration options and exports a set + of targets when installed. You may refer to the ``CMakeLists.txt`` file + for more details about available parameters and exported targets. -This build mode is also referred to as the *standalone* mode. It is enabled by -passing ``-DQIPYTHON_STANDALONE=ON`` to the CMake call. The Python setup script -also sets this mode automatically when used. +.. note:: + The procedures described below assume that you have downloaded the project + sources and that your current working directory is the project sources root + directory. -In this mode, the project will build libqi and install all its dependencies as -part of the project. +Conan +^^^^^ -The package can be built from the ``setup.py`` script: +Additionally, libqi-python is available as a Conan 2 project, which means you +can use Conan to fetch dependencies. -.. code:: bash +You can install and/or upgrade Conan 2 and create a default profile in the +following way: - python3 ./setup.py bdist_wheel +.. code-block:: console -or + # install/upgrade Conan 2 + pip install --upgrade conan~=2 + # create a default profile + conan profile detect -.. code:: bash +Install dependencies from Conan and build with CMake +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - pip3 wheel . --no-use-pep517 +The procedure to build the project using Conan to fetch dependencies is the +following. -The use of PEP517 is not yet supported and still problematic with our setup -script, so we have to disable it. +You must first install the project dependencies in Conan. -The setup script uses scikit-build_ which is itself based on setuptools_. It -handles any option the latter can handle. Additionally, it can take CMake -arguments, which means that you can almost entirely customize how the native -part is built. +.. code-block:: console -In particular, you can use the ``CMAKE_TOOLCHAIN_FILE`` variable to specify a -toolchain to build the native part of the wheel (e.g. if you are using qi -toolchains): + conan install . --build=missing -s build_type=Debug -.. code:: bash +This will generate a build directory containing a configuration with a +toolchain file that allows CMake to find dependencies inside the Conan cache. - python3 ./setup.py bdist_wheel -DCMAKE_TOOLCHAIN_FILE=$HOME/.local/share/qi/toolchains/my_toolchain/toolchain-my_toolchain.cmake +You can then invoke CMake directly inside the build configuration directory to +configure and build the project. Fortunately, Conan also generates a CMake +preset that simplifies the process. The name of the preset may differ on +your machine. You may need to find the preset generated by Conan first by +calling: -System archive --------------- - -This build mode is also referred to as the "system" mode. This is the default -mode. - -In this mode, CMake will expect libqi to be available as a binary package. The -simplest way to build the package in this mode is to use qibuild, which will -first build libqi then libqi-python. - -.. code:: bash - - qibuild configure - qibuild make - -You can also set the ``QI_DIR`` variable at the CMake call to let it know it of -the location of the libqi package. - -.. code:: bash - - mkdir build && cd build - cmake .. -DQI_DIR=/path/to/libqi/install/dir - cmake --build . - - -Technical details ------------------ - -The compiled/native part of this project is based on CMake. It can be built as -any other project of the sort and also through qibuild_, although it requires at -least CMake v3.17. - -Our CMake scripts may take a few parameters: - -- ``QIPYTHON_STANDALONE``, when set, builds the library in *standalone* mode. - Refer to the standalone_ section for details. -- ``QIPYTHON_FORCE_STRIP``, when set, forces the build system to strip the - libqi-python native module library at install, resulting in a smaller binary. -- ``QI_WITH_TESTS``, when set, enables building of tests. This option is ignored - when cross-compiling. +.. code-block:: console -Dependencies -~~~~~~~~~~~~ + cmake --list-presets -The project has a few dependencies and the build system might report errors if -it fails to find them. It uses either ``FindXXX`` CMake modules through the -``find_package`` command or the ``FetchContent`` module for subprojects. Either way, -any parameter that these modules and the ``find_package`` command accept can be -used to customize how the build system finds the libraries or fetches the -subprojects. +Here, we'll assume that the preset is named `conan-linux-x86_64-gcc-debug`. +To start building, you need to configure with CMake and then build: -Most of the variables described here are defined in the -``cmake/set_dependencies.cmake`` file. You may refer to this file for more details -on these variables and their values. +.. code-block:: console -LibQi ->>>>> + cmake --preset conan-linux-x86_64-gcc-debug + cmake --build --preset conan-linux-x86_64-gcc-debug -The project's dependencies on LibQi depends on the building mode: +You can then invoke tests using CTest_: -- In **system mode**, it will expect to find it as a binary package. The location - of the binary package installation can be specified through the ``QI_DIR`` - variable. -- In **standalone mode**, it will download and compile it as a subproject through - the ``FetchContent`` CMake module. How it is downloaded can be customized - through the following variables: +.. code-block:: console - - ``LIBQI_VERSION`` - - ``LIBQI_GIT_REPOSITORY`` - - ``LIBQI_GIT_TAG`` + ctest --preset conan-linux-x86_64-gcc-debug --output-on-failure -It is possible to skip the download step and use an existing source directory by -setting its path as the ``FETCHCONTENT_SOURCE_DIR_LIBQI`` CMake variable. The -build system will still check that the version of the sources matches the -``LIBQI_VERSION`` value if it is set. +Finally, you can install the project in the directory of your choice. -Python ->>>>>> +The project defines a single install component, the ``Module`` component. -The build system uses the FindPython_ CMake module. It will try to honor the -following variables if they are set: +.. code-block:: console - - ``PYTHON_VERSION_STRING`` - - ``PYTHON_LIBRARY`` - - ``PYTHON_INCLUDE_DIR`` + # "cmake --install" does not support preset sadly. + cmake --install build/linux-x86_64-gcc-debug + --component Module --prefix ~/my-libqi-python-install -pybind11 ->>>>>>>> +Wheel (PEP 517) +--------------- -The build system will by default download and compile pybind11__ as a -subproject through the ``FetchContent`` CMake module. How it is downloaded can be -customized through the following variables: +You may build this project as a wheel package using PEP 517. -__ pybind11_repo_ +It uses a scikit-build_ backend which interfaces with CMake. - - ``PYBIND11_VERSION`` - - ``PYBIND11_GIT_REPOSITORY`` - - ``PYBIND11_GIT_TAG`` +You may need to provide a toolchain file so that CMake finds the required +dependencies, such as a toolchain generated by Conan: +.. code-block:: console -Boost ->>>>> + conan install . --build=missing -The build system will look for the Boost libraries on the system or in the -toolchain if one is set. The expected version of the libraries is specified as -the ``BOOST_VERSION`` variable. +You now can use the ``build`` Python module to build the wheel using PEP 517. -The build system uses the FindBoost_ CMake module. +.. code-block:: console -OpenSSL ->>>>>>> + export CMAKE_TOOLCHAIN_FILE=$PWD/build/linux-x86_64-gcc-release/generators/conan_toolchain.cmake + python -m build -The build system uses the FindOpenSSL_ CMake module. +When built that way, the native libraries present in the wheel are most likely incomplete. +You will need to use ``auditwheel`` or ``delocate`` to fix it. -ICU ->>> - -The build system uses the FindICU_ CMake module. - -GoogleTest ->>>>>>>>>> - -The build system will by default download and compile GoogleTest__ as a -subproject through the ``FetchContent`` CMake module. How it is downloaded can be -customized through the following variables: - -__ GoogleTest_repo_ - - - ``GOOGLETEST_VERSION`` - - ``GOOGLETEST_GIT_REPOSITORY`` - - ``GOOGLETEST_GIT_TAG`` - -Install -~~~~~~~ - -Once the project is configured, it can be built and installed as any CMake -project: - -.. code:: bash - - mkdir build && cd build - cmake .. -DCMAKE_INSTALL_PREFIX=/myinstallpath - cmake --install . +.. code-block:: console + auditwheel repair --plat manylinux_2_31_x86_64 dist/qi-*.whl + # The wheel will be by default placed in a `./wheelhouse/` directory. Crosscompiling -------------- @@ -207,26 +127,8 @@ path of the CMake file in your toolchain. __ CMake_toolchains_ -Testing -======= - -When enabled, tests can be executed with `CTest`_. - -.. code:: bash - - cd build - ctest . --output-on-failure - .. _LibQi_repo: https://github.com/aldebaran/libqi -.. _PyPi: https://pypi.org/ .. _scikit-build: https://scikit-build.readthedocs.io/en/latest/ .. _setuptools: https://setuptools.readthedocs.io/en/latest/setuptools.html -.. _qibuild: https://github.com/aldebaran/qibuild -.. _pybind11_repo: https://pybind11.readthedocs.io/en/latest/ -.. _FindPython: https://cmake.org/cmake/help/latest/module/FindPython.html -.. _FindBoost: https://cmake.org/cmake/help/latest/module/FindBoost.html -.. _FindOpenSSL: https://cmake.org/cmake/help/latest/module/FindOpenSSL.html -.. _FindICU: https://cmake.org/cmake/help/latest/module/FindICU.html -.. _GoogleTest_repo: https://github.com/google/googletest .. _CMake_toolchains: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html .. _CTest: https://cmake.org/cmake/help/latest/manual/ctest.1.html diff --git a/cmake/BuildType.cmake b/cmake/BuildType.cmake new file mode 100644 index 0000000..7e31d51 --- /dev/null +++ b/cmake/BuildType.cmake @@ -0,0 +1,16 @@ +# Inspired by https://www.kitware.com/cmake-and-the-default-build-type/. +# +# Set a default build type if none was specified. +set(default_build_type "Release") +if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + set(default_build_type "Debug") +endif() + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE + STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() diff --git a/cmake/utils.cmake b/cmake/ParsePythonVersion.cmake similarity index 50% rename from cmake/utils.cmake rename to cmake/ParsePythonVersion.cmake index 5db32ab..4f38d1a 100644 --- a/cmake/utils.cmake +++ b/cmake/ParsePythonVersion.cmake @@ -1,5 +1,3 @@ -include_guard(GLOBAL) - # Parses a Python version as defined in PEP440 # (see https://www.python.org/dev/peps/pep-0440/) in some input and sets the # following variables accordingly: @@ -48,59 +46,3 @@ function(parse_python_version prefix input) set(${prefix}_VERSION_RELEASE_MAJOR_MINOR "${CMAKE_MATCH_1}${CMAKE_MATCH_2}" PARENT_SCOPE) set(${prefix}_VERSION_RELEASE_PATCH "${CMAKE_MATCH_5}" PARENT_SCOPE) endfunction() - -# Enables most compiler warnings for a target. -function(enable_warnings target) - if(CMAKE_COMPILER_IS_GNUCC) - target_compile_options(${target} PRIVATE -Wall -Wextra) - elseif(MSVC) - target_compile_options(${target} PRIVATE /W4) - endif() -endfunction() - -# Sets the install RPATH of some targets to a list of paths relative to the -# location of the binary. -function(set_install_rpath) - cmake_parse_arguments(SET_INSTALL_RPATH "ORIGIN" "" "TARGETS;PATHS_REL_TO_ORIGIN" ${ARGN}) - foreach(_target IN LISTS SET_INSTALL_RPATH_TARGETS) - set(_paths) - if(SET_INSTALL_RPATH_ORIGIN) - message(VERBOSE "Adding $ORIGIN to RPATH of ${_target}") - list(APPEND _paths - "$<$:$ORIGIN>$<$:@loader_path>") - endif() - foreach(_path IN LISTS SET_INSTALL_RPATH_PATHS_REL_TO_ORIGIN) - if(NOT _path) - continue() - endif() - message(VERBOSE "Adding $ORIGIN/${_path} to RPATH of ${_target}") - list(APPEND _paths - "$<$:$ORIGIN/${_path}>$<$:@loader_path/${_path}>") - endforeach() - if(_paths) - message(VERBOSE "Setting ${_target} RPATH to ${_paths}") - set_property(TARGET ${_target} PROPERTY INSTALL_RPATH ${_paths}) - endif() - endforeach() -endfunction() - -# Creates a variable that can be overridden by the user from either the -# command-line, the cache or the environment. -# The order of preference is: -# - the value from the variable in cache (or the command-line, since setting -# a variable from the command-line automatically adds it to the cache). -# - the value from the variable in the environment. -# - the default value for the variable. -function(overridable_variable name default_value) - # The variable already exists in the cache. It's the preferred value, so we - # don't change it. - if(DEFINED CACHE{${name}}) - return() - endif() - - set(value ${default_value}) - if(DEFINED ENV{${name}}) - set(value $ENV{${name}}) - endif() - set(${name} ${value} PARENT_SCOPE) -endfunction() diff --git a/cmake/add_targets.cmake b/cmake/add_targets.cmake deleted file mode 100644 index 5f91d52..0000000 --- a/cmake/add_targets.cmake +++ /dev/null @@ -1,105 +0,0 @@ -include_guard(GLOBAL) - - -add_library(qi_python_objects OBJECT) - -target_sources(qi_python_objects - PUBLIC - qipython/common.hpp - qipython/pyapplication.hpp - qipython/pyasync.hpp - qipython/pyclock.hpp - qipython/pyexport.hpp - qipython/pyfuture.hpp - qipython/pylog.hpp - qipython/pymodule.hpp - qipython/pyobject.hpp - qipython/pypath.hpp - qipython/pyproperty.hpp - qipython/pysession.hpp - qipython/pysignal.hpp - qipython/pyguard.hpp - qipython/pytranslator.hpp - qipython/pytypes.hpp - qipython/pystrand.hpp - - PRIVATE - src/pyapplication.cpp - src/pyasync.cpp - src/pyclock.cpp - src/pyexport.cpp - src/pyfuture.cpp - src/pylog.cpp - src/pymodule.cpp - src/pyobject.cpp - src/pypath.cpp - src/pyproperty.cpp - src/pysession.cpp - src/pysignal.cpp - src/pystrand.cpp - src/pytranslator.cpp - src/pytypes.cpp -) - -enable_warnings(qi_python_objects) - -target_include_directories(qi_python_objects PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(qi_python_objects - PUBLIC cxx17 - pybind11 - qi.interface) - -set_target_properties(qi_python_objects - # Use PIC as the library is linked into a shared library in the qi_python - # target. - PROPERTIES POSITION_INDEPENDENT_CODE TRUE) - - -Python_add_library(qi_python MODULE) - -set(QIPYTHON_PYTHON_MODULE_FILES - qi/__init__.py - qi/logging.py - qi/path.py - qi/translator.py - qi/_binder.py - qi/_type.py - qi/_version.py) - -target_sources(qi_python - PRIVATE src/module.cpp - ${QIPYTHON_PYTHON_MODULE_FILES} - qi/test/__init__.py - qi/test/conftest.py - qi/test/test_applicationsession.py - qi/test/test_async.py - qi/test/test_call.py - qi/test/test_log.py - qi/test/test_module.py - qi/test/test_promise.py - qi/test/test_property.py - qi/test/test_return_empty_object.py - qi/test/test_session.py - qi/test/test_signal.py - qi/test/test_strand.py - qi/test/test_typespassing.py - setup.py - pyproject.toml - README.rst) - -enable_warnings(qi_python) - -target_link_libraries(qi_python PRIVATE cxx17 qi_python_objects) - -set_target_properties(qi_python - PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${QIPYTHON_PYTHON_MODULE_NAME} - RUNTIME_OUTPUT_DIRECTORY ${QIPYTHON_PYTHON_MODULE_NAME}) - -set_build_rpath_to_qipython_dependencies(qi_python) - -if(NOT QIPYTHON_STANDALONE) - add_library(qimodule_python_plugin SHARED) - target_sources(qimodule_python_plugin PRIVATE src/qimodule_python_plugin.cpp) - target_link_libraries(qimodule_python_plugin PRIVATE qi.interface) -endif() diff --git a/cmake/add_tests.cmake b/cmake/add_tests.cmake deleted file mode 100644 index 8408498..0000000 --- a/cmake/add_tests.cmake +++ /dev/null @@ -1,124 +0,0 @@ -include_guard(GLOBAL) - -if(NOT QI_WITH_TESTS) - message(STATUS "tests: tests are disabled") - return() -endif() - -if(CMAKE_CROSSCOMPILING) - message(STATUS "tests: crosscompiling, tests are disabled") - return() -endif() - -enable_testing() - -# Use the CMake GoogleTest module that offers functions to automatically discover -# tests. -include(GoogleTest) - - -find_package(qimodule REQUIRED HINTS ${libqi_SOURCE_DIR}) -qi_create_module(moduletest NO_INSTALL) -target_sources(moduletest PRIVATE tests/moduletest.cpp) -target_link_libraries(moduletest cxx17 qi.interface) -enable_warnings(moduletest) -set_build_rpath_to_qipython_dependencies(moduletest) - - -add_executable(service_object_holder) -target_sources(service_object_holder PRIVATE tests/service_object_holder.cpp) -target_link_libraries(service_object_holder PRIVATE cxx17 qi.interface) -enable_warnings(service_object_holder) -set_build_rpath_to_qipython_dependencies(service_object_holder) - - -add_executable(test_qipython) -target_sources(test_qipython - PRIVATE tests/common.hpp - tests/test_qipython.cpp - tests/test_guard.cpp - tests/test_types.cpp - tests/test_signal.cpp - tests/test_property.cpp - tests/test_object.cpp - tests/test_module.cpp) -target_link_libraries(test_qipython - PRIVATE Python::Python - pybind11 - qi_python_objects - qi.interface - cxx17 - gmock) - -add_executable(test_qipython_local_interpreter) -target_sources(test_qipython_local_interpreter - PRIVATE tests/test_qipython_local_interpreter.cpp) -target_link_libraries(test_qipython_local_interpreter - PRIVATE Python::Python - pybind11 - qi_python_objects - qi.interface - cxx17 - gmock) - -set(_sdk_prefix "${CMAKE_BINARY_DIR}/sdk") -foreach(test_target IN ITEMS test_qipython - test_qipython_local_interpreter) - # Unfortunately, in some of our toolchains, gtest/gmock headers are found in the qi-framework - # package, which comes first in the include paths order given to the compiler. This causes the - # compiler to use those headers instead of the ones we got from a `FetchContent` of the googletest - # repository. - target_include_directories(${test_target} - BEFORE # Force this path to come first in the list of include paths. - PRIVATE $) - enable_warnings(${test_target}) - set_build_rpath_to_qipython_dependencies(${test_target}) - gtest_discover_tests(${test_target} EXTRA_ARGS --qi-sdk-prefix ${_sdk_prefix}) -endforeach() - -if(NOT Python_Interpreter_FOUND) - message(WARNING "tests: a compatible Python Interpreter was NOT found, Python tests are DISABLED.") -else() - message(STATUS "tests: a compatible Python Interpreter was found, Python tests are enabled.") - - # qibuild sets these variables which tends to mess up our calls of the Python interpreter, so - # we reset them preemptively. - set(ENV{PYTHONHOME}) - set(ENV{PYTHONPATH}) - - execute_process(COMMAND "${Python_EXECUTABLE}" -m pytest --version - OUTPUT_QUIET ERROR_QUIET - RESULT_VARIABLE pytest_version_err) - if(pytest_version_err) - message(WARNING "tests: the pytest module does not seem to be installed for this Python Interpreter\ -, the execution of the tests will most likely result in a failure.") - endif() - - macro(copy_py_files dir) - file(GLOB _files RELATIVE "${PROJECT_SOURCE_DIR}" CONFIGURE_DEPENDS "${dir}/*.py") - foreach(_file IN LISTS _files) - set_property(DIRECTORY APPEND PROPERTY - CMAKE_CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/${_file}") - file(COPY "${_file}" DESTINATION "${dir}") - endforeach() - endmacro() - - copy_py_files(qi) - copy_py_files(qi/test) - - add_test(NAME pytest - COMMAND Python::Interpreter -m pytest qi/test - --exec_path - "$ --qi-standalone --qi-listen-url tcp://127.0.0.1:0") - - get_filename_component(_ssl_dir "${OPENSSL_SSL_LIBRARY}" DIRECTORY) - set(_pytest_env - "QI_SDK_PREFIX=${_sdk_prefix}" - "LD_LIBRARY_PATH=${_ssl_dir}") - set_tests_properties(pytest PROPERTIES ENVIRONMENT "${_pytest_env}") -endif() - -# Ensure compatibility with qitest by simply running ctest. Timeout is set to 4 minutes. -file(WRITE - "${CMAKE_BINARY_DIR}/qitest.cmake" "--name;run_ctest;--timeout;240;--working-directory;\ -${CMAKE_CURRENT_BINARY_DIR};--env;PYTHONHOME=;--env;PYTHONPATH=;--;${CMAKE_CTEST_COMMAND};-V") diff --git a/cmake/install_runtime_dependencies.cmake b/cmake/install_runtime_dependencies.cmake deleted file mode 100644 index ae6f787..0000000 --- a/cmake/install_runtime_dependencies.cmake +++ /dev/null @@ -1,117 +0,0 @@ -set(_module_install_dir - "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${QIPYTHON_PYTHON_MODULE_NAME}") - -# On Windows, shared libraries (DLL) are not in the `lib` subdirectory of -# the dependencies, but in the `bin` directory. So for each dependency directory -# that ends with `/lib`, we add the corresponding `/bin` directory. -set(_runtime_dependencies_dirs ${QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS}) -if(WIN32) - set(_dep_dirs ${_runtime_dependencies_dirs}) - foreach(_dep_dir IN LISTS _dep_dirs) - if(_dep_dir MATCHES "/lib$") - get_filename_component(_full_dep_bin_dir "${_dep_dir}/../bin" ABSOLUTE) - list(APPEND _runtime_dependencies_dirs "${_full_dep_bin_dir}") - endif() - endforeach() -endif() - -# Install the runtime dependencies of the module, eventually by copying the -# libraries from the toolchain. -message(STATUS "Looking for dependencies of qi_python and qi in: ${_runtime_dependencies_dirs}") -file(GET_RUNTIME_DEPENDENCIES - LIBRARIES "${QIPYTHON_QI_TARGET_FILE}" - MODULES "${QIPYTHON_QI_PYTHON_TARGET_FILE}" - RESOLVED_DEPENDENCIES_VAR _runtime_dependencies - UNRESOLVED_DEPENDENCIES_VAR _unresolved_runtime_dependencies - CONFLICTING_DEPENDENCIES_PREFIX _conflicting_runtime_dependencies - # eay32 is a library that comes with OpenSSL. - PRE_INCLUDE_REGEXES ssl eay crypto icu boost - PRE_EXCLUDE_REGEXES .* - DIRECTORIES ${_runtime_dependencies_dirs}) - -# Processing potential conflicts in dependencies: if there are multiple -# available choices for one of the runtime dependencies, we prefer one -# that is in the toolchain. Otherwise we just fallback to the first -# available path for that dependency. -if(_conflicting_runtime_dependencies_FILENAMES) - message(STATUS "Some conflicts were found for dependencies of qi_python and qi: ${_conflicting_runtime_dependencies_FILENAMES}") - foreach(_filename IN LISTS _conflicting_runtime_dependencies_FILENAMES) - message(STATUS "Conflicts of dependency '${_filename}': ${_conflicting_runtime_dependencies_${_filename}}") - set(_dep) - foreach(_path IN LISTS _conflicting_runtime_dependencies_${_filename}) - if(QIPYTHON_TOOLCHAIN_DIR AND _path MATCHES "^${QIPYTHON_TOOLCHAIN_DIR}") - set(_dep "${_path}") - message(STATUS "Using file '${_dep}' for dependency '${_filename}' as it is in the toolchain.") - break() - endif() - endforeach() - if(NOT _dep) - list(GET _conflicting_runtime_dependencies_${_filename} 0 _dep) - message(STATUS "Fallback to the first available path '${_dep}' for dependency '${_filename}'.") - endif() - list(APPEND _runtime_dependencies "${_dep}") - endforeach() -endif() - -message(STATUS "The dependencies of qi_python and qi are: ${_runtime_dependencies}") -if(_unresolved_runtime_dependencies) - message(STATUS "Some dependencies of qi_python and qi are unresolved: ${_unresolved_runtime_dependencies}") -endif() - -if(UNIX OR APPLE) - find_program(_patchelf "patchelf") - if(_patchelf) - if(APPLE) - set(_patchelf_runtime_path "@loader_path") - elseif(UNIX) - set(_patchelf_runtime_path "$ORIGIN") - endif() - else() - message(WARNING "The target system seems to be using ELF binaries, but the `patchelf` \ -tool could not be found. The installed runtime dependencies might not have their runtime path \ -set correctly to run. You might want to install `patchelf` on your system.") - endif() -endif() - -foreach(_needed_dep IN LISTS _runtime_dependencies) - set(_dep "${_needed_dep}") - - # Some of the dependency libraries are symbolic links, and `file(INSTALL)` will - # copy the symbolic link instead of their target (the real library) by default. - # There is an option FOLLOW_SYMLINK_CHAIN that will copy both the library and - # the symlink, but then when we create an archive (a wheel for instance), - # symlinks are replaced by the file they point to. This results in libraries - # being copied multiple times in the same archive with different names. - # - # Therefore we have to detect symlink manually and copy the files ourselves. - while(IS_SYMLINK "${_dep}") - set(_symlink "${_dep}") - file(READ_SYMLINK "${_symlink}" _dep) - if(NOT IS_ABSOLUTE "${_dep}") - get_filename_component(_dir "${_symlink}" DIRECTORY) - set(_dep "${_dir}/${_dep}") - endif() - message(STATUS "Dependency ${_symlink} is a symbolic link, resolved to ${_dep}") - endwhile() - - file(INSTALL "${_dep}" DESTINATION "${_module_install_dir}") - get_filename_component(_dep_filename "${_dep}" NAME) - set(_installed_dep "${_module_install_dir}/${_dep_filename}") - - if(_patchelf) - message(STATUS "Set runtime path of runtime dependency \"${_installed_dep}\" to \"${_patchelf_runtime_path}\"") - file(TO_NATIVE_PATH "${_installed_dep}" _native_installed_dep) - execute_process(COMMAND "${_patchelf}" - # Sets the RPATH/RUNPATH or may replace the RPATH by a RUNPATH, depending on the platform. - --set-rpath "${_patchelf_runtime_path}" "${_native_installed_dep}") - endif() - - if(NOT "${_dep}" STREQUAL "${_needed_dep}") - get_filename_component(_needed_dep_filename "${_needed_dep}" NAME) - set(_installed_needed_dep "${_module_install_dir}/${_needed_dep_filename}") - message(STATUS "Renaming ${_installed_dep} into ${_installed_needed_dep}") - file(RENAME "${_installed_dep}" "${_installed_needed_dep}") - list(TRANSFORM CMAKE_INSTALL_MANIFEST_FILES - REPLACE "${_installed_dep}" "${_installed_needed_dep}") - endif() -endforeach() diff --git a/cmake/native.py.in b/cmake/native.py.in deleted file mode 100644 index 351d698..0000000 --- a/cmake/native.py.in +++ /dev/null @@ -1 +0,0 @@ -__version__ = "@NATIVE_VERSION@" \ No newline at end of file diff --git a/cmake/set_dependencies.cmake b/cmake/set_dependencies.cmake deleted file mode 100644 index 46dc882..0000000 --- a/cmake/set_dependencies.cmake +++ /dev/null @@ -1,306 +0,0 @@ -# This file finds the dependencies required by the project and creates the -# needed targets. -# -# It can only be included after a call to `project` and only once. -# -# After using CMake standard `find_package` modules, we often set the following -# variables (with the name of a dependency): -# - _PACKAGE_FOUND -# - _LIBRARIES -# - _INCLUDE_DIRS -# -# These variables are the ones the qibuild CMake part uses in its internal -# functions and `find_package` modules (such as `qi_use_lib`). This allows us -# to use CMake standard modules and bypass qibuild own search procedures. -# -# Once this file is loaded, the following targets are expected to be defined: -# -# - Threads::Threads, the thread library. -# - OpenSSL::SSL, aka libssl. -# - OpenSSL::Crypto, aka libcrypto. -# - Python::Interpreter, the Python interpreter, if found. -# - Python::Python, the Python library for embedding the interpreter. -# - Python::Module, the Python library for modules. -# - python_headers, header-only library for Python. -# - Boost::headers, the target for all the header-only libraries. -# - Boost::atomic, aka libboost_atomic. -# - Boost::date_time, aka libboost_date_time. -# - Boost::thread, aka libboost_thread. -# - Boost::chrono, aka libboost_chrono. -# - Boost::filesystem, aka libboost_filesystem. -# - Boost::locale, aka libboost_locale. -# - Boost::regex, aka libboost_regex. -# - Boost::program_options, aka libboost_program_options. -# - Boost::random, aka libboost_random. -# - pybind11, a header-only Python binding library. -# - gtest & gtest_main, the googletest library. -# - gmock & gmock_main, the googlemock library. -# - qi.interface, the high level target for linking with libqi. -# -# When needed, the following targets can also be defined: -# - ICU::i18n, aka libicui18n. -# - ICU::data, aka libicudata. -# - ICU::uc, aka libicuuc. - -include_guard(GLOBAL) - -# Version of Boost to use. It will be used as the argument to the `find_package(Boost)` call. -overridable_variable(BOOST_VERSION 1.77) - -# Version of pybind11 to use. -overridable_variable(PYBIND11_VERSION 2.9.0) - -# URL of the git repository from which to download pybind11. For more details, see CMake -# `ExternalProject` module documentation of the `GIT_REPOSITORY` argument. -overridable_variable(PYBIND11_GIT_REPOSITORY https://github.com/pybind/pybind11) - -# Git branch name, tag or commit hash to checkout for pybind11. For more details, see CMake -# `ExternalProject` module documentation of the `GIT_TAG` argument. -overridable_variable(PYBIND11_GIT_TAG v${PYBIND11_VERSION}) - -# URL of the git repository from which to download googletest. For more details, see CMake -# `ExternalProject` module documentation of the `GIT_REPOSITORY` argument. -overridable_variable(GOOGLETEST_GIT_REPOSITORY https://github.com/google/googletest.git) - -# Git branch name, tag or commit hash to checkout for googletest. For more details, see CMake -# `ExternalProject` module documentation of the `GIT_TAG` argument. -overridable_variable(GOOGLETEST_GIT_TAG main) - -set(PYTHON_VERSION_STRING "" CACHE STRING "Version of Python to look for. This \ -variable can be specified by tools run directly from Python to enforce the \ -use of the same version.") -mark_as_advanced(PYTHON_VERSION_STRING) - -if(NOT COMMAND FetchContent_Declare) - include(FetchContent) -endif() - -# This part is a bit tricky. Some of our internal toolchains tend to confuse the -# CMake standard `find_package` modules, so we try to use some heuristics to -# set hints for these modules. -# -# If we're using a toolchain, it's most likely either one of our internal -# desktop or Yocto toolchain. This should cover most of our building cases, but -# not all of them, especially for users trying to build the library outside of -# the organization. For these, we let them manually specify the needed -# dependencies path. -if(CMAKE_TOOLCHAIN_FILE) - message(VERBOSE "Toolchain file is set.") - get_filename_component(QIPYTHON_TOOLCHAIN_DIR "${CMAKE_TOOLCHAIN_FILE}" DIRECTORY) - get_filename_component(QIPYTHON_TOOLCHAIN_DIR "${QIPYTHON_TOOLCHAIN_DIR}" ABSOLUTE) - message(VERBOSE "Toolchain directory is ${QIPYTHON_TOOLCHAIN_DIR}.") - - # Cross-compiling for Yocto - if(YOCTO_SDK_TARGET_SYSROOT) - message(VERBOSE "Yocto cross compilation detected.") - message(VERBOSE "Yocto target sysroot is '${YOCTO_SDK_TARGET_SYSROOT}'.") - set(_yocto_target_sysroot_usr "${YOCTO_SDK_TARGET_SYSROOT}/usr") - set(_openssl_root ${_yocto_target_sysroot_usr}) - set(_icu_root ${_yocto_target_sysroot_usr}) - set(_boost_root ${_yocto_target_sysroot_usr}) - set(_python_root ${_yocto_target_sysroot_usr}) - - # Probably compiling for desktop - else() - message(VERBOSE "Assuming desktop compilation.") - set(_openssl_root "${QIPYTHON_TOOLCHAIN_DIR}/openssl") - set(_icu_root "${QIPYTHON_TOOLCHAIN_DIR}/icu") - set(_boost_root "${QIPYTHON_TOOLCHAIN_DIR}/boost") - # No definition of _python_root for desktop, we try to use the one - # installed on the system. - endif() - - # These variables are recognized by the associated CMake standard - # `find_package` modules (see their specific documentation for details). - # - # Setting them as cache variable enables user customisation. - set(OPENSSL_ROOT_DIR ${_openssl_root} CACHE PATH "root directory of an OpenSSL installation") - set(ICU_ROOT ${_icu_root} CACHE PATH "the root of the ICU installation") - set(BOOST_ROOT ${_boost_root} CACHE PATH "Boost preferred installation prefix") - - if(_python_root AND EXISTS "${_python_root}") - # The `qibuild` `python3-config.cmake` module must be used to find the - # dependency as it is packaged in our internal toolchains. - # The CMake standard `find_package` module is named `Python3`, looking for - # `PYTHON3` forces CMake to use the module that comes with `qibuild`. - find_package(PYTHON3 - REQUIRED CONFIG - NAMES PYTHON3 Python3 python3 - HINTS ${_python_root}) - - if(PYTHON3_FOUND OR PYTHON3_PACKAGE_FOUND) - # We want to set the `Python_LIBRARY` variable that the FindPython CMake module - # recognizes, in order to instruct it where to find the library (the FindPython - # module search mechanism uses the python-config utility which is not packaged - # well enough in our toolchains for it to work, therefore we have to manually - # tell the module where the library is). - # - # The `qibuild` Python3 module fills the PYTHON3_LIBRARIES variable with the - # format "general;;debug;", which is recognized by - # `target_link_libraries` (see the function documentation for - # details). However, the FindPython CMake module expects an absolute path. - # - # Therefore we create an interface library `Python_interface`, we use - # `target_link_libraries` so that the specific format is parsed, and then - # we get the full path of the library from the `LINK_LIBRARIES` property of - # the target. - add_library(Python_interface INTERFACE) - target_link_libraries(Python_interface INTERFACE ${PYTHON3_LIBRARIES}) - get_target_property(Python_LIBRARY Python_interface INTERFACE_LINK_LIBRARIES) - list(GET PYTHON3_INCLUDE_DIRS 0 Python_INCLUDE_DIR) - endif() - endif() -endif() - - -# C++17 -add_library(cxx17 INTERFACE) -target_compile_features(cxx17 INTERFACE cxx_std_17) - - -# Threads -find_package(Threads REQUIRED) -if(CMAKE_USE_PTHREADS_INIT) - set(PTHREAD_PACKAGE_FOUND TRUE) - set(PTHREAD_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) - set(PTHREAD_INCLUDE_DIRS) -endif() - - -# OpenSSL -find_package(OpenSSL REQUIRED) - - -# Python -parse_python_version(_python_version "${PYTHON_VERSION_STRING}") -if(_python_version_VERSION_STRING) - set(_python_version ${_python_version_VERSION_RELEASE_MAJOR_MINOR}) -else() - set(_python_version 3.5) # default to Python 3.5+. -endif() - -# Set variables that the CMake module recognizes from variables that the -# scikit-build build system module may pass to us. -if(PYTHON_LIBRARY) - set(Python_LIBRARY "${PYTHON_LIBRARY}") -endif() -if(PYTHON_INCLUDE_DIR) - set(Python_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}") -endif() - -# First search for Python with Interpreter+Devel components, to allow the module -# to look for the interpreter that goes with the Python development library. -# Then if it could not find both, try looking for the development component -# only, but this time force the module to find it or fail (REQUIRED). -# Some of our toolchains (when crosscompiling for instance) have the Python -# library but not an interpreter that can run on the host. -find_package(Python ${_python_version} COMPONENTS Development Interpreter) -if(NOT Python_FOUND) - find_package(Python ${_python_version} REQUIRED COMPONENTS Development) -endif() - -list(GET Python_LIBRARIES 0 _py_lib) -add_library(python_headers INTERFACE) -target_include_directories(python_headers INTERFACE ${Python_INCLUDE_DIRS}) -if(_py_lib MATCHES "python([23])\\.([0-9]+)([mu]*d[mu]*)") - target_compile_definitions(python_headers INTERFACE Py_DEBUG) -endif() - - -# Boost -set(_boost_comps atomic date_time thread chrono filesystem locale regex - program_options random) -find_package(Boost ${BOOST_VERSION} EXACT REQUIRED COMPONENTS ${_boost_comps}) - -set(BOOST_PACKAGE_FOUND TRUE) -set(BOOST_LIBRARIES ${Boost_LIBRARIES}) -set(BOOST_INCLUDE_DIRS ${Boost_INCLUDE_DIRS}) -# Also set the "qibuild variables" for each of the components of boost. -foreach(_comp IN LISTS _boost_comps) - string(TOUPPER ${_comp} _uc_comp) - set(BOOST_${_uc_comp}_PACKAGE_FOUND TRUE) - set(BOOST_${_uc_comp}_LIBRARIES ${Boost_${_uc_comp}_LIBRARY}) - set(BOOST_${_uc_comp}_INCLUDE_DIRS ${Boost_INCLUDE_DIRS}) -endforeach() - - -# ICU. These are the components that might be needed by Boost::locale. -find_package(ICU COMPONENTS i18n uc data) -set(ICU_PACKAGE_FOUND ${ICU_FOUND}) -if(ICU_FOUND) - target_link_libraries(Boost::locale INTERFACE ICU::i18n ICU::data ICU::uc) -endif() - - -# pybind11, which is a header-only library. -# It's simpler to just use it as a CMake external project (through FetchContent) -# and download it directly into the build directory, than to try to use a -# preinstalled version through `find_package` -FetchContent_Declare(pybind11 - GIT_REPOSITORY ${PYBIND11_GIT_REPOSITORY} - GIT_TAG ${PYBIND11_GIT_TAG}) -FetchContent_GetProperties(pybind11) -if(NOT pybind11_POPULATED) - FetchContent_Populate(pybind11) - # pybind11 build system and targets do a lot of stuff automatically (such as - # looking for Python libs, adding the C++11 flag, ...). We prefer to do things - # ourselves, so we recreate a target directly instead of adding it as a - # subproject. - add_library(pybind11 INTERFACE) - target_include_directories(pybind11 INTERFACE ${pybind11_SOURCE_DIR}/include) - target_link_libraries(pybind11 INTERFACE cxx17 python_headers) -endif() - - -# googletest -# -# This is the simplest way to depend on googletest & googlemock. As they are -# fairly quick to compile, we can have it as a subproject (as a `FetchContent` -# project). -FetchContent_Declare(googletest - GIT_REPOSITORY ${GOOGLETEST_GIT_REPOSITORY} - GIT_TAG ${GOOGLETEST_GIT_TAG}) -FetchContent_GetProperties(googletest) -if(NOT googletest_POPULATED) - FetchContent_Populate(googletest) - set(INSTALL_GTEST OFF) - add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) -endif() - -set(GTEST_PACKAGE_FOUND TRUE) -set(GMOCK_PACKAGE_FOUND TRUE) -set(GTEST_LIBRARIES $) -set(GMOCK_LIBRARIES $) - -# Unfortunately, we cannot get the `INTERFACE_INCLUDE_DIRECTORIES` of the gtest -# or gmock targets as they might contain generator expressions containing ";" -# characters, which qibuild wrongly splits as elements of a list. Instead, we -# have to build the paths manually. -set(GTEST_INCLUDE_DIRS ${googletest_SOURCE_DIR}/googletest/include) -set(GMOCK_INCLUDE_DIRS ${GTEST_INCLUDE_DIRS} - ${googletest_SOURCE_DIR}/googlemock/include) - - -# LibQi -include(set_libqi_dependency) - -# Generate a list of dependency directories that can be used for -# instance to generate RPATH or install those dependencies. -set(QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS ${Boost_LIBRARY_DIRS}) -foreach(_lib IN LISTS OPENSSL_LIBRARIES ICU_LIBRARIES) - if(EXISTS ${_lib}) - get_filename_component(_dir ${_lib} DIRECTORY) - list(APPEND QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS ${_dir}) - endif() -endforeach() -list(REMOVE_DUPLICATES QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS) - -# Adds all the dependency directories as RPATH for a target ouput -# binary in the build directory, so that we may for instance execute -# it directly. -function(set_build_rpath_to_qipython_dependencies target) - message(VERBOSE - "Setting ${target} build RPATH to '${QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS}'") - set_property(TARGET ${target} PROPERTY - BUILD_RPATH ${QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS}) -endfunction() diff --git a/cmake/set_globals.cmake b/cmake/set_globals.cmake deleted file mode 100644 index 3bbccd9..0000000 --- a/cmake/set_globals.cmake +++ /dev/null @@ -1,50 +0,0 @@ -include_guard(GLOBAL) - -# Convert relative paths in `target_sources` to absolute. -cmake_policy(SET CMP0076 NEW) - -# Set the default policies for subprojects using an older CMake version -# (specified through `cmake_minimum_required`). Setting the policy directly -# (through `cmake_policy`) is not enough as it will be overwritten by the call -# to `cmake_minimum_required` in the subproject. This is to remove spurious -# warnings we get otherwise. -# -# 0048 - project() command manages VERSION variables. -set(CMAKE_POLICY_DEFAULT_CMP0048 NEW) -# 0056 - Honor link flags in try_compile() source-file signature. -set(CMAKE_POLICY_DEFAULT_CMP0056 NEW) -# 0066 - Honor per-config flags in try_compile() source-file signature. -set(CMAKE_POLICY_DEFAULT_CMP0066 NEW) -# 0060 - Link libraries by full path even in implicit directories. -set(CMAKE_POLICY_DEFAULT_CMP0060 NEW) -# 0063 - Honor visibility properties for all target types. -set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) -# 0074 - find_package uses PackageName_ROOT variables. -set(CMAKE_POLICY_DEFAULT_CMP0074 NEW) -# 0077 - option() honors normal variables. -set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) -# 0082 - Install rules from add_subdirectory() are interleaved with those in caller. -set(CMAKE_POLICY_DEFAULT_CMP0082 NEW) - -# Do not export symbols by default on new targets. -set(CMAKE_CXX_VISIBILITY_PRESET hidden) - -# We use RPATH on systems that handle them. -set(CMAKE_SKIP_RPATH OFF) -set(CMAKE_SKIP_BUILD_RPATH OFF) - -# Do not use the installation RPATH/RUNPATH values for the build directory. -# Our build directories do not necessarily share the same structure as our install -# directories. -set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF) - -# Do not append to the runtime search path (rpath) of installed binaries any -# directory outside the project that is in the linker search path or contains -# linked library files. -# Instead we copy the dependencies we need in the install directory, and we set -# the right RPATH ourselves. -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH OFF) - -set(QIPYTHON_PYTHON_MODULE_NAME qi) -set(QIPYTHON_PYTHON_MODULE_SOURCE_DIR - ${CMAKE_CURRENT_SOURCE_DIR}/${QIPYTHON_PYTHON_MODULE_NAME}) \ No newline at end of file diff --git a/cmake/set_install_rules.cmake b/cmake/set_install_rules.cmake deleted file mode 100644 index 583a400..0000000 --- a/cmake/set_install_rules.cmake +++ /dev/null @@ -1,7 +0,0 @@ -include_guard(GLOBAL) - -if(QIPYTHON_STANDALONE) - include(set_install_rules_standalone) -else() - include(set_install_rules_system) -endif() diff --git a/cmake/set_install_rules_standalone.cmake b/cmake/set_install_rules_standalone.cmake deleted file mode 100644 index e70f3d6..0000000 --- a/cmake/set_install_rules_standalone.cmake +++ /dev/null @@ -1,57 +0,0 @@ -include_guard(GLOBAL) - -if(NOT QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS) - message(AUTHOR_WARNING "The QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS is empty.") -endif() - -# Stripping the binaries enables us to have lighter wheels. -# -# CMake offers a target "install/strip" that normally does that automatically, -# but scikit-build uses the plain "install" target and this behavior is not -# easily customisable. So this option exists so that we may manually strip the -# binaries when set. -option(QIPYTHON_FORCE_STRIP - "If set, forces the stripping of the qi_python module at install." - TRUE) - -if(${QIPYTHON_FORCE_STRIP}) - message(STATUS "The qi_python library will be stripped at install.") - install(CODE "set(CMAKE_INSTALL_DO_STRIP TRUE)" - COMPONENT Module) -endif() - -# qibuild automatically installs a `path.conf` file that we don't really care -# about in our wheel, and we have no other way to disable its installation other -# than letting it be installed then removing it. -install(CODE - "set(_path_conf \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/share/qi/path.conf\") - file(REMOVE \${_path_conf}) - list(REMOVE_ITEM CMAKE_INSTALL_MANIFEST_FILES \${_path_conf})" - COMPONENT runtime) - -if(QIPYTHON_TOOLCHAIN_DIR) - install(CODE "set(QIPYTHON_TOOLCHAIN_DIR \"${QIPYTHON_TOOLCHAIN_DIR}\")" - COMPONENT Module) -endif() - -install(CODE - "set(QIPYTHON_PYTHON_MODULE_NAME \"${QIPYTHON_PYTHON_MODULE_NAME}\") - set(QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS \"${QIPYTHON_BUILD_DEPENDENCIES_LIBRARY_DIRS}\") - set(QIPYTHON_QI_PYTHON_TARGET_FILE \"$\") - set(QIPYTHON_QI_TARGET_FILE \"$\") - set(QIPYTHON_QI_PYTHON_TARGET_FILE_NAME \"$\") - set(QIPYTHON_QI_TARGET_FILE_NAME \"$\")" - COMPONENT Module) -install(SCRIPT cmake/install_runtime_dependencies.cmake COMPONENT Module) - -set_install_rpath(TARGETS qi_python qi ORIGIN) -install(TARGETS qi_python qi - LIBRARY DESTINATION "${QIPYTHON_PYTHON_MODULE_NAME}" - # On Windows, shared libraries (DLL) are considered `RUNTIME`. - RUNTIME DESTINATION "${QIPYTHON_PYTHON_MODULE_NAME}" - COMPONENT Module) - -# Install the Python file containing native informations. -install(FILES "${QIPYTHON_NATIVE_PYTHON_FILE}" - DESTINATION "${QIPYTHON_PYTHON_MODULE_NAME}" - COMPONENT Module) \ No newline at end of file diff --git a/cmake/set_install_rules_system.cmake b/cmake/set_install_rules_system.cmake deleted file mode 100644 index b7d8e73..0000000 --- a/cmake/set_install_rules_system.cmake +++ /dev/null @@ -1,35 +0,0 @@ -include_guard(GLOBAL) - - -if(NOT Python_VERSION_MAJOR OR NOT Python_VERSION_MINOR) - message(AUTHOR_WARNING "The Python major or minor version is unknown.") -endif() -set(_sitelib_dir - "lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages/${QIPYTHON_PYTHON_MODULE_NAME}") - -# Set the RPATH of the module to a relative libraries directory. We assume that, -# if the module is installed in "/path/lib/pythonX.Y/site-packages/qi", the -# libraries we need are instead in "/path/lib", so we set the RPATH accordingly. -set_install_rpath(TARGETS qi_python ORIGIN PATHS_REL_TO_ORIGIN "../../..") - -install(TARGETS qi_python - LIBRARY DESTINATION "${_sitelib_dir}" - COMPONENT runtime) - -install(FILES ${QIPYTHON_PYTHON_MODULE_FILES} - DESTINATION "${_sitelib_dir}" - COMPONENT runtime) - -# Install the Python file containing native information. -install(FILES "${QIPYTHON_NATIVE_PYTHON_FILE}" - DESTINATION "${_sitelib_dir}" - COMPONENT runtime) - -# Set the RPATH of the plugin to a relative libraries directory. We assume that, -# if the plugin is installed in "/path/lib/qi/plugins", the -# libraries we need are instead in "/path/lib", so we set the RPATH accordingly. -set_install_rpath(TARGETS qimodule_python_plugin ORIGIN PATHS_REL_TO_ORIGIN "../..") - -install(TARGETS qimodule_python_plugin - LIBRARY DESTINATION "lib/qi/plugins" - COMPONENT runtime) \ No newline at end of file diff --git a/cmake/set_libqi_dependency.cmake b/cmake/set_libqi_dependency.cmake deleted file mode 100644 index ed2fa99..0000000 --- a/cmake/set_libqi_dependency.cmake +++ /dev/null @@ -1,53 +0,0 @@ -overridable_variable(LIBQI_VERSION 4.0.0) - -# Our github clone is sometimes late or is missing tags, so we enable -# customisation of the URL at configuration time, so users can use another clone. -overridable_variable(LIBQI_GIT_REPOSITORY https://github.com/aldebaran/libqi.git) - -overridable_variable(LIBQI_GIT_TAG qi-framework-v${LIBQI_VERSION}) - -if(LIBQI_VERSION) - message(STATUS "LibQi: expected version is \"${LIBQI_VERSION}\"") -endif() - -# Checks that the version present in the `package_xml_file` file is the same as -# the one specified in the `LIBQI_VERSION` cache variable, if it is set. -function(check_libqi_version package_xml_file version_var) - file(STRINGS ${package_xml_file} _package_xml_strings REGEX version) - set(_package_version "") - if(_package_xml_strings MATCHES [[version="([^"]*)"]]) - set(_package_version ${CMAKE_MATCH_1}) - message(STATUS "LibQi: found version \"${_package_version}\" from file '${package_xml_file}'") - if(LIBQI_VERSION AND NOT (LIBQI_VERSION VERSION_EQUAL _package_version)) - message(FATAL_ERROR "LibQi version mismatch (expected \ - \"${LIBQI_VERSION}\", found \"${_package_version}\")") - endif() - else() - set(_msg_type WARNING) - if (LIBQI_VERSION) - set(_msg_type FATAL_ERROR) - endif() - message(${_msg_type} "Could not read LibQi version from file \ -${package_xml_file}. Please check that the file contains a version \ -attribute.") - endif() - set(${version_var} ${_package_version} PARENT_SCOPE) -endfunction() - -if(QIPYTHON_STANDALONE) - include(set_libqi_dependency_standalone) -else() - include(set_libqi_dependency_system) -endif() - -target_link_libraries(qi.interface INTERFACE cxx17) - -# Generate a Python file containing information about the native part of the -# module.\ -set(NATIVE_VERSION "${LIBQI_PACKAGE_VERSION}") -if(NOT NATIVE_VERSION) - set(NATIVE_VERSION "unknown") -endif() -set(QIPYTHON_NATIVE_PYTHON_FILE - "${CMAKE_CURRENT_BINARY_DIR}/${QIPYTHON_PYTHON_MODULE_NAME}/native.py") -configure_file(cmake/native.py.in "${QIPYTHON_NATIVE_PYTHON_FILE}" @ONLY) diff --git a/cmake/set_libqi_dependency_standalone.cmake b/cmake/set_libqi_dependency_standalone.cmake deleted file mode 100644 index a7de9f9..0000000 --- a/cmake/set_libqi_dependency_standalone.cmake +++ /dev/null @@ -1,71 +0,0 @@ -# In standalone mode, LibQi is currently added as a subproject. It's a -# controversial choice but depending on a prebuilt configuration, installation -# or package is messy with our CMake code. It makes compilation longer but it -# ensures consistency. - -include_guard(GLOBAL) - -if(NOT COMMAND FetchContent_Declare) - include(FetchContent) -endif() - -if(FETCHCONTENT_SOURCE_DIR_LIBQI) - message(STATUS "LibQi: using sources at \"${FETCHCONTENT_SOURCE_DIR_LIBQI}\"") -else() - message(STATUS "LibQi: using sources from \"${LIBQI_GIT_REPOSITORY}\" at tag \ -\"${LIBQI_GIT_TAG}\"") -endif() - -FetchContent_Declare(LibQi - GIT_REPOSITORY "${LIBQI_GIT_REPOSITORY}" - GIT_TAG "${LIBQI_GIT_TAG}" -) -FetchContent_GetProperties(LibQi) -if(NOT libqi_POPULATED) - FetchContent_Populate(LibQi) -endif() - -check_libqi_version("${libqi_SOURCE_DIR}/package.xml" LIBQI_PACKAGE_VERSION) - -# Adds LibQi as a subproject which will create a `qi` target. -# -# We use a function in order to scope the value of a few variables only for the -# LibQi subproject. -function(add_libqi_subproject) - # Do not build examples and tests, we don't need them in this project. - set(BUILD_EXAMPLES OFF) - set(QI_WITH_TESTS OFF) - - # Add it as a subproject but use `EXCLUDE_FROM_ALL` to disable the installation - # rules of the subproject files.com - add_subdirectory(${libqi_SOURCE_DIR} ${libqi_BINARY_DIR} EXCLUDE_FROM_ALL) -endfunction() -add_libqi_subproject() - -add_library(qi.interface INTERFACE) - -# Add the subproject include directories as "system" to avoid warnings -# generated by LibQi headers. -target_include_directories(qi.interface SYSTEM INTERFACE - ${libqi_SOURCE_DIR} - ${libqi_BINARY_DIR}/include) - -target_link_libraries(qi.interface INTERFACE - qi - -# qibuild CMake macros fail to propagate LibQi's include directories to the -# interface of the `qi` target, so we have to add them ourselves. For Boost, -# adding the header-only target is sufficient, as it will add the include -# directories, and the link libraries are already part of the `qi` target's -# interface (qibuild propagates those fine). - Boost::boost - -# These targets are not strictly speaking libraries but they add flags that -# disable autolinking in Boost and force dynamic linking, which are required -# on Windows. - Boost::disable_autolinking - Boost::dynamic_linking - -# For the libraries other than Boost, we don't have a header-only target to -# only add the include directories, so we have to directly link to them. - OpenSSL::SSL OpenSSL::Crypto) \ No newline at end of file diff --git a/cmake/set_libqi_dependency_system.cmake b/cmake/set_libqi_dependency_system.cmake deleted file mode 100644 index 46d2168..0000000 --- a/cmake/set_libqi_dependency_system.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# In system mode, LibQi must be accessible through find_package. -find_package(qi REQUIRED CONFIG) - -# If the qi package was found from a build directory in LibQi sources (which -# is the case when built with qibuild), then we expect that `qi_DIR` is set -# to the path `<...>/libqi/build/sdk/cmake`. -# If it was found in one of our toolchains, then we expect that `qi_DIR` is -# set to the path `/libqi(or qi-framework)/share/cmake/qi` in which -# case the `package.xml` file is in `/libqi(or qi-framework)`. -get_filename_component(QIPYTHON_LIBQI_PACKAGE_FILE - "${qi_DIR}/../../../package.xml" ABSOLUTE) -if(NOT EXISTS "${QIPYTHON_LIBQI_PACKAGE_FILE}") - get_filename_component(QIPYTHON_LIBQI_PACKAGE_FILE - "${qi_DIR}/../../package.xml" ABSOLUTE) -endif() - -set(QIPYTHON_LIBQI_PACKAGE_FILE "${QIPYTHON_LIBQI_PACKAGE_FILE}" CACHE FILEPATH - "Path to the `package.xml` file of LibQi.") - -if(LIBQI_VERSION) - if(EXISTS "${QIPYTHON_LIBQI_PACKAGE_FILE}") - check_libqi_version("${QIPYTHON_LIBQI_PACKAGE_FILE}" LIBQI_PACKAGE_VERSION) - else() - message(WARNING "The `package.xml` file for LibQi could not be found at \ -'${QIPYTHON_LIBQI_PACKAGE_FILE}' (the LibQi package was found in '${qi_DIR}'). \ -The version of LibQi could not be verified. Please manually specify a valid \ -path as the 'QIPYTHON_LIBQI_PACKAGE_FILE' variable to fix this issue, or unset \ -the 'LIBQI_VERSION' variable to completely disable this check.") - endif() -endif() - -add_library(qi.interface INTERFACE) -target_include_directories(qi.interface SYSTEM INTERFACE ${QI_INCLUDE_DIRS}) -target_link_libraries(qi.interface INTERFACE ${QI_LIBRARIES}) - -foreach(_dep IN LISTS QI_DEPENDS) - if(NOT ${_dep}_PACKAGE_FOUND) - find_package(${_dep} REQUIRED CONFIG) - endif() - target_include_directories(qi.interface SYSTEM INTERFACE ${${_dep}_INCLUDE_DIRS}) - target_link_libraries(qi.interface INTERFACE ${${_dep}_LIBRARIES}) -endforeach() diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..6777ae0 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,97 @@ +from conan import ConanFile, tools +from conan.tools.cmake import cmake_layout + +BOOST_COMPONENTS = [ + "atomic", + "chrono", + "container", + "context", + "contract", + "coroutine", + "date_time", + "exception", + "fiber", + "filesystem", + "graph", + "graph_parallel", + "iostreams", + "json", + "locale", + "log", + "math", + "mpi", + "nowide", + "program_options", + "python", + "random", + "regex", + "serialization", + "stacktrace", + "system", + "test", + "thread", + "timer", + "type_erasure", + "wave", +] + +USED_BOOST_COMPONENTS = [ + "atomic", # required by thread + "chrono", # required by thread + "container", # required by thread + "date_time", # required by thread + "exception", # required by thread + "filesystem", # required by libqi + "locale", # required by libqi + "program_options", # required by libqi + "random", # required by libqi + "regex", # required by libqi + "system", # required by thread + "thread", +] + + +class QiPythonConan(ConanFile): + requires = [ + "boost/[~1.78]", + "pybind11/[~2.9]", + "qi/4.0.1", + ] + + test_requires = [ + "gtest/cci.20210126", + ] + + generators = "CMakeToolchain", "CMakeDeps" + + # Binary configuration + settings = "os", "compiler", "build_type", "arch" + + default_options = { + "boost/*:shared": True, + "openssl/*:shared": True, + "qi/*:with_boost_locale": True, # for `pytranslator.cpp` + } + + # Disable every components of Boost unless we actively use them. + default_options.update( + { + f"boost/*:without_{_name}": False + if _name in USED_BOOST_COMPONENTS + else True + for _name in BOOST_COMPONENTS + } + ) + + def layout(self): + # Configure the format of the build folder name, based on the value of some variables. + self.folders.build_folder_vars = [ + "settings.os", + "settings.arch", + "settings.compiler", + "settings.build_type", + ] + + # The cmake_layout() sets the folders and cpp attributes to follow the + # structure of a typical CMake project. + cmake_layout(self) diff --git a/pyproject.toml b/pyproject.toml index cc4c529..69a9dea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,17 +1,49 @@ -[project] -dependencies = [ - "packaging" -] - # PEP 518 [build-system] -# Minimum requirements for the build system to execute. -requires = [ - "setuptools >= 47, < 51", # setuptools 51 dropped support for Python 3.5. - "wheel >= 0.34", # tested with 0.34. - "scikit-build >= 0.10", # tested with 0.10. - "cmake ~= 3.16", # CMake >= 3.16, CMake < 4. - "ninja ~= 1", # ninja >= 1, ninja < 2. - "toml ~= 10", - "packaging ~= 21", +requires = ["scikit-build-core"] +build-backend = "scikit_build_core.build" + +# PEP 621 +[project] +name = "qi" +description = "LibQi Python bindings" +version = "3.1.3.dev0" +readme = "README.rst" +requires-python = ">=3.7" +license = { "file" = "COPYING" } +keywords=[ + "libqi", + "qi", + "naoqi", + "aldebaran", + "robotics", + "robot", + "nao", + "pepper", + "romeo", + "plato", +] +classifiers=[ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: BSD License", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Libraries :: Application Frameworks", + "Topic :: Software Development :: Embedded Systems", + "Framework :: Robot Framework :: Library", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3 :: Only", ] +maintainers = [ + { email = "framework@aldebaran.com" }, + { name = "Vincent Palancher", email = "vincent.palancher@aldebaran.com" }, + { name = "Jérémy Monnon", email = "jmonnon@aldebaran.com" }, +] + +[project.urls] +repository = "https://github.com/aldebaran/libqi-python" diff --git a/qi/__init__.py b/qi/__init__.py index 7f5f83e..1770583 100644 --- a/qi/__init__.py +++ b/qi/__init__.py @@ -5,14 +5,6 @@ """LibQi Python bindings.""" -import platform -from packaging import version - -py_version = version.parse(platform.python_version()) -min_version = version.parse('3.5') -if py_version < min_version: - raise RuntimeError('Python 3.5+ is required.') - import sys # noqa: E402 import atexit # noqa: E402 diff --git a/qi/_version.py b/qi/_version.py deleted file mode 100644 index f71b21a..0000000 --- a/qi/_version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '3.1.2' diff --git a/qi/_version.py.in b/qi/_version.py.in new file mode 100644 index 0000000..11ba4dd --- /dev/null +++ b/qi/_version.py.in @@ -0,0 +1 @@ +__version__ = "@QI_PYTHON_VERSION_STRING@" diff --git a/qi/native.py.in b/qi/native.py.in new file mode 100644 index 0000000..8a158e2 --- /dev/null +++ b/qi/native.py.in @@ -0,0 +1 @@ +__version__ = "@NATIVE_VERSION@" diff --git a/qiproject.xml b/qiproject.xml deleted file mode 100644 index b106649..0000000 --- a/qiproject.xml +++ /dev/null @@ -1,13 +0,0 @@ - - Joël Lamotte - Jérémy Monnon - Philippe Martin - Vincent Palancher - Julien Bernard - - - - - - - diff --git a/qipython.pml b/qipython.pml deleted file mode 100644 index b55798b..0000000 --- a/qipython.pml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/setup.py b/setup.py deleted file mode 100755 index 66c1e53..0000000 --- a/setup.py +++ /dev/null @@ -1,67 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- - -import os -import platform -import pathlib -from setuptools import find_packages -from skbuild import setup -from packaging import version -import toml - -py_version = version.parse(platform.python_version()) -min_version = version.parse('3.5') -if py_version < min_version: - raise RuntimeError('Python 3.5+ is required.') - -# Parse `pyproject.toml` runtime dependencies. -pyproject_data = toml.loads(pathlib.Path('pyproject.toml').read_text()) -pyproject_deps = pyproject_data['project']['dependencies'] - -here = os.path.abspath(os.path.dirname(__file__)) - -version = {} -with open(os.path.join(here, "qi", "_version.py")) as f: - exec(f.read(), version) - -# Get the long description from the README file -with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() - -setup( - name='qi', - version=version['__version__'], - description='LibQi Python bindings', - long_description=long_description, - long_description_content_type='text/x-rst', - keywords=['libqi', 'qi', 'naoqi', - 'softbank', 'robotics', 'aldebaran', - 'robot', 'nao', 'pepper', 'romeo'], - url='https://github.com/aldebaran/libqi-python', - author='SoftBank Robotics Europe', - author_email='release@softbankrobotics.com', - license='BSD 3-Clause License', - python_requires='~=3.5', - install_requires=pyproject_deps, - packages=find_packages(exclude=[ - '*.test', '*.test.*', 'test.*', 'test' - ]), - include_package_data=True, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'License :: OSI Approved :: BSD License', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Application Frameworks', - 'Topic :: Software Development :: Embedded Systems', - 'Framework :: Robot Framework :: Library', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3 :: Only', - ], - cmake_args=['-DQIPYTHON_STANDALONE:BOOL=ON'], -) From bab25a701a3ce45c70c8313f479b72ed718187eb Mon Sep 17 00:00:00 2001 From: Vincent Palancher Date: Fri, 9 Jun 2023 20:25:34 +0200 Subject: [PATCH 2/2] Bumps version to v3.1.3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 69a9dea..6e048cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "scikit_build_core.build" [project] name = "qi" description = "LibQi Python bindings" -version = "3.1.3.dev0" +version = "3.1.3" readme = "README.rst" requires-python = ">=3.7" license = { "file" = "COPYING" }