diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 710c093ab4..01692e5d74 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -99,10 +99,6 @@ jobs: run: cmake --build . --config Release working-directory: ${{ runner.workspace }}/_build - - name: Build Python Wheel - run: cmake --build . --target create_python_wheel --config Release - working-directory: ${{ runner.workspace }}/_build - # - name: Build Documentation C # run: cmake --build . --target documentation_c # working-directory: ${{ runner.workspace }}/_build @@ -125,9 +121,3 @@ jobs: with: name: macos-dmg path: ${{ runner.workspace }}/_build/_deploy/*.dmg - - - name: Upload Python Wheel - uses: actions/upload-artifact@v4 - with: - name: macos-python-wheel - path: ${{ runner.workspace }}/_build/_deploy/*.whl diff --git a/.github/workflows/build-ubuntu-20.yml b/.github/workflows/build-ubuntu-20.yml index 06093aa992..cfce3aec05 100644 --- a/.github/workflows/build-ubuntu-20.yml +++ b/.github/workflows/build-ubuntu-20.yml @@ -104,39 +104,6 @@ jobs: run: cmake --build . --config Release -- -k 0 working-directory: ${{ runner.workspace }}/_build - # Create Python Wheels - # The strang-looking double-cmake is an ugly workaround to force CMake to - # re-find Python, after we have changed the venv from the outside. The - # alternative would be to clean everything, which would cause an unnecessary - # rebuild of eCAL for each python Version. - - name: Build Python 3.9 Wheel - run: | - sudo apt-get -y install python3.9-dev python3.9-venv - mkdir ".venv_39" - python3.9 -m venv ".venv_39" - source ".venv_39/bin/activate" - pip install --upgrade pip - pip install wheel setuptools - cmake $GITHUB_WORKSPACE -DPython_FIND_VIRTUALENV=FIRST - cmake $GITHUB_WORKSPACE -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: bash - working-directory: ${{ runner.workspace }}/_build - - - name: Build Python 3.8 Wheel - run: | - sudo apt-get -y install python3.8-dev python3.8-venv - mkdir ".venv_38" - python3.8 -m venv ".venv_38" - source ".venv_38/bin/activate" - pip install --upgrade pip - pip install wheel setuptools - cmake $GITHUB_WORKSPACE -DPython_FIND_VIRTUALENV=FIRST - cmake $GITHUB_WORKSPACE -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: bash - working-directory: ${{ runner.workspace }}/_build - - name: Run Tests run: ctest -V working-directory: ${{ runner.workspace }}/_build @@ -151,8 +118,3 @@ jobs: name: ubuntu-debian path: ${{ runner.workspace }}/_build/_deploy/*.deb - - name: Upload Python Wheel - uses: actions/upload-artifact@v4 - with: - name: ubuntu-python-wheel - path: ${{ runner.workspace }}/_build/_deploy/*.whl diff --git a/.github/workflows/build-ubuntu-22.yml b/.github/workflows/build-ubuntu-22.yml index afc88c1c29..d8e0df3381 100644 --- a/.github/workflows/build-ubuntu-22.yml +++ b/.github/workflows/build-ubuntu-22.yml @@ -103,39 +103,6 @@ jobs: run: cmake --build . --config Release -- -k 0 working-directory: ${{ runner.workspace }}/_build - # Create Python Wheels - # The strang-looking double-cmake is an ugly workaround to force CMake to - # re-find Python, after we have changed the venv from the outside. The - # alternative would be to clean everything, which would cause an unnecessary - # rebuild of eCAL for each python Version. - - name: Build Python 3.11 Wheel - run: | - sudo apt-get -y install python3.11-dev python3.11-venv - mkdir ".venv_311" - python3.11 -m venv ".venv_311" - source ".venv_311/bin/activate" - pip install --upgrade pip - pip install wheel setuptools - cmake $GITHUB_WORKSPACE -DPython_FIND_VIRTUALENV=FIRST - cmake $GITHUB_WORKSPACE -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: bash - working-directory: ${{ runner.workspace }}/_build - - - name: Build Python 3.10 Wheel - run: | - sudo apt-get -y install python3.10-dev python3.10-venv - mkdir ".venv_310" - python3.10 -m venv ".venv_310" - source ".venv_310/bin/activate" - pip install --upgrade pip - pip install wheel setuptools - cmake $GITHUB_WORKSPACE -DPython_FIND_VIRTUALENV=FIRST - cmake $GITHUB_WORKSPACE -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: bash - working-directory: ${{ runner.workspace }}/_build - - name: Run Tests run: ctest -V working-directory: ${{ runner.workspace }}/_build @@ -149,9 +116,3 @@ jobs: with: name: ubuntu-debian path: ${{ runner.workspace }}/_build/_deploy/*.deb - - - name: Upload Python Wheel - uses: actions/upload-artifact@v4 - with: - name: ubuntu-python-wheel - path: ${{ runner.workspace }}/_build/_deploy/*.whl diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index b9d09ac053..28444c6f03 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -127,7 +127,7 @@ jobs: -DBUILD_APPS=ON ^ -DBUILD_SAMPLES=ON ^ -DBUILD_TIME=ON ^ - -DBUILD_PY_BINDING=ON ^ + -DBUILD_PY_BINDING=OFF ^ -DBUILD_CSHARP_BINDING=ON ^ -DBUILD_ECAL_TESTS=ON ^ -DECAL_INCLUDE_PY_SAMPLES=OFF ^ @@ -163,83 +163,6 @@ jobs: run: cmake --build . --config Release working-directory: ${{ runner.workspace }}/_build/complete - # Create Python. - # The strang-looking double-cmake is an ugly workaround to force CMake to - # re-find Python, after we have changed the venv from the outside. The - # alternative would be to clean everything, which would cause an unnecessary - # rebuild of eCAL and HDF5 for each python Version. - - name: Build Python 3.12 Wheel - run: | - mkdir ".venv_312" - py -3.12 -m venv ".venv_312" - CALL ".venv_312\Scripts\activate.bat" - pip install wheel setuptools - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=FIRST - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: cmd - working-directory: ${{ runner.workspace }}/_build/complete - - - name: Build Python 3.11 Wheel - run: | - mkdir ".venv_311" - py -3.11 -m venv ".venv_311" - CALL ".venv_311\Scripts\activate.bat" - pip install wheel - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=FIRST - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: cmd - working-directory: ${{ runner.workspace }}/_build/complete - - - name: Build Python 3.10 Wheel - run: | - mkdir ".venv_310" - py -3.10 -m venv ".venv_310" - CALL ".venv_310\Scripts\activate.bat" - pip install wheel - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=FIRST - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: cmd - working-directory: ${{ runner.workspace }}/_build/complete - - - name: Build Python 3.9 Wheel - run: | - mkdir ".venv_39" - py -3.9 -m venv ".venv_39" - CALL ".venv_39\Scripts\activate.bat" - pip install wheel - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=FIRST - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: cmd - working-directory: ${{ runner.workspace }}/_build/complete - - - name: Build Python 3.8 Wheel - run: | - mkdir ".venv_38" - py -3.8 -m venv ".venv_38" - CALL ".venv_38\Scripts\activate.bat" - pip install wheel - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=FIRST - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: cmd - working-directory: ${{ runner.workspace }}/_build/complete - - - name: Build Python 3.7 Wheel - run: | - mkdir ".venv_37" - py -3.7 -m venv ".venv_37" - CALL ".venv_37\Scripts\activate.bat" - pip install wheel - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=FIRST - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v142 -DPython_FIND_VIRTUALENV=ONLY - cmake --build . --target create_python_wheel --config Release - shell: cmd - working-directory: ${{ runner.workspace }}/_build/complete - # - name: Build Documentation C # run: cmake --build . --target documentation_c # working-directory: ${{ runner.workspace }}/_build @@ -266,12 +189,6 @@ jobs: name: windows-setup path: ${{ runner.workspace }}/_build/complete/_deploy/*.exe - - name: Upload Python Wheels - uses: actions/upload-artifact@v4 - with: - name: windows-python-wheels - path: ${{ runner.workspace }}/_build/complete/_deploy/*.whl - # -------------------------------------------------------------------------------------------------- sign-windows-installer: diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml new file mode 100644 index 0000000000..ec6689924f --- /dev/null +++ b/.github/workflows/build_wheels.yml @@ -0,0 +1,63 @@ +name: Build Wheels + +on: + pull_request: + branches: + - master + release: + types: [published] + workflow_dispatch: + + +jobs: + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + + steps: + - uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 0 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.17 + env: + CIBW_ARCHS: auto64 + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl + + build_arm64_wheels: + # Emulation takes a long time so we save it for release/manual triggering + name: Build arm64 wheels via emulation + runs-on: ubuntu-latest + if: ${{ github.event_name != 'pull_request' }} + + steps: + - uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 0 + + - name: Set up QEMU + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.17 + env: + CIBW_ARCHS_LINUX: aarch64 + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-arm64 + path: ./wheelhouse/*.whl + diff --git a/.gitignore b/.gitignore index 78c44f672e..4afb4f2396 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,12 @@ Thumbs.db /_vs_out* /out +# Python building and distribution +/_python_build/ +/dist/ +/lang/python/core/ecal/_version.py +/wheelhouse/ + # Binary libraries /thirdparty/npcap/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d5ee871cd..3777ea5eef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,7 +88,6 @@ option(BUILD_APPS "Build the eCAL applications" option(BUILD_SAMPLES "Build the eCAL samples" ON) option(BUILD_TIME "Build the eCAL time interfaces" ON) option(BUILD_PY_BINDING "Build eCAL python binding" OFF) -option(BUILD_STANDALONE_PY_WHEEL "Build eCAL python binding as standalone wheel" OFF) option(BUILD_CSHARP_BINDING "Build eCAL C# binding" OFF) option(BUILD_ECAL_TESTS "Build the eCAL google tests" OFF) @@ -125,7 +124,18 @@ option(CPACK_PACK_WITH_INNOSETUP "Create Innosetup installer for t set(ECAL_INSTALL_PYTHON_DIR "bin" CACHE PATH "Location to install the Python extension modules. Might be set by setupdtools install.") -set(ECAL_BUILD_VERSION "0.0.0" CACHE STRING "Inject a build version if not building from a git repository") +if(DEFINED SKBUILD_PROJECT_VERSION) + message(STATUS + "Using version from scikit-build-core: ${SKBUILD_PROJECT_VERSION}" + ) + set(ECAL_BUILD_VERSION "${SKBUILD_PROJECT_VERSION}" CACHE STRING + "Version provided by scikit-build-core and setuptools-scm" FORCE + ) +else() + set(ECAL_BUILD_VERSION "0.0.0" CACHE STRING + "Inject a build version if not building from a git repository" + ) +endif() set(ECAL_CSHARP_BUILD_SAMPLES ${BUILD_SAMPLES}) @@ -172,10 +182,12 @@ set(eCAL_VERSION_PATCH ${GIT_REVISION_PATCH}) set(eCAL_VERSION_STRING ${eCAL_VERSION_MAJOR}.${eCAL_VERSION_MINOR}.${eCAL_VERSION_PATCH}) set(eCAL_VERSION ${eCAL_VERSION_STRING}) +message(STATUS "eCAL version: ${eCAL_VERSION_STRING}") +message(DEBUG "eCAL git describe tag: ${GIT_DESCRIBE_TAG}") + include(helper_functions/ecal_add_functions) include(helper_functions/ecal_helper_functions) include(helper_functions/ecal_install_functions) -include(helper_functions/ecal_python_functions) if(MSVC) set(eCAL_PLATFORM_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) @@ -465,7 +477,6 @@ message(STATUS "BUILD_APPS : ${BUILD_APPS}") message(STATUS "BUILD_SAMPLES : ${BUILD_SAMPLES}") message(STATUS "BUILD_TIME : ${BUILD_TIME}") message(STATUS "BUILD_PY_BINDING : ${BUILD_PY_BINDING}") -message(STATUS "BUILD_STANDALONE_PY_WHEEL : ${BUILD_STANDALONE_PY_WHEEL}") message(STATUS "BUILD_CSHARP_BINDING : ${BUILD_CSHARP_BINDING}") message(STATUS "BUILD_ECAL_TESTS : ${BUILD_ECAL_TESTS}") message(STATUS "ECAL_INCLUDE_PY_SAMPLES : ${ECAL_INCLUDE_PY_SAMPLES}") diff --git a/CMakePresets.json b/CMakePresets.json index 986f0f0f8f..fcf0472f7d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -62,20 +62,20 @@ } }, { - "name": "wheel", + "name": "python", "inherits": "core", - "displayName": "Python Wheel", - "description": "Minimal build for standalone python wheel", + "displayName": "Python Extensions", + "description": "Minimal build for Python extensions", "cacheVariables": { "HAS_HDF5": "ON", "BUILD_PY_BINDING": "ON", - "BUILD_STANDALONE_PY_WHEEL": "ON", - "ECAL_THIRDPARTY_BUILD_HDF5": null + "BUILD_SHARED": "OFF", + "ECAL_THIRDPARTY_BUILD_HDF5": "ON" } }, { "name": "docs", - "inherits": "wheel", + "inherits": "python", "displayName": "Documentation", "description": "Build documentation", "cacheVariables": { @@ -92,6 +92,7 @@ "HAS_CURL": "ON", "HAS_FTXUI": "ON", "BUILD_APPS": "ON", + "BUILD_SAMPLES": "ON", "ECAL_THIRDPARTY_BUILD_FINEFTP": "ON", "ECAL_THIRDPARTY_BUILD_FTXUI": "ON", "ECAL_THIRDPARTY_BUILD_SPDLOG": "ON", @@ -119,16 +120,16 @@ "configurePreset": "core" }, { - "name": "wheel", - "description": "Build standalone python wheel", - "configurePreset": "wheel", - "targets": "create_python_wheel" + "name": "python", + "description": "Build python extensions", + "configurePreset": "python", + "targets": "ecal_python" }, { "name": "docs", "description": "Build sphinx documentation", "configurePreset": "docs", - "targets": "documentation_sphinx" + "targets": "documentation_sphinx" }, { "name": "cli", diff --git a/app/app_pb/CMakeLists.txt b/app/app_pb/CMakeLists.txt index edd036188f..4118e26256 100644 --- a/app/app_pb/CMakeLists.txt +++ b/app/app_pb/CMakeLists.txt @@ -73,10 +73,4 @@ target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) ecal_install_library(${PROJECT_NAME}) -if(BUILD_PY_BINDING) - protobuf_generate_python_ext(python_sources ${PYTHON_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src ${ProtoFiles}) - target_sources(${PROJECT_NAME} PRIVATE ${python_sources}) - set_source_files_properties(${python_sources} PROPERTIES HEADER_FILE_ONLY TRUE) -endif() - set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER app/app_pb) diff --git a/cmake/helper_functions/ecal_python_functions.cmake b/cmake/helper_functions/ecal_python_functions.cmake deleted file mode 100644 index b51b5b179f..0000000000 --- a/cmake/helper_functions/ecal_python_functions.cmake +++ /dev/null @@ -1,112 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -set(PYTHON_BINARY_DIR ${CMAKE_BINARY_DIR}/python) -set(PYTHON_BINARY_MODULE_DIR ${CMAKE_BINARY_DIR}/python/ecal) - -#! ecal_add_python_module : this function adds a python module -# -# This function -# -# \arg:TARGET_NAME the first argument -# \param:SOURCES SOURCES specify the fooness of the function -# \param:PYTHON_CODE PYTHON_CODE should always be 42 -# \group:GROUP1 GROUP1 is a list of project to foo -# -function(ecal_add_python_module TARGET_NAME) - - set(multiValueArgs SOURCES) - set(singleValueArgs PYTHON_CODE) - cmake_parse_arguments(ARGUMENTS - "" - "${singleValueArgs}" - "${multiValueArgs}" ${ARGN} ) - - if(NOT ARGUMENTS_SOURCES AND NOT ARGUMENTS_PYTHON_CODE) - message(ERROR "Error in ecal_add_python_module: Please specify SOURCES and / or PYTHON_CODE arguments") - endif() - - - # if Sources are specified, a library is created - if(ARGUMENTS_SOURCES) - Python_add_library(${TARGET_NAME} MODULE ${ARGUMENTS_SOURCES}) - set_target_properties(${TARGET_NAME} - PROPERTIES - PREFIX "" - LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PYTHON_BINARY_MODULE_DIR}" - LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PYTHON_BINARY_MODULE_DIR}" - LIBRARY_OUTPUT_DIRECTORY "${PYTHON_BINARY_MODULE_DIR}" - DEBUG_POSTFIX "_d" - ) - # if no sources are specified, then a custom target needs to be created - else() - add_custom_target(${TARGET_NAME} ALL - COMMENT "Custom python target: ${TARGET_NAME}") - endif() - - if(ARGUMENTS_PYTHON_CODE) - # Copy all files from the source folder to the python binary directory. - get_filename_component(absolute_folder_python_files ${ARGUMENTS_PYTHON_CODE} ABSOLUTE) - file(GLOB_RECURSE relative_python_files - RELATIVE ${absolute_folder_python_files} - LIST_DIRECTORIES false - CONFIGURE_DEPENDS - ${absolute_folder_python_files}/*.py) - - foreach (f ${relative_python_files}) - set(origin_file ${absolute_folder_python_files}/${f}) - set(destination_file ${PYTHON_BINARY_MODULE_DIR}/${f}) - configure_file(${origin_file} ${destination_file} COPYONLY) - endforeach() - endif() -endfunction() - -function(ecal_add_pybind11_module TARGET_NAME) - set(multiValueArgs SOURCES) - set(singleValueArgs PYTHON_CODE) - cmake_parse_arguments(ARGUMENTS - "" - "${singleValueArgs}" - "${multiValueArgs}" ${ARGN} ) - - if(NOT ARGUMENTS_SOURCES AND NOT ARGUMENTS_PYTHON_CODE) - message(ERROR "Error in ecal_add_python_module: Please specify SOURCES and / or PYTHON_CODE arguments") - endif() - - - pybind11_add_module(${TARGET_NAME} ${ARGUMENTS_SOURCES}) - set_target_properties(${TARGET_NAME} - PROPERTIES - PREFIX "" - LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PYTHON_BINARY_MODULE_DIR}" - LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PYTHON_BINARY_MODULE_DIR}" - LIBRARY_OUTPUT_DIRECTORY "${PYTHON_BINARY_MODULE_DIR}" - DEBUG_POSTFIX "_d" - ) - - if(ARGUMENTS_PYTHON_CODE) - add_custom_command( - TARGET ${TARGET_NAME} - PRE_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${ARGUMENTS_PYTHON_CODE} ${PYTHON_BINARY_MODULE_DIR} - ) - endif() -endfunction () - -function(ecal_install_python_module) -endfunction() \ No newline at end of file diff --git a/contrib/ecaltime/ecaltime_pb/CMakeLists.txt b/contrib/ecaltime/ecaltime_pb/CMakeLists.txt index 4c4dc0ab16..59998dfefe 100644 --- a/contrib/ecaltime/ecaltime_pb/CMakeLists.txt +++ b/contrib/ecaltime/ecaltime_pb/CMakeLists.txt @@ -62,10 +62,4 @@ target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) ecal_install_library(${PROJECT_NAME}) -if(BUILD_PY_BINDING) - protobuf_generate_python_ext(python_sources ${PYTHON_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src ${ProtoFiles}) - target_sources(${PROJECT_NAME} PRIVATE ${python_sources}) - set_source_files_properties(${python_sources} PROPERTIES HEADER_FILE_ONLY TRUE) -endif() - set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER contrib/ecaltime/ecaltime_pb) diff --git a/ecal/core_pb/CMakeLists.txt b/ecal/core_pb/CMakeLists.txt index 7f07e98b44..7295221be0 100644 --- a/ecal/core_pb/CMakeLists.txt +++ b/ecal/core_pb/CMakeLists.txt @@ -69,10 +69,4 @@ target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) ecal_install_library(${PROJECT_NAME}) -if(BUILD_PY_BINDING) - protobuf_generate_python_ext(python_sources ${PYTHON_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src ${ProtoFiles}) - target_sources(${PROJECT_NAME} PRIVATE ${python_sources}) - set_source_files_properties(${python_sources} PROPERTIES HEADER_FILE_ONLY TRUE) -endif() - set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER core) diff --git a/lang/python/CMakeLists.txt b/lang/python/CMakeLists.txt index be9c65a944..eeeee67a9d 100644 --- a/lang/python/CMakeLists.txt +++ b/lang/python/CMakeLists.txt @@ -5,9 +5,9 @@ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,54 +16,34 @@ # # ========================= eCAL LICENSE ================================= -project(_ecal_py) - -find_package(Python COMPONENTS Development Interpreter) -find_package(Protobuf REQUIRED) - -set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in") -set(SETUP_PY "${PYTHON_BINARY_DIR}/setup.py") - -# Create a shortened protobuf version string -string(REPLACE "." ";" Protobuf_VERSION_components "${Protobuf_VERSION}") -list(LENGTH Protobuf_VERSION_components Protobuf_VERSION_components_length) - -if ("${Protobuf_VERSION_components_length}" LESS "2") - message(FATAL_ERROR "Unable to determine protobuf version for python binding") -endif() -list(GET Protobuf_VERSION_components 0 Protobuf_VERSION_major) -list(GET Protobuf_VERSION_components 1 Protobuf_VERSION_minor) -MATH(EXPR Protobuf_Version_minor_inc "${Protobuf_VERSION_minor}+1") - -if (Protobuf_VERSION VERSION_LESS "3.19") - set(Protobuf_required_versions ">=${Protobuf_VERSION},<=3.20") -else() - set(Protobuf_required_versions ">=${Protobuf_VERSION}") -endif() +# 3.18 required for Development.Module. +# prior versions using just Development would also search for artifacts for +# embedding, which the manylinux containers don't provide +cmake_minimum_required(VERSION 3.18...3.26) +project(ecal_python) +find_package(Python REQUIRED COMPONENTS Development.Module Interpreter) -configure_file(${SETUP_PY_IN} ${SETUP_PY} @ONLY) +# Convenience target to have all Python targets buildable via one name +add_custom_target(${PROJECT_NAME}) -if(Python_Interpreter_FOUND) - message(STATUS "Python interpreter found: ${Python_EXECUTABLE}") +# We will want the shared objects to look relative to themselves for vendored +# dependencies, like eCAL core and hdf5 +# NB: Even though ${ORIGIN} and $ORIGIN are both valid, auditwheel only +# understands $ORIGIN +set(CMAKE_INSTALL_RPATH "\$ORIGIN") - # on make install the python module gets installed directly by invocing python interpreter - # TODO: this needs to be tested on windows also - message(STATUS "Installing Python extensions") - #install(CODE "execute_process(COMMAND ${Python_EXECUTABLE} setup.py install --user WORKING_DIRECTORY ${PYTHON_BINARY_DIR})") - add_custom_target(create_python_wheel - ${Python_EXECUTABLE} setup.py bdist_wheel --dist-dir=${BUILD_DEPLOY_DIRECTORY} - DEPENDS _ecal_core_py _ecal_hdf5_py - WORKING_DIRECTORY ${PYTHON_BINARY_DIR} - COMMENT "Creating python wheel" - ) - - set_property(TARGET ${create_python_wheel} PROPERTY FOLDER lang/python) -else() - message(FATAL_ERROR "Could not find python interpreter! This is needed to install the eCAL python binding!") -endif() +# Directly build with the install runtime paths as these shared objects aren't +# for build tree use. +set(CMAKE_BUILD_WITH_INSTALL_RPATH "ON") add_subdirectory(core) +add_dependencies(${PROJECT_NAME} _ecal_core_py) + if(HAS_HDF5) add_subdirectory(ecalhdf5) + add_dependencies(${PROJECT_NAME} _ecal_hdf5_py) +else() + message(WARNING "Building Python bindings without HDF5 support") endif() + diff --git a/lang/python/core/CMakeLists.txt b/lang/python/core/CMakeLists.txt index 0206b42252..c6d1972e1c 100644 --- a/lang/python/core/CMakeLists.txt +++ b/lang/python/core/CMakeLists.txt @@ -5,9 +5,9 @@ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,51 +16,29 @@ # # ========================= eCAL LICENSE ================================= -if (WIN32) - add_custom_target(copy_ecal_core_dll ALL - COMMAND cmake -E copy_if_different "$" "${PYTHON_BINARY_MODULE_DIR}" - COMMENT "Copy eCAL Core DLL to python directory" - DEPENDS eCAL::core - ) - set_property(TARGET copy_ecal_core_dll PROPERTY FOLDER lang/python/core) -endif() - -# ========================================== - project(_ecal_core_py) -find_package(Python COMPONENTS Development Interpreter) -find_package(Protobuf REQUIRED) - -set(ecal_lang_py_src - src/ecal_clang.cpp - src/ecal_clang.h - src/ecal_wrap.cxx +python_add_library(${PROJECT_NAME} MODULE WITH_SOABI + src/ecal_clang.cpp + src/ecal_clang.h + src/ecal_wrap.cxx ) -ecal_add_python_module(${PROJECT_NAME} SOURCES ${ecal_lang_py_src} PYTHON_CODE ${CMAKE_CURRENT_SOURCE_DIR}/ecal) - target_link_libraries(${PROJECT_NAME} - PRIVATE - eCAL::core + PRIVATE + eCAL::core ) target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) -if (WIN32) - add_dependencies(${PROJECT_NAME} copy_ecal_core_dll) -endif() - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER lang/python/core) +set_target_properties(${PROJECT_NAME} PROPERTIES + FOLDER lang/python/core +) -if(BUILD_STANDALONE_PY_WHEEL AND NOT WIN32) - find_package(patchelf REQUIRED) - add_custom_command(TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" ARGS "-DRUNTIME_FILE=\"$\"" "-DRUNTIME_DEP_FILTER=\"ecal_core|protobuf\"" "-DRUNTIME_DEPLOY_DIR=\"${PYTHON_BINARY_MODULE_DIR}\"" -P "${CMAKE_SOURCE_DIR}/cmake/deploy_runtime_deps.cmake" - COMMAND "${PATCHELF_COMMAND}" ARGS "--remove-rpath" "\"$\"" - COMMAND "${PATCHELF_COMMAND}" ARGS "--force-rpath" "--set-rpath" "\\$$ORIGIN" "\"$\"") -endif() +install(TARGETS ${PROJECT_NAME} core + RUNTIME DESTINATION ecal COMPONENT python EXCLUDE_FROM_ALL + LIBRARY DESTINATION ecal COMPONENT python EXCLUDE_FROM_ALL NAMELINK_SKIP +) if(ECAL_INCLUDE_PY_SAMPLES) if(WIN32) diff --git a/lang/python/ecalhdf5/CMakeLists.txt b/lang/python/ecalhdf5/CMakeLists.txt index 48345a8930..560b878730 100644 --- a/lang/python/ecalhdf5/CMakeLists.txt +++ b/lang/python/ecalhdf5/CMakeLists.txt @@ -5,9 +5,9 @@ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,57 +16,34 @@ # # ========================= eCAL LICENSE ================================= -if (WIN32) - if (TARGET hdf5::hdf5-shared) - add_custom_target(copy_hdf5_dll ALL - COMMAND cmake -E copy_if_different "$" "${PYTHON_BINARY_MODULE_DIR}" - COMMENT "Copy hdf5 DLL to python directory" - DEPENDS hdf5::hdf5-shared - ) - set_property(TARGET copy_hdf5_dll PROPERTY FOLDER lang/python/hdf5) - endif() -endif() - -# ========================================== - project(_ecal_hdf5_py) -find_package(Python COMPONENTS Development) - -set(ecal_lang_py_src - src/ecalhdf5_wrap.cxx +python_add_library(${PROJECT_NAME} MODULE WITH_SOABI + src/ecalhdf5_wrap.cxx ) -ecal_add_python_module(${PROJECT_NAME} SOURCES ${ecal_lang_py_src} PYTHON_CODE ${CMAKE_CURRENT_SOURCE_DIR}/ecal) - target_link_libraries(${PROJECT_NAME} - PRIVATE - eCAL::hdf5 + PRIVATE + eCAL::hdf5 ) target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) -if (WIN32) - if (TARGET hdf5::hdf5-shared) - add_dependencies(${PROJECT_NAME} copy_hdf5_dll) - endif() -endif() - -if(MSVC) - set_property(TARGET ${PROJECT_NAME} PROPERTY LINK_FLAGS "/ignore:4098,4099") -endif(MSVC) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER lang/python/hdf5) +set_target_properties(${PROJECT_NAME} PROPERTIES + FOLDER lang/python/hdf5 +) -if(BUILD_STANDALONE_PY_WHEEL AND NOT WIN32) - find_package(patchelf REQUIRED) - add_custom_command(TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" ARGS "-DRUNTIME_FILE=\"$\"" "-DRUNTIME_DEP_FILTER=\"hdf5|sz|aec\"" "-DRUNTIME_DEPLOY_DIR=\"${PYTHON_BINARY_MODULE_DIR}\"" -P "${CMAKE_SOURCE_DIR}/cmake/deploy_runtime_deps.cmake" - COMMAND "${PATCHELF_COMMAND}" ARGS "--remove-rpath" "\"$\"" - COMMAND "${PATCHELF_COMMAND}" ARGS "--force-rpath" "--set-rpath" "\\$$ORIGIN" "\"$\"") +if(TARGET hdf5-shared) + install(TARGETS ${PROJECT_NAME} hdf5-shared + RUNTIME DESTINATION ecal COMPONENT python EXCLUDE_FROM_ALL + LIBRARY DESTINATION ecal COMPONENT python EXCLUDE_FROM_ALL NAMELINK_SKIP + ) endif() +install(TARGETS ${PROJECT_NAME} + DESTINATION ecal COMPONENT python EXCLUDE_FROM_ALL +) + if(ECAL_INCLUDE_PY_SAMPLES) if(WIN32) diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in deleted file mode 100644 index 6a6028d645..0000000000 --- a/lang/python/setup.py.in +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python - -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -import setuptools -import platform -from setuptools.dist import Distribution - -class BinaryDistribution(Distribution): - """Distribution which always forces a binary package with platform name""" - def has_ext_modules(self): - return True - -# this is the input file for cmake. for further details have a look at the -# CMakeLists.txt in this directory. - -setuptools.setup ( - name = 'ecal5', - version = '@eCAL_VERSION_STRING@', - description = 'This is the eCAL python API', - license = 'Apache 2.0', - author = 'Rex Schilasky', - author_email = 'rex.schilasky@continental-corporation.com', - packages = setuptools.find_packages(), - data_files = [], - package_data = {'ecal': ['*.pyd', '*.so', '*.so.*', '*.dll']}, - install_requires = [ - 'protobuf@Protobuf_required_versions@' - ], - zip_safe=False, - distclass = BinaryDistribution, -) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..fd6a1605c3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,115 @@ +[project] # Project metadata +name = "eclipse-ecal" +readme = "README.md" +requires-python = ">=3.7" +license = { "file" = "LICENSE.txt" } +authors = [ + { "name" = "Kerstin Keller", "email" = "kerstin.keller@continental.com" }, +] +keywords = ["DDS", "Middlware"] +classifiers = ["Topic :: Scientific/Engineering"] +dependencies = ["protobuf >= 3.8, == 3.*"] +# setuptools-scm will grab the version from the latest git tag +dynamic = ["version"] + +[project.urls] +"Documentation" = "https://eclipse-ecal.github.io/ecal" +"Source" = "https://github.com/eclipse-ecal/ecal" + +[build-system] # How pip and other frontends should build this project +# Version 8 of setuptools_scm drops Python 3.7 +requires = ["scikit-build-core>=0.8", "setuptools_scm~=7.1"] +build-backend = "scikit_build_core.build" + +[tool.setuptools_scm] +write_to = "lang/python/core/ecal/_version.py" + +[tool.scikit-build] +# Setuptools-scm to provide the package version from git +metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" +# Will be installed from PyPI if system version is too old/missing +cmake.version = ">=3.18" +cmake.targets = ["ecal_python"] # Targets to build +build-dir = "./_python_build" # Build directory for unisolated builds +install.components = ["python"] # CMake component to install +# Directory structure to copy as Python package, last path component is +# the package name +wheel.packages = ["lang/python/core/ecal", "lang/python/ecalhdf5/ecal"] + +# Files to include in the source archive to build from +sdist.exclude = ["*"] +sdist.include = [ + "/CMakeLists.txt", + "/pyproject.toml", + "/LICENSE.txt", + "/NOTICE.txt", + "/README.md", + "/app/app_pb/", + "/app/apps/", # TODO: Remove this directory + "/app/rec/rec_addon_*/", # TODO: Remove this directory + "/cmake/", + "/contrib/", + "/cpack/", + "/ecal/core/", + "/ecal/service/", + "/ecal/CMakeLists.txt", + "/lib/", + "/licenses/", + "lang/python/", + + "thirdparty/cmakefunctions/", + "thirdparty/protobuf/", + "thirdparty/recycle/", + "thirdparty/simpleini/", + "thirdparty/tclap/", + "thirdparty/tcp_pubsub/", + + "thirdparty/asio/", + "!thirdparty/asio/asio/asio/src/", + + # HDF5 has a lot of stuff we don't use + "/thirdparty/hdf5/", + "!/thirdparty/hdf5/hdf5/tools/", + "!/thirdparty/hdf5/hdf5/**/test*/", + "!/thirdparty/hdf5/hdf5/**/examples*/", + "!/thirdparty/hdf5/hdf5/hl/", + "!/thirdparty/hdf5/hdf5/doxygen/", + "!/thirdparty/hdf5/hdf5/java/", + "!/thirdparty/hdf5/hdf5/fortran/", + +] + +[tool.scikit-build.cmake.define] +CMAKE_PROJECT_TOP_LEVEL_INCLUDES = "cmake/submodule_dependencies.cmake" +HAS_HDF5 = "ON" +HAS_QT = "OFF" +HAS_CURL = "OFF" +HAS_FTXUI = "OFF" +BUILD_APPS = "OFF" +BUILD_SAMPLES = "OFF" +BUILD_TIME = "OFF" +BUILD_PY_BINDING = "ON" +BUILD_SHARED_LIBS = "OFF" +ECAL_INSTALL_SAMPLE_SOURCES = "OFF" +ECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS = "ON" +ECAL_THIRDPARTY_BUILD_TCP_PUBSUB = "ON" +ECAL_THIRDPARTY_BUILD_RECYCLE = "ON" +ECAL_THIRDPARTY_BUILD_PROTOBUF = "ON" +ECAL_THIRDPARTY_BUILD_FINEFTP = "OFF" +ECAL_THIRDPARTY_BUILD_FTXUI = "OFF" +ECAL_THIRDPARTY_BUILD_SPDLOG = "OFF" +ECAL_THIRDPARTY_BUILD_TERMCOLOR = "OFF" +ECAL_THIRDPARTY_BUILD_TINYXML2 = "OFF" +ECAL_THIRDPARTY_BUILD_YAML-CPP = "OFF" +ECAL_THIRDPARTY_BUILD_CURL = "OFF" +ECAL_THIRDPARTY_BUILD_HDF5 = "ON" +# Stop HDF5 trying to export eCALCoreTargets due to the hack +# with HDF5_EXPORTED_TARGETS +HDF5_EXTERNALLY_CONFIGURED = "ON" + +[tool.cibuildwheel] +# Only support 64-bit builds for now as CMake gets confused when the +# architecture changes and causes link errors with Python +archs = ["auto64"] +# eCAL has build errors on musl libc +skip = [ "*-musllinux*" ]