# ========================= eCAL LICENSE ================================= # # Copyright (C) 2016 - 2025 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 ================================= # 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) option(ECAL_PYTHON_BUILD_SAMPLES "Includes the python samples" OFF) option(ECAL_PYTHON_BUILD_TESTS "Includes the python tests" ON) option(ECAL_PYTHON_USE_HDF5 "Enables eCAL application cmd line interfaces" ON) if (NOT ECAL_PYTHON_USE_HDF5) message(FATAL_ERROR "Building python bindings without hdf5 is not supported.") endif() set(ECAL_PYTHON_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) # Try to make modules loadable by debug python version if(WIN32) set(CMAKE_DEBUG_POSTFIX _d) endif() # This function takes a list of python files to be copied to the package directory # and associates the files with a given python extension target # Using this function allows to create the whole python package editable in place # Arguments: # TARGET Python extension target with which to associate the files # PYTHON_CODE_ROOT Root folder for Python files, to keep folder structure # PYTHON_FILES Python files associated with target function(copy_python_code) set(singleValueArgs TARGET PYTHON_CODE_ROOT) set(multiValueArgs PYTHON_FILES) cmake_parse_arguments(ARGUMENTS "" "${singleValueArgs}" "${multiValueArgs}" ${ARGN} ) cmake_path( ABSOLUTE_PATH ARGUMENTS_PYTHON_CODE_ROOT OUTPUT_VARIABLE absolute_path_python_code_root ) foreach (python_file ${ARGUMENTS_PYTHON_FILES}) cmake_path( ABSOLUTE_PATH python_file OUTPUT_VARIABLE absolute_path_python_file ) cmake_path( RELATIVE_PATH absolute_path_python_file BASE_DIRECTORY ${absolute_path_python_code_root} OUTPUT_VARIABLE relative_path) # Now we actually copy the file to the correct directory set(origin_file ${absolute_path_python_file}) set(destination_file ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/python/${relative_path}) file( GENERATE OUTPUT ${destination_file} INPUT ${origin_file} ) endforeach() endfunction() # Function to set the correct output directory for the python targets, so that they can be debugged properly function(ecal_python_set_output_directory TARGET_NAME) set_target_properties(${TARGET_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY $,${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/python/ecal,${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/python/ecal> ) endfunction() # Function to add a python file as a sample to a Visual Studio Solution # It can then be started / debugged directly from within Visual Studio # Other generators are not supported at this time. function(ecal_python_add_sample) set(singleValueArgs PY_FILE TARGET_NAME) cmake_parse_arguments(ARGUMENTS "" "${singleValueArgs}" "" ${ARGN} ) set(ECAL_PYPROJ_FILE ${ARGUMENTS_PY_FILE}) string(UUID ECAL_PYPROJ_GUID NAMESPACE 8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942 NAME ${ARGUMENTS_TARGET_NAME} TYPE MD5) get_target_property(ECAL_PYPROJ_INTERPRETER_DEBUG Python::Interpreter LOCATION) get_target_property(ECAL_PYPROJ_INTERPRETER_RELEASE Python::Interpreter LOCATION) set(ECAL_PYPROJ_NAME ${ARGUMENTS_TARGET_NAME}) cmake_path(GET ECAL_PYPROJ_INTERPRETER_RELEASE PARENT_PATH ECAL_PYPROJ_PYTHON_ROOT) set(ECAL_PYPROJ_PYTHON_VERSION ${Python_VERSION}) set(ECAL_PYPROJ_SEARCH_PATH_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug/python) set(ECAL_PYPROJ_SEARCH_PATH_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Release/python) set(ECAL_PYPROJ_SEARCH_PATH_RELWITHDEBINFO ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/RelWithDebInfo/python/ecal) set(generated_pyproj_path ${CMAKE_CURRENT_BINARY_DIR}/${ARGUMENTS_TARGET_NAME}.pyproj) # Generate the .pyproj file from the template configure_file( ${ECAL_PYTHON_DIRECTORY}/sample.pyproj.in ${generated_pyproj_path} @ONLY ) include_external_msproject(${ARGUMENTS_TARGET_NAME} ${generated_pyproj_path}) endfunction() # Convenience target to have all Python targets buildable via one name add_custom_target(${PROJECT_NAME}) # 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") # 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(src/top_level_ecal) add_dependencies(${PROJECT_NAME} python_top_level) add_subdirectory(src/core) add_dependencies(${PROJECT_NAME} _ecal_core_py) add_subdirectory(src/nanobind_core/src) add_dependencies(${PROJECT_NAME} nanobind_core) add_subdirectory(src/ecalhdf5) add_dependencies(${PROJECT_NAME} _ecal_hdf5_py) add_subdirectory(src/msg) add_dependencies(${PROJECT_NAME} python_msg) if (ECAL_PYTHON_BUILD_SAMPLES) add_subdirectory(samples) endif() # This is a custom target to copy the eCAL core dll to the output directory of the Python extensions. # Without this copying step, debugging would not be possible. if (WIN32) add_custom_target(copy_ecal_core_dll ALL COMMAND cmake -E copy_if_different "$" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/python/ecal" COMMENT "Copy eCAL Core DLL to python directory" DEPENDS eCAL::core ) set_property(TARGET copy_ecal_core_dll PROPERTY FOLDER lang/python/core) endif()