Skip to content

Commit 0ed56d3

Browse files
authored
Fix cmake program wrap (#9)
* Fix broken ament_install_python function The upstream package had copied this from catkin_virtualenv and updated it for ament. But the templates were not copied, and some of the cmake variables were broken and not correct. This patch fixes this by copying the install template, fixing the broken variables, and removing the "devel" logic as this doesn't apply to ROS2 builds. * Revert "Remove catkin_pkg in favor of ament_index_python (#4)" This reverts commit 8cbdec1. * Revert "Fix find_in_workspaces to work with bundle prefix paths (#3)" This reverts commit 43a3c4d. * Resolve workspace paths before searching workspaces The workspace paths included the project name and this directory may not even exist yet. This caused os.walk() to return an empty list. Fix this by resolving the paths first to remove any ".." paths and in turn remove the project name from the overall workspace path. * Fix workspace path checks, and support bundle paths
1 parent 969f027 commit 0ed56d3

File tree

5 files changed

+128
-24
lines changed

5 files changed

+128
-24
lines changed

ament_cmake_virtualenv/cmake/ament_install_python.cmake

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,12 @@ will not work as expected.")
5252
)
5353
endif()
5454

55-
set(program_install_location ${AMENT_PACKAGE_SHARE_DESTINATION}/ament_virtualenv_scripts)
56-
57-
# For devel-space support, we generate a bash script that invokes the source script via the virtualenv's
58-
# python interpreter.
59-
set(devel_program ${AMENT_DEVEL_PREFIX}/${ARG_DESTINATION}/${program_basename})
60-
configure_file(${ament_virtualenv_CMAKE_DIR}/templates/program.devel.in ${devel_program})
61-
execute_process(
62-
COMMAND ${AMENT_ENV} chmod +x ${devel_program}
63-
)
55+
set(program_install_location ament_virtualenv_scripts)
6456

