4 from xml.dom.minidom
import parse
6 DEPEND_ORDERING = [
'buildtool_depend',
'depend',
'build_depend',
'build_export_depend',
7 'run_depend',
'exec_depend',
'test_depend',
'doc_depend']
9 ORDERING = [
'name',
'version',
'description',
10 [
'maintainer',
'license',
'author',
'url']] + DEPEND_ORDERING + [
'export']
12 INDENT_PATTERN = re.compile(
'\n *')
14 PEOPLE_TAGS = [
'maintainer',
'author']
16 BUILD_TYPES = {
'catkin',
'cmake'}
18 FORMAT_3_HEADER =
"""<?xml version="1.0"?> 20 href="http://download.ros.org/schema/package_format3.xsd" 21 schematypens="http://www.w3.org/2001/XMLSchema"?> 26 for i, o
in enumerate(ORDERING):
33 print(
'\tUnsure of ordering for ' + name)
45 while c < len(s)
and s[-c - 1] ==
' ':
51 """Replace all the elements with tags in source_tags with new elements with new_tag.""" 53 for tag
in source_tags:
54 pkgs = set(manifest.get_packages_by_tag(tag))
55 if intersection
is None:
58 intersection = intersection.intersection(pkgs)
59 for tag
in source_tags:
60 manifest.remove_dependencies(tag, intersection)
61 manifest.insert_new_packages(new_tag, intersection)
68 self.
root = self.
tree.getElementsByTagName(
'package')[0]
69 contents = open(fn).read()
79 if self.
_name is not None:
81 name_tags = self.
root.getElementsByTagName(
'name')
84 name_tag = name_tags[0]
85 self.
_name = name_tag.firstChild.nodeValue
92 if not self.
root.hasAttribute(
'format'):
95 self.
_format = int(self.
root.attributes[
'format'].value)
105 for tag
in self.
root.getElementsByTagName(
'build_type') + self.
root.getElementsByTagName(
'buildtool_depend'):
106 value = tag.firstChild.nodeValue
107 if value
in BUILD_TYPES:
108 build_types.add(value)
110 if len(build_types) == 1:
113 elif not build_types:
114 raise RuntimeError(
'Unable to determine buildtool type in {}'.
format(self.
fn))
116 raise RuntimeError(
'Too many valid buildtool types')
122 tab_ct = collections.defaultdict(int)
123 for c
in self.
root.childNodes:
124 if c.nodeType == c.TEXT_NODE:
130 self.
_std_tab = max(tab_ct.items(), key=operator.itemgetter(1))[0]
135 for el
in self.
root.getElementsByTagName(tag):
136 pkgs.append(el.childNodes[0].nodeValue)
142 keys.append(
'build_depend')
144 keys.append(
'run_depend')
145 if self.
format >= 2
and mode !=
'test':
146 keys.append(
'depend')
148 keys.append(
'exec_depend')
150 keys.append(
'test_depend')
157 return self.
tree.createTextNode(
'\n' +
' ' * (self.
std_tab * tabs))
160 """Return a dictionary based on which children span which indexes. 162 The keys are the types of nodes in the xml (build_depend, maintainer, etc). 163 The values are arrays marking the range of elements in the xml root that match that tag. 165 For example, tags[build_depend] = [(5, 9), (11, 50)] means that elements [5, 9) and [11, 50) are 166 either build_depend elements (or the strings between them) 168 tags = collections.defaultdict(list)
173 while i < len(self.
root.childNodes):
174 child = self.
root.childNodes[i]
175 if child.nodeType == child.TEXT_NODE:
179 name = child.nodeName
182 tags[current].append((current_start, current_last))
188 tags[current].append((current_start, current_last))
192 """Return the index where to insert a new element with the given tag type. 194 If there are already elements of that type, then either insert after the last matching element, 195 or if the list is alphabetized, insert it in the correct place alphabetically using the tag_value. 196 Otherwise, look at the existing elements, and find ones that are supposed to come the closest 197 before the given tag, and insert after them. If none found, add at the end. 202 if len(indexes[tag]) == 1
and tag
in DEPEND_ORDERING:
203 start, end = indexes[tag][0]
206 for i
in range(start, end + 1):
207 child = self.
root.childNodes[i]
208 if child.nodeType == child.TEXT_NODE:
210 value = child.firstChild.data
211 tag_values.append(value)
212 if tag_value >= value:
216 if tag_values
and sorted(tag_values) == tag_values:
218 if tag_value <= tag_values[0]:
222 if tag_value <= tag_values[-1]:
226 return indexes[tag][-1][1]
239 if best_tag
is None or ni > best_index
or indexes[tag][-1] > indexes[best_tag][-1]:
244 return len(self.
root.childNodes)
246 return indexes[best_tag][-1][1]
249 if tag.tagName
in DEPEND_ORDERING:
250 value = tag.firstChild.data
255 before = self.
root.childNodes[:index + 1]
256 after = self.
root.childNodes[index + 1:]
262 if before
and before[-1].nodeType == before[-1].TEXT_NODE:
263 new_bits = [tag, new_tab_element]
266 new_bits = [new_tab_element, tag]
268 self.
root.childNodes = before + new_bits + after
278 all_elements.append(tag)
280 if len(parent.childNodes) == 0:
283 parent.childNodes = parent.childNodes[:-1] + all_elements + parent.childNodes[-1:]
287 for pkg
in sorted(values):
288 print(
'\tInserting %s: %s' % (tag, pkg))
289 node = self.
tree.createElement(tag)
290 node.appendChild(self.
tree.createTextNode(pkg))
293 def add_packages(self, build_depends, run_depends, test_depends=None, prefer_depend_tag=True):
295 run_depends.update(build_depends)
298 build_depends = build_depends - existing_build
299 run_depends = run_depends - existing_run
303 elif prefer_depend_tag:
304 depend_tags = build_depends.union(run_depends)
313 both = build_depends.intersection(run_depends)
319 if test_depends
is not None and len(test_depends) > 0:
321 test_depends = set(test_depends) - existing_build - build_depends - existing_test
325 """Remove the given element AND the text element before it if it is just an indentation.""" 326 parent = element.parentNode
327 index = parent.childNodes.index(element)
329 previous = parent.childNodes[index - 1]
330 if previous.nodeType == previous.TEXT_NODE
and INDENT_PATTERN.match(previous.nodeValue):
331 parent.removeChild(previous)
332 parent.removeChild(element)
336 for el
in self.
root.getElementsByTagName(name):
337 pkg = el.childNodes[0].nodeValue
340 print(
'\tRemoving %s %s' % (name, pkg))
346 elements += self.
root.getElementsByTagName(tag)
352 name = el.childNodes[0].nodeValue
353 email = el.getAttribute(
'email')
354 people.append((name, email))
357 def update_people(self, target_name, target_email=None, search_name=None, search_email=None):
359 name = el.childNodes[0].nodeValue
360 email = el.getAttribute(
'email')
if el.hasAttribute(
'email')
else '' 361 if (search_name
is None or name == search_name)
and (search_email
is None or email == search_email):
362 el.childNodes[0].nodeValue = target_name
364 el.setAttribute(
'email', target_email)
365 print(
'\tReplacing %s %s/%s with %s/%s' % (el.nodeName, name, email, target_name, target_email))
369 els = self.
root.getElementsByTagName(
'license')
376 return el.childNodes[0].nodeValue
380 if license != el.childNodes[0].nodeValue:
381 el.childNodes[0].nodeValue = license_str
385 for node
in self.
root.getElementsByTagName(
'export'):
386 for child
in node.childNodes:
387 if child.nodeType == child.ELEMENT_NODE:
388 if child.nodeName ==
'metapackage':
393 """Return a mapping from the package name to a list of the relative path(s) for the plugin xml(s).""" 394 xmls = collections.defaultdict(list)
395 export = self.
root.getElementsByTagName(
'export')
399 for n
in ex.childNodes:
400 if n.nodeType == self.
root.ELEMENT_NODE:
401 plugin = n.getAttribute(
'plugin').replace(
'${prefix}/',
'')
402 xmls[n.nodeName].append(plugin)
406 """Get the export tag. Create it if it doesn't exist.""" 407 export_tags = self.
root.getElementsByTagName(
'export')
408 if len(export_tags) == 0:
409 export_tag = self.
tree.createElement(
'export')
413 return export_tags[0]
416 """Add the plugin configuration if not found. Add export tag as needed. Return the surrounding export tag.""" 419 attr =
'${prefix}/' + xml_path
420 for tag
in ex_tag.childNodes:
421 if tag.nodeName != pkg_name:
423 plugin = tag.attributes.get(
'plugin')
424 if plugin
and plugin.value == attr:
427 pe = self.
tree.createElement(pkg_name)
428 pe.setAttribute(
'plugin', attr)
433 if self.
format == new_format:
435 print(
'%s already in format %d!' % (self.
name, self.
format))
438 if new_format
not in [2, 3]:
439 raise RuntimeError(
'Unknown PackageXML version: ' + repr(new_format))
445 self.
root.setAttribute(
'format',
'2')
453 self.
root.setAttribute(
'format',
'3')
454 self.
header = FORMAT_3_HEADER
462 if new_fn == self.
fn and not self.
changed:
465 s = self.
tree.toxml(self.
tree.encoding)
467 s = self.
header + s[index:] +
'\n' 469 with open(new_fn,
'wb')
as f:
470 f.write(s.encode(
'UTF-8'))
def count_trailing_spaces(s)
def upgrade(self, new_format=2, quiet=True)
def get_license_element(self)
def get_elements_by_tags(self, tags)
def set_license(self, license_str)
def update_people(self, target_name, target_email=None, search_name=None, search_email=None)
def replace_package_set(manifest, source_tags, new_tag)
def get_package_tag_index(s, key='< package')
def remove_dependencies(self, name, pkgs, quiet=False)
def get_packages_by_tag(self, tag)
def add_packages(self, build_depends, run_depends, test_depends=None, prefer_depend_tag=True)
def get_plugin_xmls(self)
def insert_new_packages(self, tag, values)
def get_tab_element(self, tabs=1)
def write(self, new_fn=None)
def get_insertion_index(self, tag, tag_value=None)
def add_plugin_export(self, pkg_name, xml_path)
def get_ordering_index(name, whiny=True)
def get_child_indexes(self)
def insert_new_tags(self, tags)
def get_packages(self, mode='build')
def remove_element(self, element)
def insert_new_tag(self, tag)
def insert_new_tag_inside_another(self, parent, tag, depth=2)