4 VARIABLE_PATTERN = re.compile(
r'\$\{([^\}]+)\}')
5 QUOTED_PATTERN = re.compile(
r'"([^"]+)"')
7 BUILD_TARGET_COMMANDS = [
'add_library',
'add_executable',
'add_rostest',
'add_dependencies',
'target_link_libraries']
8 TEST_COMMANDS = [
'catkin_download_test_data',
9 'roslint_cpp',
'roslint_python',
'roslint_add_test',
10 'catkin_add_nosetests',
'catkin_add_gtest',
'add_rostest_gtest']
11 INSTALL_COMMANDS = [
'install',
'catkin_install_python']
13 BASE_ORDERING = [
'cmake_minimum_required',
'project',
'set_directory_properties',
'find_package',
'pkg_check_modules',
14 'set',
'catkin_generate_virtualenv',
'catkin_python_setup',
'add_definitions',
15 'add_message_files',
'add_service_files',
'add_action_files',
'rosidl_generate_interfaces',
16 'generate_dynamic_reconfigure_options',
'generate_messages',
'catkin_package',
'catkin_metapackage',
17 BUILD_TARGET_COMMANDS + [
'include_directories'],
18 'ament_target_dependencies',
'ament_export_include_directories',
'ament_export_libraries',
19 'ament_export_dependencies',
24 """Examine the contents of the cmake parameter and determine the style. 26 There are four possible styles: 27 1) test_first (where test commands come strictly before install commands) 28 2) install_first (where test commands come strictly after install commands) 29 3) mixed (where test and install commands are not clearly delineated) 30 4) None (where there are only install commands, or only test commands, or neither) 33 for content
in cmake.contents:
37 elif isinstance(content, Command):
38 if content.command_name
in TEST_COMMANDS:
40 elif content.command_name
in INSTALL_COMMANDS:
45 if len(cats) == 0
or cats[-1] != cat:
53 return first_cat +
'_first' 57 """Given the style, return the correct ordering.""" 58 if style ==
'install_first':
59 return BASE_ORDERING + INSTALL_COMMANDS + [
'group'] + TEST_COMMANDS
61 return BASE_ORDERING + TEST_COMMANDS + [
'group'] + INSTALL_COMMANDS
66 Given a command name, determine the integer index into the ordering. 68 The ordering is a list of strings and arrays of strings. 70 If the command name matches one of the strings in the inner arrays, 71 the index of the inner array is returned. 73 If the command name matches one of the other strings, its index is returned. 75 Otherwise, the length of the ordering is returned (putting non-matches at the end) 77 for i, o
in enumerate(ordering):
81 elif command_name == o:
84 print(
'\tUnsure of ordering for ' + command_name)
90 Given a piece of cmake content, return a tuple representing its sort_key. 92 The first element of the tuple is the ordering_index of the content. 93 The second element is an additional variable used for sorting among elements with the same ordering_index 95 Most notably, we want all build commands with a particular library/executable to be grouped together. 96 In that case, we use the anchors parameter, which is an ordered list of all the library/executables in the file. 97 Then, the second variable is a tuple itself, with the first element being the index of library/executable in the 98 anchors list, and the second is an integer representing the canonical order of the build commands. 101 return len(ordering) + 1,
None 104 if content.__class__ == CommandGroup:
106 sections = content.initial_tag.get_real_sections()
107 if len(sections) > 0:
108 key = sections[0].name
111 if content.command_name
in BUILD_TARGET_COMMANDS:
112 token = content.first_token()
113 if token
not in anchors:
114 anchors.append(token)
115 key = anchors.index(token), BUILD_TARGET_COMMANDS.index(content.command_name)
116 elif content.command_name ==
'include_directories' and 'include_directories' in anchors:
117 key = -1, anchors.index(
'include_directories')
122 def __init__(self, prename='', name_val_sep=' ', val_sep=' '):
132 def __init__(self, name='', values=None, style=None):
137 self.
values = list(values)
144 self.values.append(v)
147 """Add the new_values to the values. 149 If alpha_order is true AND the existing values are already alphabetized, 150 add the new values in alphabetical order. 154 all_values = self.
values + list(new_values)
155 self.
values = sorted(all_values)
157 self.
values += sorted(new_values)
160 return len(self.
name) > 0
or len(self.
values) > 0
163 s = self.style.prename
164 if len(self.
name) > 0:
167 s += self.style.name_val_sep
168 s += self.style.val_sep.join(self.
values)
181 return [s
for s
in self.
sections if type(s) != str]
193 self.sections.append(
Section(key, values, style))
198 self.sections.append(section)
209 self.
sections = [section
for section
in self.
sections if section
not in bad_sections]
216 if include_name
and section.name:
217 tokens.append(section.name)
218 tokens += section.values
223 if len(sections) == 0:
227 last.values.append(s)
235 for section
in map(str, self.
sections):
236 if s[-1]
not in '( \n' and section[0]
not in ' \n':
239 if '\n' in s
and s[-1] !=
'\n':
256 cmd = content.initial_tag
257 return cmd.command_name ==
'if' and cmd.sections
and cmd.sections[0].name ==
'CATKIN_ENABLE_TESTING' 261 def __init__(self, file_path=None, initial_contents=None, depth=0):
263 if initial_contents
is None:
269 if content.__class__ == Command:
270 self.
content_map[content.command_name].append(content)
271 elif content.__class__ == CommandGroup:
277 tokens = cmd.get_tokens(include_name=
True)
278 self.
variables[tokens[0]] =
' '.join(tokens[1:])
288 return project_tags[0].get_tokens(include_name=
True)[0]
293 m = VARIABLE_PATTERN.search(s)
297 for k, v
in self.variables.items():
298 s = s.replace(
'${%s}' % k, v)
303 if token
and token[0] ==
'#':
305 m = QUOTED_PATTERN.match(token)
309 tokens += token.split(
' ')
322 for i, content
in enumerate(self.
contents):
323 if type(content) == str:
328 elif key[0] != len(ordering):
335 if i_index > 0
and type(self.
contents[i_index - 1]) != str:
336 sub_contents.append(
'\n')
338 sub_contents.append(
' ' * self.
depth)
339 sub_contents.append(cmd)
340 sub_contents.append(
'\n')
342 sub_contents.append(cmd)
344 sub_contents.append(
'\n')
348 if cmd.__class__ == Command:
350 elif cmd.__class__ == CommandGroup:
354 print(
'\tRemoving %s' % str(cmd).replace(
'\n',
' ').replace(
' ',
''))
355 self.contents.remove(cmd)
364 return len(self.
content_map[
'catkin_metapackage']) > 0
371 if resolve_target_name:
372 target = resolved_tokens[0]
374 tokens = cmd.get_tokens(
True)
377 deps = resolved_tokens[1:]
408 if content.__class__ != Command:
410 if content.command_name ==
'include_directories':
411 targets.append(
'include_directories')
413 elif content.command_name
not in BUILD_TARGET_COMMANDS:
415 token = content.first_token()
416 if token
not in targets:
417 targets.append(token)
424 sections.append(content.sub)
430 test_files.update(sub.get_library_source())
431 test_files.update(sub.get_executable_source())
436 if len(sections) > 0:
438 if not create_if_needed:
443 initial_cmd.add_section(
'CATKIN_ENABLE_TESTING')
445 test_contents =
CMake(initial_contents=[
'\n'], depth=self.
depth + 1)
449 cg =
CommandGroup(initial_cmd, test_contents, final_cmd)
454 """Return the first command that matches the command name and has a matching section name. 456 If the section name is not found, return a command with the matching command name 461 s = cmd.get_section(section_name)
466 def section_check(self, items, cmd_name, section_name='', zero_okay=False, alpha_order=True):
467 """Ensure there's a CMake command of the given type with the given section name and items.""" 468 if len(items) == 0
and not zero_okay:
478 cmd.add_section(section_name, sorted(items))
481 needed_items = [item
for item
in items
if item
not in existing
and item
not in section.values]
483 section.add_values(needed_items, alpha_order)
487 """Return a list of clusters where each cluster is an array of strings with a Command/CommandGroup at the end. 489 The clusters are sorted according to the desired style. 490 The strings are grouped at the beginning to maintain the newlines and indenting before each Command. 497 current.append(content)
498 if type(content) == str:
501 clusters.append((key, current))
504 clusters.append((
get_sort_key(
None, anchors, ordering), current))
506 return [kv[1]
for kv
in sorted(clusters, key=
lambda kv: kv[0])]
509 """Determine which style to use, install_first or test_first. 511 If the default style is one of those two, use it 513 if default_style
in [
'install_first',
'test_first']:
514 desired_style = default_style
515 elif default_style
is not None:
516 raise RuntimeError(
'Configured default cmake style "{}"' 517 ' is not install_first or test_first'.format(default_style))
522 desired_style =
'test_first' 530 for contents
in clusters:
534 group.sub.enforce_ordering(default_style)
537 """Upgrade the CMake version to the new version (specified as a tuple).""" 538 for cmd
in self.
content_map[
'cmake_minimum_required']:
539 section = cmd.get_section(
'VERSION')
540 version = tuple(map(int, section.values[0].split(
'.')))
541 if version < new_version:
542 section.values[0] =
'.'.join(map(str, new_version))
546 return ''.join(map(str, self.
contents))
551 with open(fn,
'w')
as cmake:
552 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)