installs.py
Go to the documentation of this file.
1 import os
2 import fnmatch
3 import collections
4 from ros_introspection.cmake import Command
5 from util import roscompile
6 
7 FILES_TO_NOT_INSTALL = ['CHANGELOG.rst', 'README.md', '.travis.yml', 'bitbucket-pipelines.yml']
8 
9 # We define four install types, using a unique string to identify each (the keys in this dict)
10 # The values define a tuple, where the first element is a CMake keyword.
11 # The second value is a dictionary mapping catkin destinations to some CMake section names.
12 INSTALL_CONFIGS = {
13  'exec': ('TARGETS', {'${CATKIN_PACKAGE_BIN_DESTINATION}': ['RUNTIME DESTINATION']}),
14  'library': ('TARGETS', {'${CATKIN_PACKAGE_LIB_DESTINATION}': ['ARCHIVE DESTINATION', 'LIBRARY DESTINATION'],
15  '${CATKIN_GLOBAL_BIN_DESTINATION}': ['RUNTIME DESTINATION']}),
16  'headers': ('FILES', {'${CATKIN_PACKAGE_INCLUDE_DESTINATION}': ['DESTINATION']}),
17  'misc': ('FILES', {'${CATKIN_PACKAGE_SHARE_DESTINATION}': ['DESTINATION']})
18 }
19 
20 
21 def get_install_type(destination):
22  """ For a given catkin destination, return the matching install type """
23  for name, (kw, destination_map) in INSTALL_CONFIGS.iteritems():
24  if destination in destination_map:
25  return name
26 
27 
28 def get_install_types(cmd, subfolder=''):
29  """ For a given CMake command, determine the install type(s) that this command uses.
30 
31  If there is a non-empty subfolder, we only return the install types if the command
32  installs into the catkin_destination with the given subfolder """
33  types = set()
34  for section in cmd.get_sections('DESTINATION'):
35  the_folder = section.values[0]
36  if len(subfolder) > 0:
37  if subfolder not in the_folder:
38  continue
39  the_folder = the_folder.replace('/' + subfolder, '')
40  type_ = get_install_type(the_folder)
41  if type_:
42  types.add(type_)
43  return types
44 
45 
46 def get_multiword_section(cmd, words):
47  """ Our definition of a CMake command section is ONE all-caps word followed by tokens.
48  Installing stuff requires these weird TWO word sections (i.e. ARCHIVE DESTINATION).
49 
50  Ergo, we need to find the section that matches the second word, presuming the section
51  before matched the first word.
52  """
53  i = 0
54  for section in cmd.get_real_sections():
55  if section.name == words[i]:
56  # One word matches
57  if i < len(words) - 1:
58  # If there are more words, increment the counter
59  i += 1
60  else:
61  # Otherwise, we matched all the words. Return this section
62  return section
63  else:
64  # If the word doesn't match, we need to start searching from the first word again
65  i = 0
66 
67 
68 def matches_patterns(item, patterns):
69  for pattern in patterns:
70  if pattern[0] == pattern[-1] and pattern[0] == '"':
71  pattern = pattern[1:-1]
72  if fnmatch.fnmatch(item, pattern):
73  return True
74 
75 
76 def check_complex_section(cmd, key, value):
77  """ This finds the appopriate section of the command (with a possibly multiword key, see get_multiword_section)
78  and ensures the given value is in it. If the appopriate section is not found, it adds it. """
79 
80  words = key.split()
81  if len(words) == 1:
82  section = cmd.get_section(key)
83  else:
84  section = get_multiword_section(cmd, words)
85 
86  if section:
87  if value not in section.values:
88  section.add(value)
89  cmd.changed = True
90  else:
91  cmd.add_section(key, [value])
92 
93 
94 def install_sections(cmd, destination_map, subfolder=''):
95  """ For a given command and destination_map, ensure that the command has all
96  the appropriate CMake sections with the matching catkin destinations.
97  If the subfolder is defined, the subfolder is appended to the catkin destination."""
98  for destination, section_names in destination_map.iteritems():
99  for section_name in section_names:
100  if len(subfolder) > 0:
101  destination = os.path.join(destination, subfolder)
102  check_complex_section(cmd, section_name, destination)
103 
104 
105 def remove_install_section(cmd, destination_map):
106  empty_sections_to_remove = {}
107  for destination, section_names in destination_map.iteritems():
108  for section_name in section_names:
109  parts = section_name.split()
110  if len(parts) == 2:
111  empty_sections_to_remove[parts[0]] = destination
112  sections = cmd.get_real_sections()
113  to_remove = []
114  for i, section in enumerate(sections):
115  if section.name not in empty_sections_to_remove or len(section.values) != 0:
116  continue
117  next = sections[i + 1]
118  dest = empty_sections_to_remove[section.name]
119  if next.name == 'DESTINATION' and len(next.values) == 1 and next.values[0] == dest:
120  to_remove.append(section)
121  to_remove.append(next)
122  if len(to_remove) > 0:
123  for section in to_remove:
124  cmd.sections.remove(section)
125  cmd.changed = True
126 
127 
128 def get_commands_by_type(cmake, name, subfolder=''):
129  matches = []
130  for cmd in cmake.content_map['install']:
131  if name in get_install_types(cmd, subfolder):
132  matches.append(cmd)
133  return matches
134 
135 
136 def install_section_check(cmake, items, install_type, directory=False, subfolder=''):
137  section_name, destination_map = INSTALL_CONFIGS[install_type]
138  if directory and section_name == 'FILES':
139  section_name = 'DIRECTORY'
140  cmds = get_commands_by_type(cmake, install_type, subfolder)
141  if len(items) == 0:
142  for cmd in cmds:
143  if len(get_install_types(cmd)) == 1:
144  cmake.remove_command(cmd)
145  else:
146  remove_install_section(cmd, destination_map)
147  return
148 
149  cmd = None
150  items = [os.path.join(subfolder, item) for item in items]
151  for cmd in cmds:
152  install_sections(cmd, destination_map, subfolder)
153  section = cmd.get_section(section_name)
154  if not section:
155  if section_name != 'FILES':
156  continue
157  section = cmd.get_section('DIRECTORY')
158  if not section:
159  continue
160  pattern = get_multiword_section(cmd, ['FILES_MATCHING', 'PATTERN'])
161  nonmatching_items = []
162  for item in items:
163  if pattern and not matches_patterns(item, pattern.values):
164  nonmatching_items.append(item)
165  items = nonmatching_items
166  else:
167  # We match the section
168  section.values = [value for value in section.values if value in items]
169  items = [item for item in items if item not in section.values]
170 
171  if len(items) == 0:
172  return
173 
174  print('\tInstalling', ', '.join(items))
175  if cmd is None:
176  cmd = Command('install')
177  cmd.add_section(section_name, items)
178  cmake.add_command(cmd)
179  install_sections(cmd, destination_map, subfolder)
180  elif section:
181  section = cmd.get_section(section_name)
182  section.values += items
183  cmd.changed = True
184 
185 
186 @roscompile
188  install_section_check(package.cmake, package.cmake.get_executables(), 'exec')
189  install_section_check(package.cmake, package.cmake.get_libraries(), 'library')
190  if package.name and package.source_code.has_header_files():
191  install_section_check(package.cmake, ['include/${PROJECT_NAME}/'], 'headers', directory=True)
192 
193 
194 @roscompile
195 def update_misc_installs(package):
196  extra_files_by_folder = collections.defaultdict(list)
197  rel_paths = [obj.rel_fn for obj in package.launches + package.plugin_configs] + package.misc_files
198  for rel_path in sorted(rel_paths):
199  if rel_path in FILES_TO_NOT_INSTALL:
200  continue
201  path, base = os.path.split(rel_path)
202  extra_files_by_folder[path].append(base)
203 
204  for folder, files in extra_files_by_folder.iteritems():
205  install_section_check(package.cmake, files, 'misc', subfolder=folder)
206 
207 
208 @roscompile
210  for cmd in package.cmake.content_map['install']:
211  dir_section = cmd.get_section('DIRECTORY')
212  dest_sections = cmd.get_sections('DESTINATION')
213 
214  if not dir_section or not dest_sections:
215  continue
216  directory = dir_section.values[0]
217  final_slash = directory[-1] == '/'
218 
219  for section in dest_sections:
220  destination = section.values[0]
221  if not final_slash and destination.endswith(directory):
222  # Remove double directory and final slash
223  section.values[0] = destination[:-len(directory) - 1]
224  cmd.changed = True
def matches_patterns(item, patterns)
Definition: installs.py:68
def check_complex_section(cmd, key, value)
Definition: installs.py:76
def get_multiword_section(cmd, words)
Definition: installs.py:46
def update_cplusplus_installs(package)
Definition: installs.py:187
def fix_double_directory_installs(package)
Definition: installs.py:209
def install_section_check(cmake, items, install_type, directory=False, subfolder='')
Definition: installs.py:136
def update_misc_installs(package)
Definition: installs.py:195
def remove_install_section(cmd, destination_map)
Definition: installs.py:105
def get_commands_by_type(cmake, name, subfolder='')
Definition: installs.py:128
def get_install_types(cmd, subfolder='')
Definition: installs.py:28
def install_sections(cmd, destination_map, subfolder='')
Definition: installs.py:94
def get_install_type(destination)
Definition: installs.py:21


roscompile
Author(s):
autogenerated on Wed Jun 19 2019 19:56:53