installs.py
Go to the documentation of this file.
00001 import os
00002 import fnmatch
00003 import collections
00004 from ros_introspection.cmake import Command
00005 from util import roscompile
00006 
00007 FILES_TO_NOT_INSTALL = ['CHANGELOG.rst', 'README.md', '.travis.yml', 'bitbucket-pipelines.yml']
00008 
00009 # We define four install types, using a unique string to identify each (the keys in this dict)
00010 # The values define a tuple, where the first element is a CMake keyword.
00011 # The second value is a dictionary mapping catkin destinations to some CMake section names.
00012 INSTALL_CONFIGS = {
00013     'exec': ('TARGETS', {'${CATKIN_PACKAGE_BIN_DESTINATION}': ['RUNTIME DESTINATION']}),
00014     'library': ('TARGETS', {'${CATKIN_PACKAGE_LIB_DESTINATION}': ['ARCHIVE DESTINATION', 'LIBRARY DESTINATION'],
00015                             '${CATKIN_GLOBAL_BIN_DESTINATION}': ['RUNTIME DESTINATION']}),
00016     'headers': ('FILES', {'${CATKIN_PACKAGE_INCLUDE_DESTINATION}': ['DESTINATION']}),
00017     'misc': ('FILES', {'${CATKIN_PACKAGE_SHARE_DESTINATION}': ['DESTINATION']})
00018 }
00019 
00020 
00021 def get_install_type(destination):
00022     """ For a given catkin destination, return the matching install type """
00023     for name, (kw, destination_map) in INSTALL_CONFIGS.iteritems():
00024         if destination in destination_map:
00025             return name
00026 
00027 
00028 def get_install_types(cmd, subfolder=''):
00029     """ For a given CMake command, determine the install type(s) that this command uses.
00030 
00031         If there is a non-empty subfolder, we only return the install types if the command
00032         installs into the catkin_destination with the given subfolder """
00033     types = set()
00034     for section in cmd.get_sections('DESTINATION'):
00035         the_folder = section.values[0]
00036         if len(subfolder) > 0:
00037             if subfolder not in the_folder:
00038                 continue
00039             the_folder = the_folder.replace('/' + subfolder, '')
00040         type_ = get_install_type(the_folder)
00041         if type_:
00042             types.add(type_)
00043     return types
00044 
00045 
00046 def get_multiword_section(cmd, words):
00047     """ Our definition of a CMake command section is ONE all-caps word followed by tokens.
00048         Installing stuff requires these weird TWO word sections (i.e. ARCHIVE DESTINATION).
00049 
00050         Ergo, we need to find the section that matches the second word, presuming the section
00051         before matched the first word.
00052     """
00053     i = 0
00054     for section in cmd.get_real_sections():
00055         if section.name == words[i]:
00056             # One word matches
00057             if i < len(words) - 1:
00058                 # If there are more words, increment the counter
00059                 i += 1
00060             else:
00061                 # Otherwise, we matched all the words. Return this section
00062                 return section
00063         else:
00064             # If the word doesn't match, we need to start searching from the first word again
00065             i = 0
00066 
00067 
00068 def matches_patterns(item, patterns):
00069     for pattern in patterns:
00070         if pattern[0] == pattern[-1] and pattern[0] == '"':
00071             pattern = pattern[1:-1]
00072         if fnmatch.fnmatch(item, pattern):
00073             return True
00074 
00075 
00076 def check_complex_section(cmd, key, value):
00077     """ This finds the appopriate section of the command (with a possibly multiword key, see get_multiword_section)
00078         and ensures the given value is in it. If the appopriate section is not found, it adds it. """
00079 
00080     words = key.split()
00081     if len(words) == 1:
00082         section = cmd.get_section(key)
00083     else:
00084         section = get_multiword_section(cmd, words)
00085 
00086     if section:
00087         if value not in section.values:
00088             section.add(value)
00089             cmd.changed = True
00090     else:
00091         cmd.add_section(key, [value])
00092 
00093 
00094 def install_sections(cmd, destination_map, subfolder=''):
00095     """ For a given command and destination_map, ensure that the command has all
00096         the appropriate CMake sections with the matching catkin destinations.
00097         If the subfolder is defined, the subfolder is appended to the catkin destination."""
00098     for destination, section_names in destination_map.iteritems():
00099         for section_name in section_names:
00100             if len(subfolder) > 0:
00101                 destination = os.path.join(destination, subfolder)
00102             check_complex_section(cmd, section_name, destination)
00103 
00104 
00105 def remove_install_section(cmd, destination_map):
00106     empty_sections_to_remove = {}
00107     for destination, section_names in destination_map.iteritems():
00108         for section_name in section_names:
00109             parts = section_name.split()
00110             if len(parts) == 2:
00111                 empty_sections_to_remove[parts[0]] = destination
00112     sections = cmd.get_real_sections()
00113     to_remove = []
00114     for i, section in enumerate(sections):
00115         if section.name not in empty_sections_to_remove or len(section.values) != 0:
00116             continue
00117         next = sections[i + 1]
00118         dest = empty_sections_to_remove[section.name]
00119         if next.name == 'DESTINATION' and len(next.values) == 1 and next.values[0] == dest:
00120             to_remove.append(section)
00121             to_remove.append(next)
00122     if len(to_remove) > 0:
00123         for section in to_remove:
00124             cmd.sections.remove(section)
00125         cmd.changed = True
00126 
00127 
00128 def get_commands_by_type(cmake, name, subfolder=''):
00129     matches = []
00130     for cmd in cmake.content_map['install']:
00131         if name in get_install_types(cmd, subfolder):
00132             matches.append(cmd)
00133     return matches
00134 
00135 
00136 def install_section_check(cmake, items, install_type, directory=False, subfolder=''):
00137     section_name, destination_map = INSTALL_CONFIGS[install_type]
00138     if directory and section_name == 'FILES':
00139         section_name = 'DIRECTORY'
00140     cmds = get_commands_by_type(cmake, install_type, subfolder)
00141     if len(items) == 0:
00142         for cmd in cmds:
00143             if len(get_install_types(cmd)) == 1:
00144                 cmake.remove_command(cmd)
00145             else:
00146                 remove_install_section(cmd, destination_map)
00147         return
00148 
00149     cmd = None
00150     items = [os.path.join(subfolder, item) for item in items]
00151     for cmd in cmds:
00152         install_sections(cmd, destination_map, subfolder)
00153         section = cmd.get_section(section_name)
00154         if not section:
00155             if section_name != 'FILES':
00156                 continue
00157             section = cmd.get_section('DIRECTORY')
00158             if not section:
00159                 continue
00160             pattern = get_multiword_section(cmd, ['FILES_MATCHING', 'PATTERN'])
00161             nonmatching_items = []
00162             for item in items:
00163                 if pattern and not matches_patterns(item, pattern.values):
00164                     nonmatching_items.append(item)
00165             items = nonmatching_items
00166         else:
00167             # We match the section
00168             section.values = [value for value in section.values if value in items]
00169             items = [item for item in items if item not in section.values]
00170 
00171     if len(items) == 0:
00172         return
00173 
00174     print('\tInstalling', ', '.join(items))
00175     if cmd is None:
00176         cmd = Command('install')
00177         cmd.add_section(section_name, items)
00178         cmake.add_command(cmd)
00179         install_sections(cmd, destination_map, subfolder)
00180     elif section:
00181         section = cmd.get_section(section_name)
00182         section.values += items
00183         cmd.changed = True
00184 
00185 
00186 @roscompile
00187 def update_cplusplus_installs(package):
00188     install_section_check(package.cmake, package.cmake.get_executables(), 'exec')
00189     install_section_check(package.cmake, package.cmake.get_libraries(), 'library')
00190     if package.name and package.source_code.has_header_files():
00191         install_section_check(package.cmake, ['include/${PROJECT_NAME}/'], 'headers', directory=True)
00192 
00193 
00194 @roscompile
00195 def update_misc_installs(package):
00196     extra_files_by_folder = collections.defaultdict(list)
00197     rel_paths = [obj.rel_fn for obj in package.launches + package.plugin_configs] + package.misc_files
00198     for rel_path in sorted(rel_paths):
00199         if rel_path in FILES_TO_NOT_INSTALL:
00200             continue
00201         path, base = os.path.split(rel_path)
00202         extra_files_by_folder[path].append(base)
00203 
00204     for folder, files in extra_files_by_folder.iteritems():
00205         install_section_check(package.cmake, files, 'misc', subfolder=folder)
00206 
00207 
00208 @roscompile
00209 def fix_double_directory_installs(package):
00210     for cmd in package.cmake.content_map['install']:
00211         dir_section = cmd.get_section('DIRECTORY')
00212         dest_sections = cmd.get_sections('DESTINATION')
00213 
00214         if not dir_section or not dest_sections:
00215             continue
00216         directory = dir_section.values[0]
00217         final_slash = directory[-1] == '/'
00218 
00219         for section in dest_sections:
00220             destination = section.values[0]
00221             if not final_slash and destination.endswith(directory):
00222                 # Remove double directory and final slash
00223                 section.values[0] = destination[:-len(directory) - 1]
00224                 cmd.changed = True


roscompile
Author(s):
autogenerated on Wed Jun 19 2019 19:21:36