|
| 1 | +# Glob the source files and then exclude files that don't make sense to be in the glob. |
| 2 | +# Note(wjwwood): we know this isn't the "right" way to do this, but it helps us catch |
| 3 | +# additions to the sdk when it is upgraded until we have a more structured approach |
| 4 | +# to extract build targets out of bazel. |
| 5 | +file(GLOB_RECURSE intrinsic_python_SRCS |
| 6 | + RELATIVE "${intrinsic_sdk_SOURCE_DIR}" |
| 7 | + "${intrinsic_sdk_SOURCE_DIR}/**/*.py" |
| 8 | +) |
| 9 | +list(FILTER intrinsic_python_SRCS EXCLUDE REGEX "/\\.github/") |
| 10 | +list(FILTER intrinsic_python_SRCS EXCLUDE REGEX "/examples/") |
| 11 | +list(FILTER intrinsic_python_SRCS EXCLUDE REGEX "_test\\.py$") |
| 12 | + |
| 13 | +# Overlay sdk Python files on top of generated protobuf Python files. |
| 14 | +set(python_sdk_destination_dir "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py") |
| 15 | +set(copied_python_sdk_files "") |
| 16 | +foreach(sdk_python_file ${intrinsic_python_SRCS}) |
| 17 | + # Ensure the target directory exists. |
| 18 | + get_filename_component(sdk_python_file_dir "${sdk_python_file}" DIRECTORY) |
| 19 | + set(python_sdk_file_dest_dir "${python_sdk_destination_dir}/${sdk_python_file_dir}") |
| 20 | + file(MAKE_DIRECTORY "${python_sdk_file_dest_dir}") |
| 21 | + # Ensure every python package (directory in the output) has at least an empty __init__.py. |
| 22 | + set(python_init_py_file "${python_sdk_file_dest_dir}/__init__.py") |
| 23 | + if(NOT EXISTS "${python_init_py_file}") |
| 24 | + file(TOUCH "${python_init_py_file}") |
| 25 | + endif() |
| 26 | + # Copy the file over. |
| 27 | + set(python_sdk_file_destination "${python_sdk_destination_dir}/${sdk_python_file}") |
| 28 | + file(COPY_FILE "${intrinsic_sdk_SOURCE_DIR}/${sdk_python_file}" "${python_sdk_file_destination}" ONLY_IF_DIFFERENT) |
| 29 | + list(APPEND copied_python_sdk_files "${python_sdk_file_destination}") |
| 30 | +endforeach() |
| 31 | + |
| 32 | +# Check to see if there are any collisions between the protobuf generation and copied files. |
| 33 | +foreach(copied_python_sdk_file ${copied_python_sdk_files}) |
| 34 | + if("${copied_python_sdk_file}" IN_LIST sdk_protos_python_sources) |
| 35 | + message(FATAL_ERROR "Python sdk file '${copied_python_sdk_file}' would be overwritten by protoc.") |
| 36 | + endif() |
| 37 | +endforeach() |
| 38 | + |
| 39 | +# Install select (for now) python packages. |
| 40 | +# Note(wjwwood): More work needs to be done to wrangle all of the generated protobuf code into |
| 41 | +# a single python project and/or distrbute them separately by namespace. |
| 42 | +set(python_packages_to_install |
| 43 | + # "google" # This one is disabled because it collides with google.protobuf which comes from elsewhere. |
| 44 | + "intrinsic" |
| 45 | + "protoc_gen_openapiv2" # This one should be fixed to have a better python package name |
| 46 | + # "src" # This one is disabled because it isn't being used atm and is a weird layout |
| 47 | + # "third_party" # This one contains protobuf versions of ROS messages and is unused atm |
| 48 | +) |
| 49 | + |
| 50 | +macro(_get_python_install_dir) |
| 51 | + if(NOT DEFINED PYTHON_INSTALL_DIR) |
| 52 | + # avoid storing backslash in cached variable since CMake will interpret it as escape character |
| 53 | + # This auto detection code uses the same logic as get_python_install_path() in colcon-core |
| 54 | + set(_python_code |
| 55 | + "\ |
| 56 | +import os |
| 57 | +import sysconfig |
| 58 | +schemes = sysconfig.get_scheme_names() |
| 59 | +kwargs = {'vars': {'base': '${CMAKE_INSTALL_PREFIX}'}} |
| 60 | +if 'deb_system' in schemes or 'osx_framework_library' in schemes: |
| 61 | + kwargs['scheme'] = 'posix_prefix' |
| 62 | +elif 'rpm_prefix' in schemes: |
| 63 | + kwargs['scheme'] = 'rpm_prefix' |
| 64 | +print(os.path.relpath(sysconfig.get_path('purelib', **kwargs), start='${CMAKE_INSTALL_PREFIX}').replace(os.sep, '/'))" |
| 65 | + ) |
| 66 | + get_executable_path(_python_interpreter Python3::Interpreter CONFIGURE) |
| 67 | + execute_process( |
| 68 | + COMMAND |
| 69 | + "${_python_interpreter}" |
| 70 | + "-c" |
| 71 | + "${_python_code}" |
| 72 | + OUTPUT_VARIABLE _output |
| 73 | + RESULT_VARIABLE _result |
| 74 | + OUTPUT_STRIP_TRAILING_WHITESPACE |
| 75 | + ) |
| 76 | + if(NOT _result EQUAL 0) |
| 77 | + message(FATAL_ERROR |
| 78 | + "execute_process(${_python_interpreter} -c '${_python_code}') returned " |
| 79 | + "error code ${_result}") |
| 80 | + endif() |
| 81 | + |
| 82 | + set(PYTHON_INSTALL_DIR |
| 83 | + "${_output}" |
| 84 | + CACHE INTERNAL |
| 85 | + "The directory for Python library installation. This needs to be in PYTHONPATH when 'setup.py install' is called.") |
| 86 | + endif() |
| 87 | +endmacro() |
| 88 | + |
| 89 | +_get_python_install_dir() |
| 90 | + |
| 91 | +function(_install_python_package package_name) |
| 92 | + cmake_parse_arguments( |
| 93 | + ARG "SKIP_COMPILE" "PACKAGE_DIR;DESTINATION" "" ${ARGN}) |
| 94 | + if(ARG_UNPARSED_ARGUMENTS) |
| 95 | + message(FATAL_ERROR "_install_python_package() called with unused " |
| 96 | + "arguments: ${ARG_UNPARSED_ARGUMENTS}") |
| 97 | + endif() |
| 98 | + |
| 99 | + if(NOT ARG_PACKAGE_DIR) |
| 100 | + message(FATAL_ERROR "ARG PACKAGE_DIR required") |
| 101 | + endif() |
| 102 | + |
| 103 | + if(NOT ARG_DESTINATION) |
| 104 | + if(NOT PYTHON_INSTALL_DIR) |
| 105 | + message(FATAL_ERROR "_install_python_package() variable 'PYTHON_INSTALL_DIR' must not be empty") |
| 106 | + endif() |
| 107 | + set(ARG_DESTINATION ${PYTHON_INSTALL_DIR}) |
| 108 | + endif() |
| 109 | + |
| 110 | + set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/_install_python_package/${package_name}") |
| 111 | + |
| 112 | + string(CONFIGURE "\ |
| 113 | +from setuptools import find_packages |
| 114 | +from setuptools import setup |
| 115 | +
|
| 116 | +setup( |
| 117 | + name='${package_name}', |
| 118 | + version='${sdk_version}', |
| 119 | + packages=find_packages( |
| 120 | + include=('${package_name}', '${package_name}.*')), |
| 121 | +) |
| 122 | +" setup_py_content) |
| 123 | + |
| 124 | + file(GENERATE |
| 125 | + OUTPUT "${build_dir}/setup.py" |
| 126 | + CONTENT "${setup_py_content}" |
| 127 | + ) |
| 128 | + |
| 129 | + add_custom_target( |
| 130 | + _install_python_package_copy_${package_name} |
| 131 | + COMMAND ${CMAKE_COMMAND} -E copy_directory |
| 132 | + "${ARG_PACKAGE_DIR}" "${build_dir}/${package_name}" |
| 133 | + ) |
| 134 | + set(egg_dependencies _install_python_package_copy_${package_name}) |
| 135 | + |
| 136 | + get_executable_path(python_interpreter Python3::Interpreter BUILD) |
| 137 | + |
| 138 | + add_custom_target( |
| 139 | + _install_python_package_build_${package_name}_egg ALL |
| 140 | + COMMAND ${python_interpreter} setup.py egg_info |
| 141 | + WORKING_DIRECTORY "${build_dir}" |
| 142 | + DEPENDS ${egg_dependencies} |
| 143 | + ) |
| 144 | + |
| 145 | + set(python_version "py${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}") |
| 146 | + |
| 147 | + set(egg_name "${package_name}") |
| 148 | + set(egg_install_name "${egg_name}-${ARG_VERSION}") |
| 149 | + set(egg_install_name "${egg_install_name}-${python_version}") |
| 150 | + |
| 151 | + install( |
| 152 | + DIRECTORY "${build_dir}/${egg_name}.egg-info/" |
| 153 | + DESTINATION "${ARG_DESTINATION}/${egg_install_name}.egg-info" |
| 154 | + ) |
| 155 | + |
| 156 | + install( |
| 157 | + DIRECTORY "${ARG_PACKAGE_DIR}/" |
| 158 | + DESTINATION "${ARG_DESTINATION}/${package_name}" |
| 159 | + PATTERN "*.pyc" EXCLUDE |
| 160 | + PATTERN "__pycache__" EXCLUDE |
| 161 | + ) |
| 162 | + |
| 163 | + if(NOT ARG_SKIP_COMPILE) |
| 164 | + get_executable_path(python_interpreter_config Python3::Interpreter CONFIGURE) |
| 165 | + # compile Python files |
| 166 | + install(CODE |
| 167 | + "execute_process( |
| 168 | + COMMAND |
| 169 | + \"${python_interpreter_config}\" \"-m\" \"compileall\" |
| 170 | + \"${CMAKE_INSTALL_PREFIX}/${ARG_DESTINATION}/${package_name}\" |
| 171 | + )" |
| 172 | + ) |
| 173 | + endif() |
| 174 | +endfunction() |
| 175 | + |
| 176 | +foreach(python_package_to_install ${python_packages_to_install}) |
| 177 | + _install_python_package( |
| 178 | + ${python_package_to_install} |
| 179 | + PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/protos_gen_py/${python_package_to_install}" |
| 180 | + ) |
| 181 | +endforeach() |
0 commit comments