Skip to content

Commit a8df7e3

Browse files
committed
major refactor to move the installation logic to the ament_package extension
Signed-off-by: Nadav Elkabets <[email protected]>
1 parent 2338dec commit a8df7e3

File tree

3 files changed

+219
-119
lines changed

3 files changed

+219
-119
lines changed

ament_cmake_python/ament_cmake_python-extras.cmake

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,17 @@ print(os.path.relpath(sysconfig.get_path('purelib', **kwargs), start='${CMAKE_IN
7979
endif()
8080
endmacro()
8181

82+
macro(_ament_cmake_python_register_extension_hook)
83+
if(NOT DEFINED AMENT_CMAKE_PYTHON_EXTENSION_REGISTERED)
84+
set(AMENT_CMAKE_PYTHON_EXTENSION_REGISTERED TRUE)
85+
86+
ament_register_extension(
87+
"ament_package"
88+
"ament_cmake_python"
89+
"ament_python_install_registered_packages.cmake")
90+
endif()
91+
endmacro()
92+
8293
include("${ament_cmake_python_DIR}/ament_python_install_module.cmake")
8394
include("${ament_cmake_python_DIR}/ament_python_install_package.cmake")
8495
include("${ament_cmake_python_DIR}/ament_get_python_install_dir.cmake")

ament_cmake_python/cmake/ament_python_install_package.cmake

Lines changed: 26 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
# :type SKIP_COMPILE: option
3636
#
3737
macro(ament_python_install_package)
38+
_ament_cmake_python_register_extension_hook()
3839
_ament_cmake_python_register_environment_hook()
3940
_ament_cmake_python_install_package(${ARGN})
4041
endmacro()
@@ -83,129 +84,35 @@ function(_ament_cmake_python_install_package package_name)
8384
set(ARG_DESTINATION ${PYTHON_INSTALL_DIR})
8485
endif()
8586

86-
set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/ament_cmake_python/${package_name}")
87-
88-
string(CONFIGURE "\
89-
from setuptools import find_packages
90-
from setuptools import setup
91-
92-
setup(
93-
name='${package_name}',
94-
version='${ARG_VERSION}',
95-
packages=find_packages(
96-
include=('${package_name}', '${package_name}.*')),
97-
)
98-
" setup_py_content)
99-
100-
file(GENERATE
101-
OUTPUT "${build_dir}/setup.py"
102-
CONTENT "${setup_py_content}"
103-
)
104-
105-
if(AMENT_CMAKE_SYMLINK_INSTALL)
106-
add_custom_target(
107-
ament_cmake_python_symlink_${package_name}
108-
COMMAND ${CMAKE_COMMAND} -E create_symlink
109-
"${ARG_PACKAGE_DIR}" "${build_dir}/${package_name}"
110-
)
111-
set(egg_dependencies ament_cmake_python_symlink_${package_name})
112-
113-
if(ARG_SETUP_CFG)
114-
add_custom_target(
115-
ament_cmake_python_symlink_${package_name}_setup
116-
COMMAND ${CMAKE_COMMAND} -E create_symlink
117-
"${ARG_SETUP_CFG}" "${build_dir}/setup.cfg"
118-
)
119-
list(APPEND egg_dependencies ament_cmake_python_symlink_${package_name}_setup)
120-
endif()
87+
get_property(_pkgs GLOBAL PROPERTY AMENT_CMAKE_PYTHON_PKGS)
88+
list(FIND _pkgs "${package_name}" _idx)
89+
if(_idx EQUAL -1)
90+
set_property(GLOBAL APPEND PROPERTY AMENT_CMAKE_PYTHON_PKGS "${package_name}")
12191
else()
122-
add_custom_target(
123-
ament_cmake_python_copy_${package_name}
124-
COMMAND ${CMAKE_COMMAND} -E copy_directory
125-
"${ARG_PACKAGE_DIR}" "${build_dir}/${package_name}"
126-
)
127-
set(egg_dependencies ament_cmake_python_copy_${package_name})
128-
129-
if(ARG_SETUP_CFG)
130-
add_custom_target(
131-
ament_cmake_python_copy_${package_name}_setup
132-
COMMAND ${CMAKE_COMMAND} -E copy
133-
"${ARG_SETUP_CFG}" "${build_dir}/setup.cfg"
134-
)
135-
list(APPEND egg_dependencies ament_cmake_python_copy_${package_name}_setup)
136-
endif()
92+
message(STATUS "ament_python_install_package: extending '${package_name}'")
13793
endif()
13894

139-
# Technically, we should call find_package(Python3) first to ensure that Python3::Interpreter
140-
# is available. But we skip this here because this macro requires ament_cmake, and ament_cmake
141-
# calls find_package(Python3) for us.
142-
get_executable_path(python_interpreter Python3::Interpreter BUILD)
143-
144-
add_custom_target(
145-
ament_cmake_python_build_${package_name}_egg ALL
146-
COMMAND ${python_interpreter} setup.py egg_info
147-
WORKING_DIRECTORY "${build_dir}"
148-
DEPENDS ${egg_dependencies}
149-
)
150-
151-
set(python_version "py${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}")
152-
153-
set(egg_name "${package_name}")
154-
set(egg_install_name "${egg_name}-${ARG_VERSION}")
155-
set(egg_install_name "${egg_install_name}-${python_version}")
156-
157-
install(
158-
DIRECTORY "${build_dir}/${egg_name}.egg-info/"
159-
DESTINATION "${ARG_DESTINATION}/${egg_install_name}.egg-info"
160-
)
161-
162-
if(ARG_SCRIPTS_DESTINATION)
163-
file(MAKE_DIRECTORY "${build_dir}/scripts") # setup.py may or may not create it
164-
165-
add_custom_target(
166-
ament_cmake_python_build_${package_name}_scripts ALL
167-
COMMAND ${python_interpreter} setup.py install_scripts -d scripts
168-
WORKING_DIRECTORY "${build_dir}"
169-
DEPENDS ${egg_dependencies}
170-
)
171-
172-
if(NOT AMENT_CMAKE_SYMLINK_INSTALL)
173-
# Not needed for nor supported by symlink installs
174-
set(_extra_install_args USE_SOURCE_PERMISSIONS)
175-
endif()
176-
177-
install(
178-
DIRECTORY "${build_dir}/scripts/"
179-
DESTINATION "${ARG_SCRIPTS_DESTINATION}/"
180-
${_extra_install_args}
181-
)
95+
get_property(_dirs GLOBAL PROPERTY AMENT_CMAKE_PYTHON_${package_name}_PACKAGE_DIRS)
96+
list(FIND _dirs "${ARG_PACKAGE_DIR}" _didx)
97+
if(_didx EQUAL -1)
98+
set_property(GLOBAL APPEND PROPERTY AMENT_CMAKE_PYTHON_${package_name}_PACKAGE_DIRS "${ARG_PACKAGE_DIR}")
99+
else()
100+
message(WARNING "duplicate PACKAGE_DIR for '${package_name}', skipping")
182101
endif()
183102

184-
install(
185-
DIRECTORY "${ARG_PACKAGE_DIR}/"
186-
DESTINATION "${ARG_DESTINATION}/${package_name}"
187-
PATTERN "*.pyc" EXCLUDE
188-
PATTERN "__pycache__" EXCLUDE
189-
)
190-
191-
if(NOT ARG_SKIP_COMPILE)
192-
get_executable_path(python_interpreter_config Python3::Interpreter CONFIGURE)
193-
# compile Python files
194-
install(CODE
195-
"execute_process(
196-
COMMAND
197-
\"${python_interpreter_config}\" \"-m\" \"compileall\"
198-
\"${CMAKE_INSTALL_PREFIX}/${ARG_DESTINATION}/${package_name}\"
199-
)"
200-
)
201-
endif()
103+
_ament_cmake_python_override(SKIP_COMPILE)
104+
_ament_cmake_python_override(VERSION)
105+
_ament_cmake_python_override(SETUP_CFG)
106+
_ament_cmake_python_override(DESTINATION)
107+
_ament_cmake_python_override(SCRIPTS_DESTINATION)
108+
endfunction()
202109

203-
if(package_name IN_LIST AMENT_CMAKE_PYTHON_INSTALL_INSTALLED_NAMES)
204-
message(FATAL_ERROR
205-
"ament_python_install_package() a Python module file or package with "
206-
"the same name '${package_name}' has been installed before")
110+
macro(_ament_cmake_python_override param)
111+
set(_prop "AMENT_CMAKE_PYTHON_${package_name}_${param}")
112+
set(_val "${ARG_${param}}")
113+
get_property(_old_val GLOBAL PROPERTY ${_prop})
114+
if(_old_val AND NOT _old_val STREQUAL "${_val}")
115+
message(WARNING "${param} for '${package_name}' changed from '${_old_val}' to '${_val}'")
207116
endif()
208-
list(APPEND AMENT_CMAKE_PYTHON_INSTALL_INSTALLED_NAMES "${package_name}")
209-
set(AMENT_CMAKE_PYTHON_INSTALL_INSTALLED_NAMES
210-
"${AMENT_CMAKE_PYTHON_INSTALL_INSTALLED_NAMES}" PARENT_SCOPE)
211-
endfunction()
117+
set_property(GLOBAL PROPERTY ${_prop} "${_val}")
118+
endmacro()
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
2+
function(ament_cmake_python_install_registered_packages)
3+
get_property(_pkgs GLOBAL PROPERTY AMENT_CMAKE_PYTHON_PKGS)
4+
foreach(pkg IN LISTS _pkgs)
5+
_ament_cmake_python_install_package_impl(${pkg})
6+
endforeach()
7+
endfunction()
8+
9+
function(_ament_cmake_python_install_package_impl package_name)
10+
foreach(_prop IN ITEMS SKIP_COMPILE VERSION SETUP_CFG DESTINATION SCRIPTS_DESTINATION PACKAGE_DIRS)
11+
get_property(_${_prop} GLOBAL PROPERTY AMENT_CMAKE_PYTHON_${package_name}_${_prop})
12+
endforeach()
13+
14+
_ament_cmake_python_prepare_build(${package_name})
15+
_ament_cmake_python_copy_or_symlink(${package_name})
16+
17+
# Technically, we should call find_package(Python3) first to ensure that Python3::Interpreter
18+
# is available. But we skip this here because this macro requires ament_cmake, and ament_cmake
19+
# calls find_package(Python3) for us.
20+
get_executable_path(python_interpreter Python3::Interpreter BUILD)
21+
22+
_ament_cmake_python_generate_egg(${package_name})
23+
24+
if(_SCRIPTS_DESTINATION)
25+
_ament_cmake_python_install_scripts(${package_name})
26+
endif()
27+
28+
_ament_cmake_python_install_sources(${package_name})
29+
30+
if(NOT _SKIP_COMPILE)
31+
_ament_cmake_python_byte_compile(${package_name})
32+
endif()
33+
34+
endfunction()
35+
36+
macro(_ament_cmake_python_prepare_build package_name)
37+
set(_build_dir "${CMAKE_CURRENT_BINARY_DIR}/ament_cmake_python/${package_name}")
38+
39+
string(CONFIGURE "\
40+
from setuptools import find_packages
41+
from setuptools import setup
42+
43+
setup(
44+
name='${package_name}',
45+
version='${_VERSION}',
46+
packages=find_packages(
47+
include=('${package_name}', '${package_name}.*')),
48+
)
49+
" setup_py_content)
50+
51+
file(GENERATE
52+
OUTPUT "${_build_dir}/setup.py"
53+
CONTENT "${setup_py_content}"
54+
)
55+
56+
endmacro()
57+
58+
macro(_ament_cmake_python_copy_or_symlink package_name)
59+
set(_sync_target "ament_cmake_python_sync_${package_name}")
60+
add_custom_target(${_sync_target})
61+
62+
foreach(_dir IN LISTS _PACKAGE_DIRS)
63+
file(GLOB_RECURSE _dir_files RELATIVE "${_dir}" "${_dir}/*")
64+
foreach(_rel IN LISTS _dir_files)
65+
set(_src "${_dir}/${_rel}")
66+
set(_dst "${_build_dir}/${package_name}/${_rel}")
67+
68+
get_filename_component(_dst_parent "${_dst}" DIRECTORY)
69+
file(MAKE_DIRECTORY "${_dst_parent}")
70+
71+
list(FIND _already_processed "${_dst}" _idx)
72+
if(NOT _idx EQUAL -1)
73+
continue()
74+
endif()
75+
list(APPEND _already_processed "${_dst}")
76+
77+
if(AMENT_CMAKE_SYMLINK_INSTALL)
78+
add_custom_command(
79+
OUTPUT "${_dst}"
80+
COMMAND ${CMAKE_COMMAND} -E create_symlink "${_src}" "${_dst}"
81+
DEPENDS "${_src}"
82+
COMMENT "Symlinking ${_rel}"
83+
VERBATIM
84+
)
85+
else()
86+
add_custom_command(
87+
OUTPUT "${_dst}"
88+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_src}" "${_dst}"
89+
DEPENDS "${_src}"
90+
COMMENT "Copying ${_rel}"
91+
VERBATIM
92+
)
93+
endif()
94+
endforeach()
95+
endforeach()
96+
97+
if(_SETUP_CFG)
98+
set(_cfg_dst "${_build_dir}/setup.cfg")
99+
if(AMENT_CMAKE_SYMLINK_INSTALL)
100+
set(_copy_cmd ${CMAKE_COMMAND} -E create_symlink "${_SETUP_CFG}" "${_cfg_dst}")
101+
else()
102+
set(_copy_cmd ${CMAKE_COMMAND} -E copy_if_different "${_SETUP_CFG}" "${_cfg_dst}")
103+
endif()
104+
105+
add_custom_command(
106+
OUTPUT "${_cfg_dst}"
107+
COMMAND ${_copy_cmd}
108+
DEPENDS "${_SETUP_CFG}"
109+
COMMENT "Synchronising setup.cfg"
110+
VERBATIM
111+
)
112+
add_custom_target(phony_${package_name}_cfg DEPENDS "${_cfg_dst}")
113+
add_dependencies(${_sync_target} phony_${package_name}_cfg)
114+
endif()
115+
endmacro()
116+
117+
macro(_ament_cmake_python_generate_egg package_name)
118+
add_custom_target(
119+
ament_cmake_python_build_${package_name}_egg ALL
120+
COMMAND ${python_interpreter} setup.py egg_info
121+
WORKING_DIRECTORY "${_build_dir}"
122+
DEPENDS ${_sync_target}
123+
)
124+
125+
set(python_version "py${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}")
126+
127+
set(egg_name "${package_name}")
128+
set(egg_install_name "${egg_name}-${_VERSION}")
129+
set(egg_install_name "${egg_install_name}-${python_version}")
130+
131+
install(
132+
DIRECTORY "${_build_dir}/${egg_name}.egg-info/"
133+
DESTINATION "${_DESTINATION}/${egg_install_name}.egg-info"
134+
)
135+
endmacro()
136+
137+
macro(_ament_cmake_python_install_scripts package_name)
138+
file(MAKE_DIRECTORY "${_build_dir}/scripts") # setup.py may or may not create it
139+
140+
add_custom_target(
141+
ament_cmake_python_build_${package_name}_scripts ALL
142+
COMMAND ${python_interpreter} setup.py install_scripts -d scripts
143+
WORKING_DIRECTORY "${_build_dir}"
144+
DEPENDS ${_sync_target}
145+
)
146+
147+
if(NOT AMENT_CMAKE_SYMLINK_INSTALL)
148+
# Not needed for nor supported by symlink installs
149+
set(_extra_install_args USE_SOURCE_PERMISSIONS)
150+
endif()
151+
152+
install(
153+
DIRECTORY "${_build_dir}/scripts/"
154+
DESTINATION "${_SCRIPTS_DESTINATION}/"
155+
${_extra_install_args}
156+
)
157+
endmacro()
158+
159+
macro(_ament_cmake_python_install_sources package_name)
160+
foreach(_dir IN LISTS _PACKAGE_DIRS)
161+
install(
162+
DIRECTORY "${_dir}/"
163+
DESTINATION "${_DESTINATION}/${package_name}"
164+
PATTERN "*.pyc" EXCLUDE
165+
PATTERN "__pycache__" EXCLUDE
166+
)
167+
endforeach()
168+
endmacro()
169+
170+
macro(_ament_cmake_python_byte_compile package_name)
171+
get_executable_path(python_interpreter_config Python3::Interpreter CONFIGURE)
172+
# compile Python files
173+
install(CODE
174+
"execute_process(
175+
COMMAND
176+
\"${python_interpreter_config}\" \"-m\" \"compileall\"
177+
\"${CMAKE_INSTALL_PREFIX}/${_DESTINATION}/${package_name}\"
178+
)"
179+
)
180+
endmacro()
181+
182+
ament_cmake_python_install_registered_packages()

0 commit comments

Comments
 (0)