26
26
import sys
27
27
import os
28
28
29
+ from pathlib import Path
29
30
from typing import List
30
31
from catkin_pkg .package import Package
31
32
from ament_index_python .packages import get_package_share_directory , PackageNotFoundError
44
45
from Queue import Queue
45
46
46
47
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
+ "\n AMENT_PREFIX_PATH=" + os .environ .get ('AMENT_PREFIX_PATH' , 'NOT SET' ) +
110
+ "\n CMAKE_PREFIX_PATH=" + os .environ .get ('CMAKE_PREFIX_PATH' , 'NOT SET' ) +
111
+ "\n COLCON_PREFIX_PATH=" + os .environ .get ('COLCON_PREFIX_PATH' , 'NOT SET' ) +
112
+ "\n CWD=" + 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
+
47
139
AMENT_VIRTUALENV_TAGNAME = "pip_requirements"
48
140
49
141
50
142
def parse_exported_requirements (package : Package ) -> List [str ]:
51
143
requirements_list = []
52
144
for export in package .exports :
53
145
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
+ )
60
150
if not requirements_path :
61
151
print (
62
152
("[ERROR] ament_virtualenv "
@@ -75,12 +165,19 @@ def parse_exported_requirements(package: Package) -> List[str]:
75
165
76
166
def process_package (package_name , soft_fail = True ):
77
167
# 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 [], []
84
181
else :
85
182
package = parse_package (package_path )
86
183
dependencies = package .build_depends + package .test_depends
0 commit comments