6557
# For install-space support, we install the source script, and then generate a bash script to invoke it using
6658
# the virtualenv's python interpreter.
6759
set(install_program ${CMAKE_BINARY_DIR}/${program_basename})
68-
configure_file(${ament_virtualenv_CMAKE_DIR}/templates/program.install.in ${install_program})
60+
configure_file(${ament_cmake_virtualenv_DIR}/templates/program.install.in ${install_program})
6961
execute_process(
7062
COMMAND ${AMENT_ENV} chmod +x ${install_program}
7163
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env bash
2+
3+
if [ "${ARG_RENAME_PROCESS}" = "TRUE" ]; then
4+
exec ${${PROJECT_NAME}_VENV_INSTALL_DIR}/bin/python - "$@" <<- EOF
5+
import re
6+
import sys
7+
8+
from setproctitle import setproctitle
9+
10+
program_path = "${CMAKE_INSTALL_PREFIX}/${program_install_location}/${program_basename}"
11+
setproctitle(' '.join([program_path] + sys.argv[1:]))
12+
exec(open(program_path).read())
13+
EOF
14+
else
15+
exec ${${PROJECT_NAME}_VENV_INSTALL_DIR}/bin/python ${CMAKE_INSTALL_PREFIX}/${program_install_location}/${program_basename} "$@"
16+
fi
17+

ament_virtualenv/ament_virtualenv/glob_requirements.py

Lines changed: 109 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import sys
2727
import os
2828

29+
from pathlib import Path
2930
from typing import List
3031
from catkin_pkg.package import Package
3132
from ament_index_python.packages import get_package_share_directory, PackageNotFoundError
@@ -44,19 +45,108 @@
4445
from Queue import Queue
4546

4647

48+
def find_in_workspaces(project, file, workspaces=[]):
49+
# Add default workspace search paths
50+
ament_paths = os.environ.get('AMENT_PREFIX_PATH')
51+
if ament_paths is not None:
52+
# AMENT_PREFIX_PATH points at <prefix>/install
53+
ament_paths = ament_paths.split(os.pathsep)
54+
for path in ament_paths:
55+
if ((os.path.sep + 'install') in path or
56+
(os.path.sep + 'install_isolated') in path):
57+
workspaces.append(os.path.join(path, '..'))
58+
workspaces.append(os.path.join(path, '..', '..', 'src'))
59+
break
60+
if len(workspaces) == 0:
61+
# if AMENT_PREFIX_PATH wasn't set, we can fall back on
62+
# CMAKE_PREFIX_PATH (should contain the same information)
63+
cmake_paths = os.environ.get('CMAKE_PREFIX_PATH')
64+
if cmake_paths is not None:
65+
# CMAKE_PREFIX_PATH points at <prefix>/install or <prefix>/install_isolated
66+
cmake_paths = cmake_paths.split(os.pathsep)
67+
for path in cmake_paths:
68+
if ((os.path.sep + 'install') in path or
69+
(os.path.sep + 'install_isolated') in path):
70+
workspaces.append(os.path.join(path, '..'))
71+
workspaces.append(os.path.join(path, '..', '..', 'src'))
72+
break
73+
if len(workspaces) == 0:
74+
# COLCON_PREFIX_PATH points to the `install/` directory,
75+
# which is fine when ament_python is used as build tool
76+
# (ament_python copies the files right away),
77+
# but ament_cmake does not copy the files until after the
78+
# build, which is too late. So for ament_cmake we also
79+
# need to add the neighboring `src/` folder to the seach
80+
# (eg.: `install/../src/`)
81+
colcon_paths = os.environ.get('COLCON_PREFIX_PATH')
82+
if colcon_paths is not None:
83+
colcon_paths = colcon_paths.split(os.pathsep)
84+
for path in colcon_paths:
85+
if (os.path.sep + 'install') in path or (os.path.sep + 'install_isolated') in path:
86+
workspaces.append(path)
87+
workspaces.append(os.path.join(path, '..', 'src'))
88+
if len(workspaces) == 0:
89+
# final (local) fallback: use working directory (usually src/<package>)
90+
path = os.getcwd()
91+
if (os.path.sep + 'src') in path:
92+
workspaces.append(path)
93+
94+
# Above, all paths required an "install/" or "src/" folder in order to qualify
95+
# as a workspace. This only applies to local workspaces but does not take into
96+
# account any installed bundles. We should prefer local workspaces, but we
97+
# should also include the bundle path in case the workspace found above either
98+
# doesn't contain the package in question or if its ignored. Since the first
99+
# match is used we are safe to append the bundle path unconditionally.
100+
try:
101+
path = get_package_share_directory(project)
102+
workspaces.append(path)
103+
except PackageNotFoundError:
104+
pass
105+
106+
if len(workspaces) == 0:
107+
raise RuntimeError(
108+
"[ament_virtualenv] Failed to find any workspaces." +
109+
"\nAMENT_PREFIX_PATH=" + os.environ.get('AMENT_PREFIX_PATH', 'NOT SET') +
110+
"\nCMAKE_PREFIX_PATH=" + os.environ.get('CMAKE_PREFIX_PATH', 'NOT SET') +
111+
"\nCOLCON_PREFIX_PATH=" + os.environ.get('COLCON_PREFIX_PATH', 'NOT SET') +
112+
"\nCWD=" + os.getcwd()
113+
)
114+
115+
# The paths in "workspaces" will look something like (depending on logic above)
116+
# <prefix>/install/<project>/../
117+
# The issue here is <project> dir may not actually exist so below when we walk the
118+
# directories it will ignore that folder since it doesn't exist. To fix this we
119+
# need to resolve the paths so they point to valid directories, ignoring the di
120+
# of the packages we're building which may not exist.
121+
workspaces = [str(Path(path).resolve()) for path in workspaces]
122+
123+
# now search the workspaces
124+
for workspace in (workspaces or []):
125+
for d, dirs, files in os.walk(workspace, topdown=True, followlinks=True):
126+
if (('CATKIN_IGNORE' in files) or
127+
('COLCON_IGNORE' in files) or
128+
('AMENT_IGNORE' in files)):
129+
del dirs[:]
130+
continue
131+
dirname = os.path.basename(d)
132+
if dirname == project and file in files:
133+
return os.path.join(workspace, d, file)
134+
# none found:
135+
return None
136+
#
137+
138+
47139
AMENT_VIRTUALENV_TAGNAME = "pip_requirements"
48140

49141

50142
def parse_exported_requirements(package: Package) -> List[str]:
51143
requirements_list = []
52144
for export in package.exports:
53145
if export.tagname == AMENT_VIRTUALENV_TAGNAME:
54-
package_path = get_package_share_directory(package.name)
55-
if os.path.exists(f"{package_path}/{export.content}"):
56-
requirements_path = f"{package_path}/{export.content}"
57-
else:
58-
requirements_path = None
59-
146+
requirements_path = find_in_workspaces(
147+
project=package.name,
148+
file=export.content
149+
)
60150
if not requirements_path:
61151
print(
62152
("[ERROR] ament_virtualenv "
@@ -75,12 +165,19 @@ def parse_exported_requirements(package: Package) -> List[str]:
75165

76166
def process_package(package_name, soft_fail=True):
77167
# type: (str) -> List[str], List[str]
78-
try:
79-
package_path = get_package_share_directory(package_name)
80-
except PackageNotFoundError:
81-
# This is used to parse all dependencies listed in package.xml which
82-
# may be dependences that are not ROS packages.
83-
return [], []
168+
workspaces = []
169+
package_path = find_in_workspaces(
170+
project=package_name,
171+
file="package.xml",
172+
workspaces=workspaces
173+
)
174+
if not package_path:
175+
if not soft_fail:
176+
raise RuntimeError("Failed to find package.xml for package " +
177+
package_name + ' in ' + ';'.join(workspaces))
178+
else:
179+
# This is not an ament dependency
180+
return [], []
84181
else:
85182
package = parse_package(package_path)
86183
dependencies = package.build_depends + package.test_depends

ament_virtualenv/resource/ament_virtualenv

Whitespace-only changes.

ament_virtualenv/setup.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
packages=find_packages(exclude=['test']),
1010
data_files=[
1111
('share/' + package_name, ['package.xml']),
12-
('share/ament_index/resource_index/packages',
13-
['resource/' + package_name]),
1412
],
1513
install_requires=['setuptools'],
1614
zip_safe=False,

0 commit comments

Comments
 (0)