cmake.py
Go to the documentation of this file.
00001 import collections
00002 import re
00003 
00004 VARIABLE_PATTERN = re.compile(r'\$\{([^\}]+)\}')
00005 QUOTED_PATTERN = re.compile(r'"([^"]+)"')
00006 
00007 BUILD_TARGET_COMMANDS = ['add_library', 'add_executable', 'add_rostest', 'add_dependencies', 'target_link_libraries']
00008 
00009 ORDERING = ['cmake_minimum_required', 'project', 'set_directory_properties', 'find_package', 'pkg_check_modules',
00010             'set', 'catkin_generate_virtualenv', 'catkin_python_setup', 'add_definitions',
00011             'add_message_files', 'add_service_files', 'add_action_files',
00012             'generate_dynamic_reconfigure_options', 'generate_messages', 'catkin_package', 'catkin_metapackage',
00013             BUILD_TARGET_COMMANDS + ['include_directories'],
00014             ['catkin_download_test_data', 'roslint_cpp', 'roslint_python', 'roslint_add_test', 'catkin_add_nosetests'],
00015             'catkin_add_gtest', 'group',
00016             ['install', 'catkin_install_python']]
00017 
00018 
00019 def get_ordering_index(command_name):
00020     for i, o in enumerate(ORDERING):
00021         if type(o) == list:
00022             if command_name in o:
00023                 return i
00024         elif command_name == o:
00025             return i
00026     if command_name:
00027         print('\tUnsure of ordering for', command_name)
00028     return len(ORDERING)
00029 
00030 
00031 def get_sort_key(content, anchors):
00032     if content is None:
00033         return len(ORDERING) + 1, None
00034     index = None
00035     key = None
00036     if content.__class__ == CommandGroup:
00037         index = get_ordering_index('group')
00038         sections = content.initial_tag.get_real_sections()
00039         if len(sections) > 0:
00040             key = sections[0].name
00041     else:  # Command
00042         index = get_ordering_index(content.command_name)
00043         if content.command_name in BUILD_TARGET_COMMANDS:
00044             token = content.first_token()
00045             if token not in anchors:
00046                 anchors.append(token)
00047             key = anchors.index(token), BUILD_TARGET_COMMANDS.index(content.command_name)
00048         elif content.command_name == 'include_directories' and 'include_directories' in anchors:
00049             key = anchors.index('include_directories')
00050     return index, key
00051 
00052 
00053 class SectionStyle:
00054     def __init__(self, prename='', name_val_sep=' ', val_sep=' '):
00055         self.prename = prename
00056         self.name_val_sep = name_val_sep
00057         self.val_sep = val_sep
00058 
00059     def __repr__(self):
00060         return 'SectionStyle(%s, %s, %s)' % (repr(self.prename), repr(self.name_val_sep), repr(self.val_sep))
00061 
00062 
00063 class Section:
00064     def __init__(self, name='', values=None, style=None):
00065         self.name = name
00066         if values is None:
00067             self.values = []
00068         else:
00069             self.values = list(values)
00070         if style:
00071             self.style = style
00072         else:
00073             self.style = SectionStyle()
00074 
00075     def add(self, v):
00076         self.values.append(v)
00077 
00078     def is_valid(self):
00079         return len(self.name) > 0 or len(self.values) > 0
00080 
00081     def __repr__(self):
00082         s = self.style.prename
00083         if len(self.name) > 0:
00084             s += self.name
00085             if len(self.values) > 0:
00086                 s += self.style.name_val_sep
00087         s += self.style.val_sep.join(self.values)
00088         return s
00089 
00090 
00091 class Command:
00092     def __init__(self, command_name):
00093         self.command_name = command_name
00094         self.original = None
00095         self.changed = False
00096         self.pre_paren = ''
00097         self.sections = []
00098 
00099     def get_real_sections(self):
00100         return [s for s in self.sections if type(s) != str]
00101 
00102     def get_section(self, key):
00103         for s in self.get_real_sections():
00104             if s.name == key:
00105                 return s
00106         return None
00107 
00108     def get_sections(self, key):
00109         return [s for s in self.get_real_sections() if s.name == key]
00110 
00111     def add_section(self, key, values=None, style=None):
00112         self.sections.append(Section(key, values, style))
00113         self.changed = True
00114 
00115     def add(self, section):
00116         if section:
00117             self.sections.append(section)
00118             self.changed = True
00119 
00120     def first_token(self):
00121         return self.get_real_sections()[0].values[0]
00122 
00123     def remove_sections(self, key):
00124         bad_sections = self.get_sections(key)
00125         if not bad_sections:
00126             return
00127         self.changed = True
00128         self.sections = [section for section in self.sections if section not in bad_sections]
00129         if len(self.sections) == 1 and type(self.sections[0]) == str:
00130             self.sections = []
00131 
00132     def get_tokens(self, include_name=False):
00133         tokens = []
00134         for section in self.get_real_sections():
00135             if include_name and section.name:
00136                 tokens.append(section.name)
00137             tokens += section.values
00138         return tokens
00139 
00140     def add_token(self, s):
00141         sections = self.get_real_sections()
00142         if len(sections) == 0:
00143             self.add(Section(values=[s]))
00144         else:
00145             last = sections[-1]
00146             last.values.append(s)
00147         self.changed = True
00148 
00149     def __repr__(self):
00150         if self.original and not self.changed:
00151             return self.original
00152 
00153         s = self.command_name + self.pre_paren + '('
00154         for section in map(str, self.sections):
00155             if s[-1] not in '( \n' and section[0] not in ' \n':
00156                 s += ' '
00157             s += section
00158         if '\n' in s and s[-1] != '\n':
00159             s += '\n'
00160         s += ')'
00161         return s
00162 
00163 
00164 class CommandGroup:
00165     def __init__(self, initial_tag, sub, close_tag):
00166         self.initial_tag = initial_tag
00167         self.sub = sub
00168         self.close_tag = close_tag
00169 
00170     def __repr__(self):
00171         return str(self.initial_tag) + str(self.sub) + str(self.close_tag)
00172 
00173 
00174 class CMake:
00175     def __init__(self, file_path=None, initial_contents=None, depth=0):
00176         self.file_path = file_path
00177         if initial_contents is None:
00178             self.contents = []
00179         else:
00180             self.contents = initial_contents
00181         self.content_map = collections.defaultdict(list)
00182         for content in self.contents:
00183             if content.__class__ == Command:
00184                 self.content_map[content.command_name].append(content)
00185             elif content.__class__ == CommandGroup:
00186                 self.content_map['group'].append(content)
00187         self.depth = depth
00188 
00189         self.variables = {}
00190         for cmd in self.content_map['set']:
00191             tokens = cmd.get_tokens(include_name=True)
00192             self.variables[tokens[0]] = ' '.join(tokens[1:])
00193         self.variables['PROJECT_NAME'] = self.get_project_name()
00194 
00195     def get_project_name(self):
00196         project_tags = self.content_map['project']
00197         if not project_tags:
00198             return ''
00199         # Get all tokens just in case the name is all caps
00200         return project_tags[0].get_tokens(include_name=True)[0]
00201 
00202     def resolve_variables(self, var):
00203         if type(var) == str:
00204             s = var
00205             m = VARIABLE_PATTERN.search(s)
00206             if not m:
00207                 return s
00208 
00209             for k, v in self.variables.iteritems():
00210                 s = s.replace('${%s}' % k, v)
00211             return s
00212         else:
00213             tokens = []
00214             for token in var:
00215                 if token and token[0] == '#':
00216                     continue
00217                 m = QUOTED_PATTERN.match(token)
00218                 if m:
00219                     token = m.group(1)
00220                 token = self.resolve_variables(token)
00221                 tokens += token.split(' ')
00222             return tokens
00223 
00224     def get_resolved_tokens(self, cmd, include_name=False):
00225         return self.resolve_variables(cmd.get_tokens(include_name))
00226 
00227     def get_insertion_index(self, cmd):
00228         anchors = self.get_ordered_build_targets()
00229 
00230         new_key = get_sort_key(cmd, anchors)
00231         i_index = 0
00232 
00233         for i, content in enumerate(self.contents):
00234             if type(content) == str:
00235                 continue
00236             key = get_sort_key(content, anchors)
00237             if key <= new_key:
00238                 i_index = i + 1
00239             elif key[0] != len(ORDERING):
00240                 return i_index
00241         return len(self.contents)
00242 
00243     def add_command(self, cmd):
00244         i_index = self.get_insertion_index(cmd)
00245         sub_contents = []
00246         if i_index > 0 and type(self.contents[i_index - 1]) != str:
00247             sub_contents.append('\n')
00248         if self.depth > 0:
00249             sub_contents.append('  ' * self.depth)
00250             sub_contents.append(cmd)
00251             sub_contents.append('\n')
00252         else:
00253             sub_contents.append(cmd)
00254         if i_index == len(self.contents):
00255             sub_contents.append('\n')
00256 
00257         self.contents = self.contents[:i_index] + sub_contents + self.contents[i_index:]
00258 
00259         if cmd.__class__ == Command:
00260             self.content_map[cmd.command_name].append(cmd)
00261         elif cmd.__class__ == CommandGroup:
00262             self.content_map['group'].append(cmd)
00263 
00264     def remove_command(self, cmd):
00265         print('\tRemoving %s' % str(cmd).replace('\n', ' ').replace('  ', ''))
00266         self.contents.remove(cmd)
00267         self.content_map[cmd.command_name].remove(cmd)
00268 
00269     def remove_all_commands(self, cmd_name):
00270         cmds = list(self.content_map[cmd_name])
00271         for cmd in cmds:
00272             self.remove_command(cmd)
00273 
00274     def get_source_build_rules(self, tag, resolve_target_name=False):
00275         rules = {}
00276         for cmd in self.content_map[tag]:
00277             resolved_tokens = self.get_resolved_tokens(cmd, True)
00278 
00279             if resolve_target_name:
00280                 target = resolved_tokens[0]
00281             else:
00282                 tokens = cmd.get_tokens(True)
00283                 target = tokens[0]
00284 
00285             deps = resolved_tokens[1:]
00286             rules[target] = deps
00287         return rules
00288 
00289     def get_source_helper(self, tag):
00290         lib_src = set()
00291         for target, deps in self.get_source_build_rules(tag).iteritems():
00292             lib_src.update(deps)
00293         return lib_src
00294 
00295     def get_library_source(self):
00296         return self.get_source_helper('add_library')
00297 
00298     def get_executable_source(self):
00299         return self.get_source_helper('add_executable')
00300 
00301     def get_libraries(self):
00302         return self.get_source_build_rules('add_library').keys()
00303 
00304     def get_executables(self):
00305         return self.get_source_build_rules('add_executable').keys()
00306 
00307     def get_target_build_rules(self):
00308         targets = {}
00309         targets.update(self.get_source_build_rules('add_library'))
00310         targets.update(self.get_source_build_rules('add_executable'))
00311         return targets
00312 
00313     def get_ordered_build_targets(self):
00314         targets = []
00315         for content in self.contents:
00316             if content.__class__ != Command:
00317                 continue
00318             if content.command_name == 'include_directories':
00319                 targets.append('include_directories')
00320                 continue
00321             elif content.command_name not in BUILD_TARGET_COMMANDS:
00322                 continue
00323             token = content.first_token()
00324             if token not in targets:
00325                 targets.append(token)
00326         return targets
00327 
00328     def get_test_sections(self):
00329         sections = []
00330         for content in self.content_map['group']:
00331             cmd = content.initial_tag
00332             if cmd.command_name != 'if' or len(cmd.sections) == 0 or cmd.sections[0].name != 'CATKIN_ENABLE_TESTING':
00333                 continue
00334             sections.append(content.sub)
00335         return sections
00336 
00337     def get_test_source(self):
00338         test_files = set()
00339         for sub in self.get_test_sections():
00340             test_files.update(sub.get_library_source())
00341             test_files.update(sub.get_executable_source())
00342         return test_files
00343 
00344     def get_test_section(self, create_if_needed=False):
00345         sections = self.get_test_sections()
00346         if len(sections) > 0:
00347             return sections[0]
00348         if not create_if_needed:
00349             return None
00350 
00351         # Create Test Section
00352         initial_cmd = Command('if')
00353         initial_cmd.add_section('CATKIN_ENABLE_TESTING')
00354 
00355         test_contents = CMake(initial_contents=['\n'], depth=self.depth + 1)
00356 
00357         final_cmd = Command('endif')
00358 
00359         cg = CommandGroup(initial_cmd, test_contents, final_cmd)
00360         self.add_command(cg)
00361         return cg.sub
00362 
00363     def get_command_section(self, command_name, section_name):
00364         """ Return the first command that matches the command name and
00365             has a matching section name. If the section name is not found,
00366             return a command with the matching command name"""
00367         if len(self.content_map[command_name]) == 0:
00368             return None, None
00369         for cmd in self.content_map[command_name]:
00370             s = cmd.get_section(section_name)
00371             if s:
00372                 return cmd, s
00373         return self.content_map[command_name][0], None
00374 
00375     def section_check(self, items, cmd_name, section_name='', zero_okay=False):
00376         """ This function ensures that there's a CMake command of the given type
00377             with the given section name and items somewhere in the file. """
00378         if len(items) == 0 and not zero_okay:
00379             return
00380 
00381         cmd, section = self.get_command_section(cmd_name, section_name)
00382 
00383         if cmd is None:
00384             cmd = Command(cmd_name)
00385             self.add_command(cmd)
00386 
00387         if section is None:
00388             cmd.add_section(section_name, sorted(items))
00389         else:
00390             existing = self.resolve_variables(section.values)
00391             needed_items = [item for item in items if item not in existing]
00392             section.values += sorted(needed_items)
00393             cmd.changed = True
00394 
00395     def __repr__(self):
00396         return ''.join(map(str, self.contents))
00397 
00398     def write(self, fn=None):
00399         if fn is None:
00400             fn = self.file_path
00401         with open(fn, 'w') as cmake:
00402             cmake.write(str(self))


ros_introspection
Author(s):
autogenerated on Wed Jun 19 2019 19:21:34