cmake.py
Go to the documentation of this file.
00001 from ros_introspection.cmake import Command, CommandGroup, get_sort_key
00002 from ros_introspection.source_code_file import CPLUS
00003 from ros_introspection.resource_list import is_message, is_service
00004 from util import get_ignore_data, roscompile
00005 
00006 SHOULD_ALPHABETIZE = ['COMPONENTS', 'DEPENDENCIES', 'FILES', 'CATKIN_DEPENDS']
00007 NEWLINE_PLUS_4 = '\n    '
00008 NEWLINE_PLUS_8 = '\n        '
00009 
00010 
00011 def check_cmake_dependencies_helper(cmake, dependencies, check_catkin_pkg=True):
00012     if len(dependencies) == 0:
00013         return
00014     if len(cmake.content_map['find_package']) == 0:
00015         cmd = Command('find_package')
00016         cmd.add_section('', ['catkin'])
00017         cmd.add_section('REQUIRED')
00018         cmake.add_command(cmd)
00019 
00020     for cmd in cmake.content_map['find_package']:
00021         tokens = cmd.get_tokens()
00022         if tokens and tokens[0] == 'catkin' and cmd.get_section('REQUIRED'):
00023             req_sec = cmd.get_section('REQUIRED')
00024             section = cmd.get_section('COMPONENTS')
00025             if section is None and req_sec.values:
00026                 section = req_sec  # Allow packages to be listed without COMPONENTS keyword
00027             if section is None:
00028                 cmd.add_section('COMPONENTS', sorted(dependencies))
00029             else:
00030                 existing = cmake.resolve_variables(section.values)
00031                 needed_items = dependencies - set(existing)
00032                 if len(needed_items) > 0:
00033                     section.values += list(sorted(needed_items))
00034                     cmd.changed = True
00035     if check_catkin_pkg:
00036         cmake.section_check(dependencies, 'catkin_package', 'CATKIN_DEPENDS')
00037 
00038 
00039 @roscompile
00040 def check_cmake_dependencies(package):
00041     dependencies = package.get_dependencies_from_msgs()
00042     dependencies.update(package.get_build_dependencies())
00043     check_cmake_dependencies_helper(package.cmake, dependencies)
00044 
00045 
00046 def get_matching_add_depends(cmake, search_target):
00047     valid_targets = set([search_target])
00048     alt_target = cmake.resolve_variables(search_target)
00049     if alt_target != search_target:
00050         valid_targets.add(alt_target)
00051 
00052     for cmd in cmake.content_map['add_dependencies']:
00053         target = cmd.first_token()
00054         if target in valid_targets:
00055             return cmd
00056         resolved_target = cmake.resolve_variables(target)
00057         if resolved_target in valid_targets:
00058             return cmd
00059 
00060 
00061 def match_generator_name(package, name):
00062     for gen in package.get_all_generators():
00063         if name == gen.base_name:
00064             return gen
00065 
00066 
00067 def get_msg_dependencies_from_source(package, sources):
00068     deps = set()
00069     for rel_fn in sources:
00070         if rel_fn not in package.source_code.sources:
00071             continue
00072         src = package.source_code.sources[rel_fn]
00073         for pkg, name in src.search_lines_for_pattern(CPLUS):
00074             if len(name) == 0 or name[-2:] != '.h':
00075                 continue
00076             name = name.replace('.h', '')
00077             if is_message(pkg, name) or is_service(pkg, name):
00078                 deps.add(pkg)
00079             elif pkg == package.name and match_generator_name(package, name):
00080                 deps.add(pkg)
00081     if package.dynamic_reconfigs:
00082         deps.add(package.name)
00083     return sorted(list(deps))
00084 
00085 
00086 @roscompile
00087 def check_exported_dependencies(package):
00088     targets = package.cmake.get_target_build_rules()
00089     for target, sources in targets.iteritems():
00090         deps = get_msg_dependencies_from_source(package, sources)
00091         if len(deps) == 0:
00092             continue
00093 
00094         if package.name in deps:
00095             self_depend = True
00096             if len(deps) == 1:
00097                 cat_depend = False
00098             else:
00099                 cat_depend = True
00100         else:
00101             self_depend = False
00102             cat_depend = True
00103 
00104         add_deps = get_matching_add_depends(package.cmake, target)
00105         add_add_deps = False
00106 
00107         if add_deps is None:
00108             add_deps = Command('add_dependencies')
00109             add_add_deps = True  # Need to wait to add the command for proper sorting
00110 
00111         if len(add_deps.sections) == 0:
00112             add_deps.add_section('', [target])
00113             add_deps.changed = True
00114 
00115         section = add_deps.sections[0]
00116         if cat_depend and '${catkin_EXPORTED_TARGETS}' not in section.values:
00117             section.add('${catkin_EXPORTED_TARGETS}')
00118             add_deps.changed = True
00119         if self_depend:
00120             tokens = [package.cmake.resolve_variables(s) for s in section.values]
00121             key = '${%s_EXPORTED_TARGETS}' % package.name
00122             if key not in tokens:
00123                 section.add(key)
00124                 add_deps.changed = True
00125 
00126         if add_add_deps:
00127             package.cmake.add_command(add_deps)
00128 
00129 
00130 def remove_pattern(section, pattern):
00131     prev_len = len(section.values)
00132     section.values = [v for v in section.values if pattern not in v]
00133     return prev_len != len(section.values)
00134 
00135 
00136 @roscompile
00137 def remove_old_style_cpp_dependencies(package):
00138     global_changed = False
00139     targets = package.cmake.get_target_build_rules()
00140     for target, sources in targets.iteritems():
00141         add_deps = get_matching_add_depends(package.cmake, target)
00142         if add_deps is None or len(add_deps.sections) == 0:
00143             continue
00144 
00145         section = add_deps.sections[0]
00146         changed = remove_pattern(section, '_generate_messages_cpp')
00147         changed = remove_pattern(section, '_gencpp') or changed
00148         changed = remove_pattern(section, '_gencfg') or changed
00149         if changed:
00150             add_deps.changed = True
00151             global_changed = True
00152     if global_changed:
00153         check_exported_dependencies(package)
00154 
00155 
00156 @roscompile
00157 def target_catkin_libraries(package):
00158     CATKIN = '${catkin_LIBRARIES}'
00159     targets = package.cmake.get_libraries() + package.cmake.get_executables()
00160     for cmd in package.cmake.content_map['target_link_libraries']:
00161         tokens = cmd.get_tokens()
00162         if tokens[0] in targets:
00163             if CATKIN not in tokens:
00164                 print('\tAdding %s to target_link_libraries for %s' % (CATKIN, tokens[0]))
00165                 cmd.add_token(CATKIN)
00166             targets.remove(tokens[0])
00167             continue
00168     for target in targets:
00169         print('\tAdding target_link_libraries for %s' % target)
00170         cmd = Command('target_link_libraries')
00171         cmd.add_section('', [target, CATKIN])
00172         package.cmake.add_command(cmd)
00173 
00174 
00175 @roscompile
00176 def check_generators(package):
00177     if len(package.generators) == 0:
00178         return
00179 
00180     for gen_type, cmake_cmd in [('msg', 'add_message_files'),
00181                                 ('srv', 'add_service_files'),
00182                                 ('action', 'add_action_files')]:
00183         names = [gen.name for gen in package.generators[gen_type]]
00184         package.cmake.section_check(names, cmake_cmd, 'FILES')
00185 
00186     package.cmake.section_check(['message_generation'], 'find_package', 'COMPONENTS')
00187     package.cmake.section_check(['message_runtime'], 'catkin_package', 'CATKIN_DEPENDS')
00188     for cmd in package.cmake.content_map['catkin_package']:
00189         section = cmd.get_section('CATKIN_DEPENDS')
00190         if 'message_generation' in section.values:
00191             section.values.remove('message_generation')
00192             cmd.changed = True
00193 
00194     msg_deps = package.get_dependencies_from_msgs()
00195     if msg_deps:
00196         package.cmake.section_check(msg_deps, 'generate_messages',
00197                                     'DEPENDENCIES', zero_okay=True)
00198     else:
00199         package.cmake.section_check(msg_deps, 'generate_messages',
00200                                     zero_okay=True)
00201 
00202 
00203 @roscompile
00204 def check_includes(package):
00205     has_includes = False
00206     if package.source_code.has_header_files():
00207         package.cmake.section_check(['include'], 'catkin_package', 'INCLUDE_DIRS')
00208         package.cmake.section_check(['include'], 'include_directories')
00209         has_includes = True
00210 
00211     if len(package.source_code.get_source_by_language('c++')) > 0:
00212         package.cmake.section_check(['${catkin_INCLUDE_DIRS}'], 'include_directories')
00213         has_includes = True
00214 
00215     if not has_includes and 'include_directories' in package.cmake.content_map:
00216         for cmd in package.cmake.content_map['include_directories']:
00217             package.cmake.remove_command(cmd)
00218 
00219 
00220 @roscompile
00221 def check_library_setup(package):
00222     package.cmake.section_check(package.cmake.get_libraries(), 'catkin_package', 'LIBRARIES')
00223 
00224 
00225 def alphabetize_sections_helper(cmake):
00226     for content in cmake.contents:
00227         if content.__class__ == Command:
00228             for section in content.get_real_sections():
00229                 if section.name in SHOULD_ALPHABETIZE:
00230                     sorted_values = list(sorted(section.values))
00231                     if sorted_values != section.values:
00232                         section.values = sorted_values
00233                         content.changed = True
00234         elif content.__class__ == CommandGroup:
00235             alphabetize_sections_helper(content.sub)
00236 
00237 
00238 @roscompile
00239 def alphabetize_sections(package):
00240     alphabetize_sections_helper(package.cmake)
00241 
00242 
00243 @roscompile
00244 def prettify_catkin_package_cmd(package):
00245     for cmd in package.cmake.content_map['catkin_package']:
00246         for section in cmd.get_real_sections():
00247             section.style.prename = NEWLINE_PLUS_4
00248         cmd.changed = True
00249 
00250 
00251 @roscompile
00252 def prettify_package_lists(package):
00253     acceptable_styles = [(NEWLINE_PLUS_8, NEWLINE_PLUS_8), (NEWLINE_PLUS_4, NEWLINE_PLUS_8)]
00254 
00255     for cmd_name, section_name in [('find_package', 'COMPONENTS'), ('catkin_package', 'CATKIN_DEPENDS')]:
00256         for cmd in package.cmake.content_map[cmd_name]:
00257             for section in cmd.get_real_sections():
00258                 if section.name != section_name:
00259                     continue
00260                 n = len(str(section))
00261                 if n > 120:
00262                     key = section.style.name_val_sep, section.style.val_sep
00263                     if key not in acceptable_styles:
00264                         section.style.name_val_sep = NEWLINE_PLUS_4
00265                         section.style.val_sep = NEWLINE_PLUS_8
00266                         cmd.changed = True
00267 
00268 
00269 @roscompile
00270 def prettify_msgs_srvs(package):
00271     for cmd in package.cmake.content_map['add_message_files'] + package.cmake.content_map['add_service_files']:
00272         for section in cmd.get_real_sections():
00273             if len(section.values) > 1:
00274                 section.style.name_val_sep = NEWLINE_PLUS_4
00275                 section.style.val_sep = NEWLINE_PLUS_4
00276             cmd.changed = True
00277 
00278 
00279 @roscompile
00280 def prettify_installs(package):
00281     for cmd in package.cmake.content_map['install']:
00282         cmd.changed = True
00283         cmd.sections = [s for s in cmd.sections if type(s) != str]
00284         zeroed = False
00285         for section in cmd.sections[1:]:
00286             if len(section.values) == 0:
00287                 section.style.prename = NEWLINE_PLUS_8
00288                 zeroed = True
00289             elif not zeroed:
00290                 section.style.prename = NEWLINE_PLUS_8
00291             else:
00292                 section.style.prename = ''
00293 
00294 
00295 def remove_empty_strings(a):
00296     return filter(lambda x: x != '', a)
00297 
00298 
00299 def remove_cmake_command_comments_helper(command, ignorables, replacement=''):
00300     for i, section in enumerate(command.sections):
00301         if type(section) != str:
00302             continue
00303         for ignorable in ignorables:
00304             while ignorable in command.sections[i]:
00305                 command.changed = True
00306                 command.sections[i] = command.sections[i].replace(ignorable, replacement)
00307     if command.changed:
00308         command.sections = remove_empty_strings(command.sections)
00309         if command.sections == ['\n']:
00310             command.sections = []
00311 
00312 
00313 def remove_cmake_comments_helper(cmake, ignorables, replacement=''):
00314     for i, content in enumerate(cmake.contents):
00315         if content.__class__ == Command:
00316             remove_cmake_command_comments_helper(content, ignorables, replacement)
00317         elif content.__class__ == CommandGroup:
00318             remove_cmake_comments_helper(content.sub, ignorables, replacement)
00319         else:
00320             for ignorable in ignorables:
00321                 while ignorable in cmake.contents[i]:
00322                     cmake.contents[i] = cmake.contents[i].replace(ignorable, replacement)
00323     cmake.contents = remove_empty_strings(cmake.contents)
00324 
00325 
00326 @roscompile
00327 def remove_boilerplate_cmake_comments(package):
00328     ignorables = get_ignore_data('cmake', {'package': package.name})
00329     remove_cmake_comments_helper(package.cmake, ignorables)
00330     remove_empty_cmake_lines(package)
00331 
00332 
00333 @roscompile
00334 def remove_empty_cmake_lines(package):
00335     for i, content in enumerate(package.cmake.contents[:-2]):
00336         if str(content)[-1] == '\n' and package.cmake.contents[i + 1] == '\n' and package.cmake.contents[i + 2] == '\n':
00337             package.cmake.contents[i + 1] = ''
00338     package.cmake.contents = remove_empty_strings(package.cmake.contents)
00339 
00340 
00341 def get_cmake_clusters(cmake):
00342     anchors = cmake.get_ordered_build_targets()
00343     clusters = []
00344     current = []
00345     for content in cmake.contents:
00346         current.append(content)
00347         if type(content) == str:
00348             continue
00349         key = get_sort_key(content, anchors)
00350         clusters.append((key, current))
00351         current = []
00352     if len(current) > 0:
00353         clusters.append((get_sort_key(None, anchors), current))
00354 
00355     return sorted(clusters, key=lambda kv: kv[0])
00356 
00357 
00358 def enforce_cmake_ordering_helper(cmake):
00359     clusters = get_cmake_clusters(cmake)
00360     cmake.contents = []
00361     for key, contents in clusters:
00362         cmake.contents += contents
00363 
00364 
00365 @roscompile
00366 def enforce_cmake_ordering(package):
00367     enforce_cmake_ordering_helper(package.cmake)
00368     for group in package.cmake.content_map['group']:
00369         enforce_cmake_ordering_helper(group.sub)


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