4 VARIABLE_PATTERN = re.compile(
r'\$\{([^\}]+)\}')
5 QUOTED_PATTERN = re.compile(
r'"([^"]+)"')
7 BUILD_TARGET_COMMANDS = [
'add_library',
'add_executable',
'add_rostest',
8 'target_include_directories',
'add_dependencies',
'target_link_libraries',
9 'set_target_properties',
'ament_target_dependencies']
10 TEST_COMMANDS = [(
'group',
'CATKIN_ENABLE_TESTING'),
'catkin_download_test_data',
11 'roslint_cpp',
'roslint_python',
'roslint_add_test',
12 'catkin_add_nosetests',
'catkin_add_gtest',
'add_rostest_gtest']
13 INSTALL_COMMANDS = [
'install',
'catkin_install_python']
15 BASE_ORDERING = [
'cmake_minimum_required',
'project',
'set_directory_properties',
'find_package',
'pkg_check_modules',
16 'set',
'catkin_generate_virtualenv',
'catkin_python_setup',
'add_definitions',
17 'add_message_files',
'add_service_files',
'add_action_files',
'rosidl_generate_interfaces',
18 'generate_dynamic_reconfigure_options',
'generate_messages',
'catkin_package',
'catkin_metapackage',
19 BUILD_TARGET_COMMANDS + [
'include_directories'],
20 'ament_target_dependencies',
'ament_export_include_directories',
'ament_export_libraries',
21 'ament_export_dependencies',
26 """Examine the contents of the cmake parameter and determine the style. 28 There are four possible styles: 29 1) test_first (where test commands come strictly before install commands) 30 2) install_first (where test commands come strictly after install commands) 31 3) mixed (where test and install commands are not clearly delineated) 32 4) None (where there are only install commands, or only test commands, or neither) 35 for content
in cmake.contents:
39 elif isinstance(content, Command):
40 if content.command_name
in TEST_COMMANDS:
42 elif content.command_name
in INSTALL_COMMANDS:
47 if len(cats) == 0
or cats[-1] != cat:
55 return first_cat +
'_first' 59 """Given the style, return the correct ordering.""" 60 if style ==
'install_first':
61 return BASE_ORDERING + INSTALL_COMMANDS + TEST_COMMANDS
63 return BASE_ORDERING + TEST_COMMANDS + INSTALL_COMMANDS
68 Given a command name, determine the integer index into the ordering. 70 The ordering is a list of strings and arrays of strings. 72 If the command name matches one of the strings in the inner arrays, 73 the index of the inner array is returned. 75 If the command name matches one of the other strings, its index is returned. 77 Otherwise, the length of the ordering is returned (putting non-matches at the end) 79 for i, o
in enumerate(ordering):
83 elif command_name == o:
86 print(
'\tUnsure of ordering for ' + str(command_name))
92 Given a piece of cmake content, return a tuple representing its sort_key. 94 The first element of the tuple is the ordering_index of the content. 95 The second element is an additional variable used for sorting among elements with the same ordering_index 97 Most notably, we want all build commands with a particular library/executable to be grouped together. 98 In that case, we use the anchors parameter, which is an ordered list of all the library/executables in the file. 99 Then, the second variable is a tuple itself, with the first element being the index of library/executable in the 100 anchors list, and the second is an integer representing the canonical order of the build commands. 103 return len(ordering) + 1,
None 106 if content.__class__ == CommandGroup:
108 for token
in content.initial_tag.get_tokens(include_name=
True):
116 if content.command_name
in BUILD_TARGET_COMMANDS:
117 token = content.first_token()
118 if token
not in anchors:
119 anchors.append(token)
120 key = anchors.index(token), BUILD_TARGET_COMMANDS.index(content.command_name)
121 elif content.command_name ==
'include_directories' and 'include_directories' in anchors:
122 key = -1, anchors.index(
'include_directories')
127 def __init__(self, prename='', name_val_sep=' ', val_sep=' '):
137 def __init__(self, name='', values=None, style=None):
142 self.
values = list(values)
152 """Add the new_values to the values. 154 If alpha_order is true AND the existing values are already alphabetized, 155 add the new values in alphabetical order. 159 all_values = self.
values + list(new_values)
160 self.
values = sorted(all_values)
162 self.
values += sorted(new_values)
165 return len(self.
name) > 0
or len(self.
values) > 0
168 s = self.
style.prename
169 if len(self.
name) > 0:
171 if len(self.
values) > 0
or '\n' in self.
style.name_val_sep:
172 s += self.
style.name_val_sep
186 return [s
for s
in self.
sections if type(s) != str]
214 self.
sections = [section
for section
in self.
sections if section
not in bad_sections]
221 if include_name
and section.name:
222 tokens.append(section.name)
223 tokens += section.values
228 if len(sections) == 0:
232 last.values.append(s)
240 for section
in map(str, self.
sections):
241 if s[-1]
not in '( \n' and section[0]
not in ' \n':
244 if '\n' in s
and s[-1] !=
'\n':
261 cmd = content.initial_tag
262 return cmd.command_name ==
'if' and cmd.sections
and cmd.sections[0].name ==
'CATKIN_ENABLE_TESTING' 266 def __init__(self, file_path=None, initial_contents=None, depth=0):
268 if initial_contents
is None:
274 if content.__class__ == Command:
275 self.
content_map[content.command_name].append(content)
276 elif content.__class__ == CommandGroup:
282 tokens = cmd.get_tokens(include_name=
True)
283 self.
variables[tokens[0]] =
' '.join(tokens[1:])
293 return project_tags[0].get_tokens(include_name=
True)[0]
298 m = VARIABLE_PATTERN.search(s)
303 s = s.replace(
'${%s}' % k, v)
308 if token
and token[0] ==
'#':
310 m = QUOTED_PATTERN.match(token)
314 tokens += token.split(
' ')
327 for i, content
in enumerate(self.
contents):
328 if type(content) == str:
333 elif key[0] != len(ordering):
340 if i_index > 0
and type(self.
contents[i_index - 1]) != str:
341 sub_contents.append(
'\n')
343 sub_contents.append(
' ' * self.
depth)
344 sub_contents.append(cmd)
345 sub_contents.append(
'\n')
347 sub_contents.append(cmd)
349 sub_contents.append(
'\n')
353 if cmd.__class__ == Command:
355 elif cmd.__class__ == CommandGroup:
359 print(
'\tRemoving %s' % str(cmd).replace(
'\n',
' ').replace(
' ',
''))
369 return len(self.
content_map[
'catkin_metapackage']) > 0
376 if resolve_target_name:
377 target = resolved_tokens[0]
379 tokens = cmd.get_tokens(
True)
382 deps = resolved_tokens[1:]
413 if content.__class__ != Command:
415 if content.command_name ==
'include_directories':
416 targets.append(
'include_directories')
418 elif content.command_name
not in BUILD_TARGET_COMMANDS:
420 token = content.first_token()
421 if token
not in targets:
422 targets.append(token)
429 sections.append(content.sub)
435 test_files.update(sub.get_library_source())
436 test_files.update(sub.get_executable_source())
441 if len(sections) > 0:
443 if not create_if_needed:
448 initial_cmd.add_section(
'CATKIN_ENABLE_TESTING')
450 test_contents =
CMake(initial_contents=[
'\n'], depth=self.
depth + 1)
454 cg =
CommandGroup(initial_cmd, test_contents, final_cmd)
459 """Return the first command that matches the command name and has a matching section name. 461 If the section name is not found, return a command with the matching command name 466 s = cmd.get_section(section_name)
471 def section_check(self, items, cmd_name, section_name='', zero_okay=False, alpha_order=True):
472 """Ensure there's a CMake command of the given type with the given section name and items.""" 473 if len(items) == 0
and not zero_okay:
483 cmd.add_section(section_name, sorted(items))
486 needed_items = [item
for item
in items
if item
not in existing
and item
not in section.values]
488 section.add_values(needed_items, alpha_order)
492 """Return a list of clusters where each cluster is an array of strings with a Command/CommandGroup at the end. 494 The clusters are sorted according to the desired style. 495 The strings are grouped at the beginning to maintain the newlines and indenting before each Command. 502 current.append(content)
503 if type(content) == str:
506 clusters.append((key, current))
509 clusters.append((
get_sort_key(
None, anchors, ordering), current))
511 return [kv[1]
for kv
in sorted(clusters, key=
lambda kv: kv[0])]
514 """Determine which style to use, install_first or test_first. 516 If the default style is one of those two, use it 518 if default_style
in [
'install_first',
'test_first']:
519 desired_style = default_style
520 elif default_style
is not None:
521 raise RuntimeError(
'Configured default cmake style "{}"' 522 ' is not install_first or test_first'.format(default_style))
527 desired_style =
'test_first' 535 for contents
in clusters:
539 group.sub.enforce_ordering(default_style)
542 """Upgrade the CMake version to the new version (specified as a tuple).""" 543 for cmd
in self.
content_map[
'cmake_minimum_required']:
544 section = cmd.get_section(
'VERSION')
545 version = tuple(map(int, section.values[0].split(
'.')))
546 if version < new_version:
547 section.values[0] =
'.'.join(map(str, new_version))
551 return ''.join(map(str, self.
contents))
556 with open(fn,
'w')
as cmake:
557 cmake.write(str(self))
def get_desired_style(self, default_style=None)
def get_resolved_tokens(self, cmd, include_name=False)
def get_test_sections(self)
def enforce_ordering(self, default_style=None)
def get_ordered_build_targets(self)
def get_real_sections(self)
def remove_sections(self, key)
def get_project_name(self)
def __init__(self, file_path=None, initial_contents=None, depth=0)
def __init__(self, prename='', name_val_sep=' ', val_sep=' ')
def remove_all_commands(self, cmd_name)
def get_source_helper(self, tag)
def get_target_build_rules(self)
def __init__(self, command_name)
def __init__(self, initial_tag, sub, close_tag)
def get_ordering_index(command_name, ordering)
def get_clusters(self, desired_style)
def get_tokens(self, include_name=False)
def add_section(self, key, values=None, style=None)
def is_testing_group(content)
def remove_command(self, cmd)
def get_test_source(self)
def add_command(self, cmd)
def get_executable_source(self)
def get_test_section(self, create_if_needed=False)
def get_section(self, key)
def resolve_variables(self, var)
def __init__(self, name='', values=None, style=None)
def section_check(self, items, cmd_name, section_name='', zero_okay=False, alpha_order=True)
def get_sections(self, key)
def get_library_source(self)
def add_values(self, new_values, alpha_order=True)
def get_sort_key(content, anchors, ordering)
def upgrade_minimum_version(self, new_version)
def get_source_build_rules(self, tag, resolve_target_name=False)
def get_executables(self)
def get_command_section(self, command_name, section_name)
def get_insertion_index(self, cmd)