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
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
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)