1# Software License Agreement (BSD License) 2# 3# Copyright (c) 2012, Willow Garage, Inc. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 10# * Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# * Redistributions in binary form must reproduce the above 13# copyright notice, this list of conditions and the following 14# disclaimer in the documentation and/or other materials provided 15# with the distribution. 16# * Neither the name of Willow Garage, Inc. nor the names of its 17# contributors may be used to endorse or promote products derived 18# from this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31# POSSIBILITY OF SUCH DAMAGE. 32 33from __future__ import print_function 34 35import os 36 37from catkin.workspace import get_source_paths, get_workspaces 38 39from catkin_pkg.packages import find_packages 40 41 42def _get_valid_search_dirs(search_dirs, project): 43 """ 44 Compare param collection of search dirs with valid names, raises ValueError if invalid. 45 46 Maintains the order of param if any. 47 If project is given other names are allowed than without. 48 49 :param search_dirs: collection of foldernames (basename) to search for 50 :param project: the project to search in or None 51 :raises: ValueError 52 """ 53 # define valid search folders 54 valid_global_search_dirs = ['bin', 'etc', 'include', 'lib', 'share'] 55 valid_project_search_dirs = ['etc', 'include', 'libexec', 'share'] 56 57 valid_search_dirs = (valid_global_search_dirs 58 if project is None 59 else valid_project_search_dirs) 60 if not search_dirs: 61 search_dirs = valid_search_dirs 62 else: 63 # make search folders a list 64 search_dirs = list(search_dirs) 65 66 # determine valid search folders 67 all_valid_search_dirs = set(valid_global_search_dirs).union( 68 set(valid_project_search_dirs)) 69 70 # check folder name is known at all 71 diff_dirs = set(search_dirs).difference(all_valid_search_dirs) 72 if len(diff_dirs) > 0: 73 raise ValueError('Unsupported search folders: ' + 74 ', '.join(['"%s"' % i for i in diff_dirs])) 75 # check foldername works with project arg 76 diff_dirs = set(search_dirs).difference(valid_search_dirs) 77 if len(diff_dirs) > 0: 78 msg = 'Searching %s a project can not be combined with the search folders:' % ('without' if project is None else 'for') 79 raise ValueError(msg + ', '.join(['"%s"' % i for i in diff_dirs])) 80 return search_dirs 81 82 83# OUT is always a list of folders 84# 85# IN: project=None 86# OUT: foreach ws in workspaces: foreach s in search_in: cand = ws[0] + s (+ path) 87# add cand to result list if it exists 88# is not defined for s == 'libexec', bailing out 89# 90# IN: project=not None 91# OUT: foreach ws in workspaces: foreach s in search_in: cand = ws[0] + s + project (+ path) 92# except for s == 'share', cand is a list of two paths: ws[0] + s + project (+ path) and ws[1] + project (+ path) 93# add cand to result list if it exists 94# is not defined for s in ['bin', 'lib'], bailing out 95def find_in_workspaces(search_dirs=None, project=None, path=None, _workspaces=None, considered_paths=None, first_matching_workspace_only=False, first_match_only=False, workspace_to_source_spaces=None, source_path_to_packages=None): 96 """ 97 Find all paths which match the search criteria. 98 99 All workspaces are searched in order. 100 Each workspace, each search_in subfolder, the project name and the path are concatenated to define a candidate path. 101 If the candidate path exists it is appended to the result list. 102 Note: the search might return multiple paths for 'share' from devel- and source-space. 103 104 :param search_dir: The list of subfolders to search in (default contains all valid values: 'bin', 'etc', 'lib', 'libexec', 'share'), ``list`` 105 :param project: The project name to search for (optional, not possible with the global search_in folders 'bin' and 'lib'), ``str`` 106 :param path: The path, ``str`` 107 :param _workspaces: (optional, used for unit tests), the list of workspaces to use. 108 :param considered_paths: If not None, function will append all path that were searched 109 :param first_matching_workspace_only: if True returns all results found for first workspace with results 110 :param first_match_only: if True returns first path found (supercedes first_matching_workspace_only) 111 :param workspace_to_source_spaces: the dictionary is populated with mappings from workspaces to source paths, pass in the same dictionary to avoid repeated reading of the catkin marker file 112 :param source_path_to_packages: the dictionary is populated with mappings from source paths to packages, pass in the same dictionary to avoid repeated crawling 113 :raises ValueError: if search_dirs contains an invalid folder name 114 :returns: List of paths 115 """ 116 search_dirs = _get_valid_search_dirs(search_dirs, project) 117 if 'libexec' in search_dirs: 118 search_dirs.insert(search_dirs.index('libexec'), 'lib') 119 if _workspaces is None: 120 _workspaces = get_workspaces() 121 if workspace_to_source_spaces is None: 122 workspace_to_source_spaces = {} 123 if source_path_to_packages is None: 124 source_path_to_packages = {} 125 126 paths = [] 127 existing_paths = [] 128 try: 129 for workspace in (_workspaces or []): 130 for sub in search_dirs: 131 # search in workspace 132 p = os.path.join(workspace, sub) 133 if project: 134 p = os.path.join(p, project) 135 if path: 136 p = os.path.join(p, path) 137 paths.append(p) 138 if os.path.exists(p): 139 existing_paths.append(p) 140 if first_match_only: 141 raise StopIteration 142 143 # for search in share also consider source spaces 144 if project is not None and sub == 'share': 145 if workspace not in workspace_to_source_spaces: 146 workspace_to_source_spaces[workspace] = get_source_paths(workspace) 147 for source_path in workspace_to_source_spaces[workspace]: 148 if source_path not in source_path_to_packages: 149 source_path_to_packages[source_path] = find_packages(source_path) 150 matching_packages = [p for p, pkg in source_path_to_packages[source_path].items() if pkg.name == project] 151 if matching_packages: 152 p = source_path 153 if matching_packages[0] != os.curdir: 154 p = os.path.join(p, matching_packages[0]) 155 if path is not None: 156 p = os.path.join(p, path) 157 paths.append(p) 158 if os.path.exists(p): 159 existing_paths.append(p) 160 if first_match_only: 161 raise StopIteration 162 163 if first_matching_workspace_only and existing_paths: 164 break 165 166 except StopIteration: 167 pass 168 169 if considered_paths is not None: 170 considered_paths.extend(paths) 171 172 return existing_paths 173