diff --git a/.gitignore b/.gitignore index e69de29..e43b0f9 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/grpc_vendor/CMakeLists.txt b/grpc_vendor/CMakeLists.txt index b85e575..8eba8c8 100644 --- a/grpc_vendor/CMakeLists.txt +++ b/grpc_vendor/CMakeLists.txt @@ -34,6 +34,7 @@ ament_vendor(grpc_vendor -DgRPC_BUILD_NODE_PLUGIN:BOOL=OFF -DgRPC_BUILD_OBJECTIVEC_PLUGIN:BOOL=OFF -DgRPC_BUILD_RUBY_PLUGIN:BOOL=OFF + -DgRPC_BUILD_GRPC_PYTHON_PLUGIN=ON -DgRPC_PROTOBUF_PROVIDER:STRING=module -DPROTOBUF_ROOT_DIR=${PROTOBUF_ROOT_DIR} ) diff --git a/intrinsic_sdk_cmake/CMakeLists.txt b/intrinsic_sdk_cmake/CMakeLists.txt index 652e626..0cc80c6 100644 --- a/intrinsic_sdk_cmake/CMakeLists.txt +++ b/intrinsic_sdk_cmake/CMakeLists.txt @@ -34,6 +34,9 @@ include(cmake/sdk_imw_zenoh.cmake) # Build the sdk C++ sources into a library. include(cmake/sdk.cmake) +# Collect and install the sdk Python sources. +include(cmake/sdk_python.cmake) + # Extract tool binaries from bazel build of sdk. include(cmake/sdk_tools.cmake) diff --git a/intrinsic_sdk_cmake/cmake/find_replace_fix_imports.cmake b/intrinsic_sdk_cmake/cmake/find_replace_fix_imports.cmake new file mode 100644 index 0000000..1298d99 --- /dev/null +++ b/intrinsic_sdk_cmake/cmake/find_replace_fix_imports.cmake @@ -0,0 +1,46 @@ +set(python_package_directory ${PYTHON_PACKAGE_DIRECTORY}) +set(python_package_to_install_package_dir + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/${python_package_directory}") +set(python_package_to_install_package_dir_fixed + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/fixed_imports/${python_package_directory}") + +file(GLOB_RECURSE python_package_sources + RELATIVE "${python_package_to_install_package_dir}" + "${python_package_to_install_package_dir}/**/*.py" +) + +set(FIND_LIST + "from google.api" + "from google.longrunning" + "from google.rpc" + "from google.type" +) +set(REPLACE_LIST + "from googleapis.google.api" + "from googleapis.google.longrunning" + "from googleapis.google.rpc" + "from googleapis.google.type" +) + +foreach(python_package_source ${python_package_sources}) + set(source_file "${python_package_to_install_package_dir}/${python_package_source}") + set(target_file "${python_package_to_install_package_dir_fixed}/${python_package_source}") + + file(READ "${source_file}" contents) + + list(LENGTH FIND_LIST len1) + math(EXPR len2 "${len1} - 1") + + foreach(val RANGE ${len2}) + list(GET FIND_LIST ${val} find) + list(GET REPLACE_LIST ${val} replace) + + message(STATUS "'${find}' -> '${replace}' in '${source_file}'") + string(REPLACE "${find}" "${replace}" contents "${contents}") + endforeach() + + get_filename_component(target_file_dir "${target_file}" DIRECTORY) + file(MAKE_DIRECTORY "${target_file_dir}") + + file(WRITE "${target_file}" "${contents}") +endforeach() diff --git a/intrinsic_sdk_cmake/cmake/sdk_protos.cmake b/intrinsic_sdk_cmake/cmake/sdk_protos.cmake index 2d65f1c..b02200f 100644 --- a/intrinsic_sdk_cmake/cmake/sdk_protos.cmake +++ b/intrinsic_sdk_cmake/cmake/sdk_protos.cmake @@ -1,10 +1,11 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/protos_gen) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py) file(GLOB_RECURSE intrinsic_proto_SRCS "${intrinsic_sdk_SOURCE_DIR}/**/*.proto") list(FILTER intrinsic_proto_SRCS EXCLUDE REGEX "_test\\.proto$") set(exclude_SRCS - intrinsic/icon/proto/service.proto + intrinsic/icon/proto/service.proto third_party/ros2/ros_interfaces/jazzy/diagnostic_msgs/srv/self_test.proto third_party/ros2/ros_interfaces/jazzy/diagnostic_msgs/srv/add_diagnostics.proto third_party/ros2/ros_interfaces/jazzy/rcl_interfaces/srv/set_parameters.proto @@ -77,22 +78,28 @@ set(grpc_gateway_SRCS ${grpc_gateway_SOURCE_DIR}/protoc-gen-openapiv2/options/openapiv2.proto ) -# Generate code from protos and build into a library. -add_library(intrinsic_sdk_protos STATIC +set(sdk_protos ${intrinsic_proto_SRCS} ${googleapis_SRCS} ${grpc_gateway_SRCS} ${grpc_SRCS} ) +set(sdk_proto_import_dirs + ${intrinsic_sdk_SOURCE_DIR} + ${googleapis_SOURCE_DIR} + ${grpc_gateway_SOURCE_DIR} + ${grpc_SOURCE_DIR} +) + +# Generate code from protos and build into a library. +add_library(intrinsic_sdk_protos STATIC + ${sdk_protos} +) set_property(TARGET intrinsic_sdk_protos PROPERTY POSITION_INDEPENDENT_CODE ON) protobuf_generate( TARGET intrinsic_sdk_protos LANGUAGE cpp - IMPORT_DIRS - ${intrinsic_sdk_SOURCE_DIR} - ${googleapis_SOURCE_DIR} - ${grpc_gateway_SOURCE_DIR} - ${grpc_SOURCE_DIR} + IMPORT_DIRS ${sdk_proto_import_dirs} PROTOC_OPTIONS --experimental_editions PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/protos_gen ) @@ -114,11 +121,7 @@ protobuf_generate( TARGET intrinsic_sdk_services LANGUAGE grpc PLUGIN protoc-gen-grpc=$ - IMPORT_DIRS - ${intrinsic_sdk_SOURCE_DIR} - ${googleapis_SOURCE_DIR} - ${grpc_gateway_SOURCE_DIR} - ${grpc_SOURCE_DIR} + IMPORT_DIRS ${sdk_proto_import_dirs} PROTOC_OPTIONS --experimental_editions PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/protos_gen GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc @@ -131,6 +134,158 @@ target_include_directories(intrinsic_sdk_services target_link_libraries(intrinsic_sdk_services PUBLIC intrinsic_sdk_protos) set_property(TARGET intrinsic_sdk_services PROPERTY POSITION_INDEPENDENT_CODE ON) +# Generate Python code from the protos. +# add_custom_target(intrinsic_sdk_protos_python ALL DEPENDS ${sdk_protos}) +# protobuf_generate( +# TARGET intrinsic_sdk_protos_python +# PLUGIN protoc-gen-grpc=$ +# LANGUAGE python +# PROTOS ${sdk_protos} +# IMPORT_DIRS ${sdk_proto_import_dirs} +# PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py +# OUT_VAR sdk_protos_python_sources_maybe_hyphens +# ) +# Use the protoc from the grpcio-tools Python package to generate _pb2.py and _pb2_grpc.py files. +set(venv_dir "${CMAKE_CURRENT_BINARY_DIR}/grpc_venv") +if(NOT EXISTS "${venv_dir}") + execute_process( + COMMAND "${Python3_EXECUTABLE}" -m venv "${venv_dir}" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + RESULT_VARIABLE VENV_CREATE_RESULT + OUTPUT_VARIABLE VENV_CREATE_OUTPUT + ERROR_VARIABLE VENV_CREATE_ERROR + ) + if(NOT VENV_CREATE_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to create virtual environment: ${VENV_CREATE_ERROR}") + endif() + + execute_process( + COMMAND "${venv_dir}/bin/pip" install -U grpcio-tools + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + RESULT_VARIABLE PIP_INSTALL_RESULT + OUTPUT_VARIABLE PIP_INSTALL_OUTPUT + ERROR_VARIABLE PIP_INSTALL_ERROR + ) + if(NOT PIP_INSTALL_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to install Python dependencies: ${PIP_INSTALL_ERROR}") + endif() +endif() +set(protoc_include_flags) +foreach(sdk_proto_import_dir ${sdk_proto_import_dirs}) + list(APPEND protoc_include_flags -I "${sdk_proto_import_dir}") +endforeach() +set(protoc_generated_files) +foreach(sdk_proto ${sdk_protos}) + set(_proto ${sdk_proto}) + get_filename_component(_abs_file ${_proto} ABSOLUTE) + get_filename_component(_abs_dir ${_abs_file} DIRECTORY) + get_filename_component(_file_full_name ${_proto} NAME) + string(FIND "${_file_full_name}" "." _file_last_ext_pos REVERSE) + string(SUBSTRING "${_file_full_name}" 0 ${_file_last_ext_pos} _basename) + set(_suitable_include_found FALSE) + foreach(DIR ${sdk_proto_import_dirs}) + if(NOT DIR STREQUAL "-I") + file(RELATIVE_PATH _rel_dir ${DIR} ${_abs_dir}) + if(_rel_dir STREQUAL _abs_dir) + # When there is no relative path from DIR to _abs_dir (e.g. due to + # different drive letters on Windows), _rel_dir is equal to _abs_dir. + # Therefore, DIR is not a suitable include path and must be skipped. + continue() + endif() + string(FIND "${_rel_dir}" "../" _is_in_parent_folder) + if (NOT ${_is_in_parent_folder} EQUAL 0) + set(_suitable_include_found TRUE) + break() + endif() + endif() + endforeach() + if(NOT _suitable_include_found) + message(FATAL_ERROR "Error: could not find any correct proto include directory: ${_proto}") + endif() + set(_proto_generated_files) + list(APPEND _proto_generated_files + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/${_rel_dir}/${_basename}_pb2.py") + list(APPEND _proto_generated_files + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/${_rel_dir}/${_basename}_pb2_grpc.py") + list(APPEND protoc_generated_files ${_proto_generated_files}) + add_custom_command( + OUTPUT ${_proto_generated_files} + DEPENDS "${sdk_proto}" + COMMAND + "${venv_dir}/bin/python3" + -m grpc_tools.protoc + ${protoc_include_flags} + --python_out="${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py" + --grpc_python_out="${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py" + "${sdk_proto}" + COMMENT "Using 'grpc_tools.protoc' to generate Python code for '${sdk_proto}'..." + ) +endforeach() +add_custom_target(intrinsic_sdk_protos_python ALL DEPENDS ${sdk_protos} ${protoc_generated_files}) +set(sdk_protos_python_sources_maybe_hyphens ${protoc_generated_files}) +# Create empty __init__.py files for each python package generated by protoc. +set(sdk_protos_python_sources "") +set(python_package_directories "") +foreach(generated_python_file_maybe_hyphen ${sdk_protos_python_sources_maybe_hyphens}) + file(RELATIVE_PATH rel_generated_python_file_maybe_hyphen + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py" + "${generated_python_file_maybe_hyphen}" + ) + string(REPLACE "-" "_" rel_generated_python_file "${rel_generated_python_file_maybe_hyphen}") + set(generated_python_file "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/${rel_generated_python_file}") + list(APPEND sdk_protos_python_sources "${generated_python_file}") + get_filename_component(generated_python_file_dir "${generated_python_file}" DIRECTORY) + list(APPEND python_package_directories "${generated_python_file_dir}") +endforeach() +list(REMOVE_DUPLICATES python_package_directories) +set(python_init_py_files "") +# add __init__.py to "leaf" directories +foreach(python_package_directory ${python_package_directories}) + file(MAKE_DIRECTORY "${python_package_directory}") + set(python_init_py_file "${python_package_directory}/__init__.py") + list(APPEND python_init_py_files "${python_init_py_file}") + file(TOUCH "${python_init_py_file}") +endforeach() +# second pass to add __init__.py to intermediate directories now that the folder structure exists. +file(GLOB_RECURSE leaf_python_init_files_and_dirs + LIST_DIRECTORIES true + RELATIVE "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/" + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/*/__init__.py" +) +foreach(leaf_python_init_file_or_dir ${leaf_python_init_files_and_dirs}) + if(IS_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/${leaf_python_init_file_or_dir}") + set(python_init_py_file "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/${leaf_python_init_file_or_dir}/__init__.py") + list(APPEND python_init_py_files "${python_init_py_file}") + file(TOUCH "${python_init_py_file}") + endif() +endforeach() +# final pass because glob doesn't get the root folders correctly. +file(GLOB root_directories + LIST_DIRECTORIES true + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/*" +) +foreach(root_directory ${root_directories}) + if(IS_DIRECTORY "${root_directory}") + set(python_init_py_file "${root_directory}/__init__.py") + list(APPEND python_init_py_files "${python_init_py_file}") + file(TOUCH "${python_init_py_file}") + endif() +endforeach() +list(REMOVE_DUPLICATES python_init_py_files) + +# add_custom_target(intrinsic_sdk_protos_python_grpc ALL DEPENDS ${sdk_protos}) +# foreach(sdk_protos_python_source ${sdk_protos_python_sources}) +# string(REPLACE "_pb2.py" "_pb2_grpc.py" grpc_version "${sdk_protos_python_source}") +# add_custom_command( +# TARGET intrinsic_sdk_protos_python_grpc POST_BUILD +# DEPENDS "${sdk_protos_python_source}" intrinsic_sdk_protos_python +# COMMAND ${CMAKE_COMMAND} -E copy "${sdk_protos_python_source}" "${grpc_version}" +# COMMENT "Duplicating '${sdk_protos_python_source}' as '${grpc_version}'..." +# ) +# endforeach() + +# Generated Python code is installed with the Python sdk files later. + # Generate a descriptor set. add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/intrinsic_proto.desc diff --git a/intrinsic_sdk_cmake/cmake/sdk_python.cmake b/intrinsic_sdk_cmake/cmake/sdk_python.cmake new file mode 100644 index 0000000..915ca96 --- /dev/null +++ b/intrinsic_sdk_cmake/cmake/sdk_python.cmake @@ -0,0 +1,255 @@ +# set(python_proto_output_dir "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_grpc_tools_py") + +# set(python_proto_include_flags "") +# foreach(sdk_proto_import_dir ${sdk_proto_import_dirs}) +# list(APPEND python_proto_include_flags "-I${sdk_proto_import_dir}") +# endforeach() +# list(REMOVE_DUPLICATES python_proto_include_flags) + +# set(python_proto_outputs "") +# foreach(sdk_proto ${sdk_protos}) + +# endforeach() + +# add_custom_command( +# COMMAND python3 +# -m grpc_tools.protoc +# ${python_proto_include_flags} +# --python_out="${python_proto_output_dir}" +# --pyi_out="${python_proto_output_dir}" +# --grpc_python_out="${python_proto_output_dir}" +# ${sdk_protos} +# ) + +# protobuf_generate( +# LANGUAGE python +# PROTOS ${sdk_protos} +# IMPORT_DIRS ${sdk_proto_import_dirs} +# PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py2 +# OUT_VAR sdk_protos_python_sources2 +# PROTOC_EXE python3 -m grpc_tools.protoc +# ) + +# Namespace the googleapis in Python to avoid collision with google.protobuf. +# This does require some rewriting of python files in the sdk. +set(python_proto_gen_dir "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py") +set(renamed_googleapis_dir "${python_proto_gen_dir}/googleapis") +add_custom_target(intrinsic_sdk_protos_python_rename_googleapis + DEPENDS intrinsic_sdk_protos_python + COMMAND + ${CMAKE_COMMAND} + -E copy_directory_if_different + "${python_proto_gen_dir}/google" + "${renamed_googleapis_dir}/" + COMMAND + ${CMAKE_COMMAND} + -E touch + "${renamed_googleapis_dir}/__init__.py" + COMMENT "Copying 'google' protobufs into 'googleapis' namespace..." +) + +# Glob the source files and then exclude files that don't make sense to be in the glob. +# Note(wjwwood): we know this isn't the "right" way to do this, but it helps us catch +# additions to the sdk when it is upgraded until we have a more structured approach +# to extract build targets out of bazel. +file(GLOB_RECURSE intrinsic_python_SRCS + RELATIVE "${intrinsic_sdk_SOURCE_DIR}" + "${intrinsic_sdk_SOURCE_DIR}/**/*.py" +) +list(FILTER intrinsic_python_SRCS EXCLUDE REGEX "/\\.github/") +list(FILTER intrinsic_python_SRCS EXCLUDE REGEX "/examples/") +list(FILTER intrinsic_python_SRCS EXCLUDE REGEX "_test\\.py$") + +# Overlay sdk Python files on top of generated protobuf Python files. +set(copied_python_sdk_files "") +foreach(sdk_python_file ${intrinsic_python_SRCS}) + # Ensure the target directory exists. + set(python_sdk_destination_dir "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py") + get_filename_component(sdk_python_file_dir "${sdk_python_file}" DIRECTORY) + set(python_sdk_file_dest_dir "${python_sdk_destination_dir}/${sdk_python_file_dir}") + file(MAKE_DIRECTORY "${python_sdk_file_dest_dir}") + # Ensure every python package (directory in the output) has at least an empty __init__.py. + set(python_init_py_file "${python_sdk_file_dest_dir}/__init__.py") + if(NOT EXISTS "${python_init_py_file}") + file(TOUCH "${python_init_py_file}") + endif() + # Copy the file over. + set(python_sdk_file_destination "${python_sdk_destination_dir}/${sdk_python_file}") + file(COPY_FILE "${intrinsic_sdk_SOURCE_DIR}/${sdk_python_file}" "${python_sdk_file_destination}" ONLY_IF_DIFFERENT) + list(APPEND copied_python_sdk_files "${python_sdk_file_destination}") +endforeach() + +# Check to see if there are any collisions between the protobuf generation and copied files. +foreach(copied_python_sdk_file ${copied_python_sdk_files}) + if("${copied_python_sdk_file}" IN_LIST sdk_protos_python_sources) + message(FATAL_ERROR "Python sdk file '${copied_python_sdk_file}' would be overwritten by protoc.") + endif() +endforeach() + +# Install select (for now) python packages. +# Note(wjwwood): More work needs to be done to wrangle all of the generated protobuf code into +# a single python project and/or distrbute them separately by namespace. +set(python_packages_to_install + "googleapis" # This one is a re-namespacing of the generated googleapis proto python packages + "intrinsic" + "protoc_gen_openapiv2" # This one should be fixed to have a better python package name + # "src" # This one is disabled because it isn't being used atm and is a weird layout + # "third_party" # This one contains protobuf versions of ROS messages and is unused atm +) + +macro(_get_python_install_dir) + if(NOT DEFINED PYTHON_INSTALL_DIR) + # avoid storing backslash in cached variable since CMake will interpret it as escape character + # This auto detection code uses the same logic as get_python_install_path() in colcon-core + set(_python_code + "\ +import os +import sysconfig +schemes = sysconfig.get_scheme_names() +kwargs = {'vars': {'base': '${CMAKE_INSTALL_PREFIX}'}} +if 'deb_system' in schemes or 'osx_framework_library' in schemes: + kwargs['scheme'] = 'posix_prefix' +elif 'rpm_prefix' in schemes: + kwargs['scheme'] = 'rpm_prefix' +print(os.path.relpath(sysconfig.get_path('purelib', **kwargs), start='${CMAKE_INSTALL_PREFIX}').replace(os.sep, '/'))" + ) + get_executable_path(_python_interpreter Python3::Interpreter CONFIGURE) + execute_process( + COMMAND + "${_python_interpreter}" + "-c" + "${_python_code}" + OUTPUT_VARIABLE _output + RESULT_VARIABLE _result + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT _result EQUAL 0) + message(FATAL_ERROR + "execute_process(${_python_interpreter} -c '${_python_code}') returned " + "error code ${_result}") + endif() + + set(PYTHON_INSTALL_DIR + "${_output}" + CACHE INTERNAL + "The directory for Python library installation. This needs to be in PYTHONPATH when 'setup.py install' is called.") + endif() +endmacro() + +_get_python_install_dir() + +function(_install_python_package package_name) + cmake_parse_arguments( + ARG "SKIP_COMPILE" "PACKAGE_DIR;DESTINATION" "DEPENDS" ${ARGN}) + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "_install_python_package() called with unused " + "arguments: ${ARG_UNPARSED_ARGUMENTS}") + endif() + + if(NOT ARG_PACKAGE_DIR) + message(FATAL_ERROR "ARG PACKAGE_DIR required") + endif() + + if(NOT ARG_DESTINATION) + if(NOT PYTHON_INSTALL_DIR) + message(FATAL_ERROR "_install_python_package() variable 'PYTHON_INSTALL_DIR' must not be empty") + endif() + set(ARG_DESTINATION ${PYTHON_INSTALL_DIR}) + endif() + + set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/_install_python_package/${package_name}") + + string(CONFIGURE "\ +from setuptools import find_packages +from setuptools import setup + +setup( + name='${package_name}', + version='${sdk_version}', + packages=find_packages( + include=('${package_name}', '${package_name}.*')), +) +" setup_py_content) + + file(GENERATE + OUTPUT "${build_dir}/setup.py" + CONTENT "${setup_py_content}" + ) + + add_custom_target( + _install_python_package_copy_${package_name} + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${ARG_PACKAGE_DIR}" "${build_dir}/${package_name}" + DEPENDS ${ARG_DEPENDS} + ) + set(egg_dependencies _install_python_package_copy_${package_name}) + + get_executable_path(python_interpreter Python3::Interpreter BUILD) + + add_custom_target( + _install_python_package_build_${package_name}_egg ALL + COMMAND ${python_interpreter} setup.py egg_info + WORKING_DIRECTORY "${build_dir}" + DEPENDS ${egg_dependencies} + ) + + set(python_version "py${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}") + + set(egg_name "${package_name}") + set(egg_install_name "${egg_name}-${ARG_VERSION}") + set(egg_install_name "${egg_install_name}-${python_version}") + + install( + DIRECTORY "${build_dir}/${egg_name}.egg-info/" + DESTINATION "${ARG_DESTINATION}/${egg_install_name}.egg-info" + ) + + install( + DIRECTORY "${ARG_PACKAGE_DIR}/" + DESTINATION "${ARG_DESTINATION}/${package_name}" + PATTERN "*.pyc" EXCLUDE + PATTERN "__pycache__" EXCLUDE + ) + + if(NOT ARG_SKIP_COMPILE) + get_executable_path(python_interpreter_config Python3::Interpreter CONFIGURE) + # compile Python files + install(CODE + "execute_process( + COMMAND + \"${python_interpreter_config}\" \"-m\" \"compileall\" + \"${CMAKE_INSTALL_PREFIX}/${ARG_DESTINATION}/${package_name}\" + )" + ) + endif() +endfunction() + +foreach(python_package_to_install ${python_packages_to_install}) + set(python_package_to_install_package_dir + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/${python_package_to_install}") + set(python_package_to_install_package_dir_fixed + "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/fixed_imports/${python_package_to_install}") + + # before installing the python files, rewrite them to use the namespaced googleapi package + set(script "${CMAKE_CURRENT_SOURCE_DIR}/cmake/find_replace_fix_imports.cmake") + add_custom_target(fix_imports_before_install_${python_package_to_install} + DEPENDS + ${copied_python_sdk_files} + ${sdk_protos_python_sources} + "${script}" + intrinsic_sdk_protos_python + # intrinsic_sdk_protos_python_grpc + intrinsic_sdk_protos_python_rename_googleapis + COMMAND + ${CMAKE_COMMAND} + -DPYTHON_PACKAGE_DIRECTORY="${python_package_to_install}" + -P "${script}" + COMMENT "Fixing python import statements for '${python_package_to_install}'..." + ) + + _install_python_package( + ${python_package_to_install} + PACKAGE_DIR "${python_package_to_install_package_dir_fixed}" + DEPENDS fix_imports_before_install_${python_package_to_install} + ) +endforeach()