diff options
author | Robin H. Johnson <robbat2@gentoo.org> | 2015-08-08 13:49:04 -0700 |
---|---|---|
committer | Robin H. Johnson <robbat2@gentoo.org> | 2015-08-08 17:38:18 -0700 |
commit | 56bd759df1d0c750a065b8c845e93d5dfa6b549d (patch) | |
tree | 3f91093cdb475e565ae857f1c5a7fd339e2d781e /dev-util/gprof2dot | |
download | gentoo-56bd759df1d0c750a065b8c845e93d5dfa6b549d.tar.gz gentoo-56bd759df1d0c750a065b8c845e93d5dfa6b549d.tar.bz2 gentoo-56bd759df1d0c750a065b8c845e93d5dfa6b549d.zip |
proj/gentoo: Initial commit
This commit represents a new era for Gentoo:
Storing the gentoo-x86 tree in Git, as converted from CVS.
This commit is the start of the NEW history.
Any historical data is intended to be grafted onto this point.
Creation process:
1. Take final CVS checkout snapshot
2. Remove ALL ChangeLog* files
3. Transform all Manifests to thin
4. Remove empty Manifests
5. Convert all stale $Header$/$Id$ CVS keywords to non-expanded Git $Id$
5.1. Do not touch files with -kb/-ko keyword flags.
Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
X-Thanks: Alec Warner <antarus@gentoo.org> - did the GSoC 2006 migration tests
X-Thanks: Robin H. Johnson <robbat2@gentoo.org> - infra guy, herding this project
X-Thanks: Nguyen Thai Ngoc Duy <pclouds@gentoo.org> - Former Gentoo developer, wrote Git features for the migration
X-Thanks: Brian Harring <ferringb@gentoo.org> - wrote much python to improve cvs2svn
X-Thanks: Rich Freeman <rich0@gentoo.org> - validation scripts
X-Thanks: Patrick Lauer <patrick@gentoo.org> - Gentoo dev, running new 2014 work in migration
X-Thanks: Michał Górny <mgorny@gentoo.org> - scripts, QA, nagging
X-Thanks: All of other Gentoo developers - many ideas and lots of paint on the bikeshed
Diffstat (limited to 'dev-util/gprof2dot')
-rw-r--r-- | dev-util/gprof2dot/Manifest | 1 | ||||
-rw-r--r-- | dev-util/gprof2dot/files/gprof2dot-0_p20100216-python3.patch | 489 | ||||
-rw-r--r-- | dev-util/gprof2dot/files/gprof2dot-0_p20130517-py3-xrange.patch | 10 | ||||
-rw-r--r-- | dev-util/gprof2dot/gprof2dot-0_p20130517.ebuild | 51 | ||||
-rw-r--r-- | dev-util/gprof2dot/metadata.xml | 8 |
5 files changed, 559 insertions, 0 deletions
diff --git a/dev-util/gprof2dot/Manifest b/dev-util/gprof2dot/Manifest new file mode 100644 index 000000000000..f5e3cfc4d54b --- /dev/null +++ b/dev-util/gprof2dot/Manifest @@ -0,0 +1 @@ +DIST gprof2dot-0_p20130517.tar.xz 1080464 SHA256 d491a8048a7ebc169557393ee517ede3c3958696e5257fbe3698bfbab6218691 SHA512 1907cb889d1776723ca944d588fa4185afdeee11cc85ec40c03832568d91612bab708160c69036fc45fef05bca62c5bc8f6ef19cbb45c6bfd1952dda94c54a4b WHIRLPOOL 1f04729faf6fa47921c3b953d4a38db598d9905a35d198ff7680e778f214e5ec54026840de7a8bcfaf351fdceadd3d0ba3865dbbe95cbc28704b6cab9670b2d7 diff --git a/dev-util/gprof2dot/files/gprof2dot-0_p20100216-python3.patch b/dev-util/gprof2dot/files/gprof2dot-0_p20100216-python3.patch new file mode 100644 index 000000000000..875ccbc1ebd9 --- /dev/null +++ b/dev-util/gprof2dot/files/gprof2dot-0_p20100216-python3.patch @@ -0,0 +1,489 @@ +From 6087a16e81d5c41647e05291dd25bb6eac9493eb Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping <sebastian@pipping.org> +Date: Fri, 5 Nov 2010 18:10:29 +0100 +Subject: [PATCH] Support both Python 2.x and 3.x + +--- + gprof2dot.py | 138 ++++++++++++++++++++++++++++++++++------------------------ + 1 files changed, 81 insertions(+), 57 deletions(-) + +diff --git a/gprof2dot.py b/gprof2dot.py +index bf0aba8..888081f 100755 +--- a/gprof2dot.py ++++ b/gprof2dot.py +@@ -32,6 +32,27 @@ import optparse + import xml.parsers.expat + + ++# Python 2.x/3.x compatibility ++if sys.version_info[0] == 3: ++ PYTHON_3 = True ++ def compat_iteritems(x): return x.items() # No iteritems() in Python 3 ++ def compat_itervalues(x): return x.values() # No itervalues() in Python 3 ++ def compat_keys(x): return list(x.keys()) # keys() is a generator in Python 3 ++ compat_basestring = str # No class basestring in Python 3 ++ ++ CALL_TIMES_FORMAT = "%u\xd7" # All strings are unicode in Python 3, no u"" marking ++else: ++ PYTHON_3 = False ++ def compat_iteritems(x): return x.iteritems() ++ def compat_itervalues(x): return x.itervalues() ++ def compat_keys(x): return x.keys() ++ compat_basestring = basestring ++ ++ # u"" strings not supported in Python 3 ++ # By using eval() we don't get a syntax error ++ CALL_TIMES_FORMAT = eval('u"%u\xd7"') ++ ++ + try: + # Debugging helper module + import debug +@@ -40,7 +61,7 @@ except ImportError: + + + def times(x): +- return u"%u\xd7" % (x,) ++ return CALL_TIMES_FORMAT % (x,) + + def percentage(p): + return "%.02f%%" % (p*100.0,) +@@ -236,8 +257,8 @@ class Profile(Object): + def validate(self): + """Validate the edges.""" + +- for function in self.functions.itervalues(): +- for callee_id in function.calls.keys(): ++ for function in compat_itervalues(self.functions): ++ for callee_id in compat_keys(function.calls): + assert function.calls[callee_id].callee_id == callee_id + if callee_id not in self.functions: + sys.stderr.write('warning: call to undefined function %s from function %s\n' % (str(callee_id), function.name)) +@@ -248,11 +269,11 @@ class Profile(Object): + + # Apply the Tarjan's algorithm successively until all functions are visited + visited = set() +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + if function not in visited: + self._tarjan(function, 0, [], {}, {}, visited) + cycles = [] +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + if function.cycle is not None and function.cycle not in cycles: + cycles.append(function.cycle) + self.cycles = cycles +@@ -275,7 +296,7 @@ class Profile(Object): + order += 1 + pos = len(stack) + stack.append(function) +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + callee = self.functions[call.callee_id] + # TODO: use a set to optimize lookup + if callee not in orders: +@@ -299,10 +320,10 @@ class Profile(Object): + for cycle in self.cycles: + cycle_totals[cycle] = 0.0 + function_totals = {} +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + function_totals[function] = 0.0 +- for function in self.functions.itervalues(): +- for call in function.calls.itervalues(): ++ for function in compat_itervalues(self.functions): ++ for call in compat_itervalues(function.calls): + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + function_totals[callee] += call[event] +@@ -310,8 +331,8 @@ class Profile(Object): + cycle_totals[callee.cycle] += call[event] + + # Compute the ratios +- for function in self.functions.itervalues(): +- for call in function.calls.itervalues(): ++ for function in compat_itervalues(self.functions): ++ for call in compat_itervalues(function.calls): + assert call.ratio is None + if call.callee_id != function.id: + callee = self.functions[call.callee_id] +@@ -332,10 +353,10 @@ class Profile(Object): + + # Sanity checking + assert outevent not in self +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + assert outevent not in function + assert inevent in function +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + assert outevent not in call + if call.callee_id != function.id: + assert call.ratio is not None +@@ -343,13 +364,13 @@ class Profile(Object): + # Aggregate the input for each cycle + for cycle in self.cycles: + total = inevent.null() +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + total = inevent.aggregate(total, function[inevent]) + self[inevent] = total + + # Integrate along the edges + total = inevent.null() +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + total = inevent.aggregate(total, function[inevent]) + self._integrate_function(function, outevent, inevent) + self[outevent] = total +@@ -360,7 +381,7 @@ class Profile(Object): + else: + if outevent not in function: + total = function[inevent] +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + if call.callee_id != function.id: + total += self._integrate_call(call, outevent, inevent) + function[outevent] = total +@@ -381,7 +402,7 @@ class Profile(Object): + total = inevent.null() + for member in cycle.functions: + subtotal = member[inevent] +- for call in member.calls.itervalues(): ++ for call in compat_itervalues(member.calls): + callee = self.functions[call.callee_id] + if callee.cycle is not cycle: + subtotal += self._integrate_call(call, outevent, inevent) +@@ -390,9 +411,9 @@ class Profile(Object): + + # Compute the time propagated to callers of this cycle + callees = {} +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + if function.cycle is not cycle: +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + callee = self.functions[call.callee_id] + if callee.cycle is cycle: + try: +@@ -403,7 +424,7 @@ class Profile(Object): + for member in cycle.functions: + member[outevent] = outevent.null() + +- for callee, call_ratio in callees.iteritems(): ++ for callee, call_ratio in compat_iteritems(callees): + ranks = {} + call_ratios = {} + partials = {} +@@ -418,7 +439,7 @@ class Profile(Object): + def _rank_cycle_function(self, cycle, function, rank, ranks): + if function not in ranks or ranks[function] > rank: + ranks[function] = rank +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + if callee.cycle is cycle: +@@ -427,7 +448,7 @@ class Profile(Object): + def _call_ratios_cycle(self, cycle, function, ranks, call_ratios, visited): + if function not in visited: + visited.add(function) +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + if callee.cycle is cycle: +@@ -438,7 +459,7 @@ class Profile(Object): + def _integrate_cycle_function(self, cycle, function, partial_ratio, partials, ranks, call_ratios, outevent, inevent): + if function not in partials: + partial = partial_ratio*function[inevent] +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + if call.callee_id != function.id: + callee = self.functions[call.callee_id] + if callee.cycle is not cycle: +@@ -465,7 +486,7 @@ class Profile(Object): + """Aggregate an event for the whole profile.""" + + total = event.null() +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + try: + total = event.aggregate(total, function[event]) + except UndefinedEvent: +@@ -475,11 +496,11 @@ class Profile(Object): + def ratio(self, outevent, inevent): + assert outevent not in self + assert inevent in self +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + assert outevent not in function + assert inevent in function + function[outevent] = ratio(function[inevent], self[inevent]) +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + assert outevent not in call + if inevent in call: + call[outevent] = ratio(call[inevent], self[inevent]) +@@ -489,13 +510,13 @@ class Profile(Object): + """Prune the profile""" + + # compute the prune ratios +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + try: + function.weight = function[TOTAL_TIME_RATIO] + except UndefinedEvent: + pass + +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + callee = self.functions[call.callee_id] + + if TOTAL_TIME_RATIO in call: +@@ -509,24 +530,24 @@ class Profile(Object): + pass + + # prune the nodes +- for function_id in self.functions.keys(): ++ for function_id in compat_keys(self.functions): + function = self.functions[function_id] + if function.weight is not None: + if function.weight < node_thres: + del self.functions[function_id] + + # prune the egdes +- for function in self.functions.itervalues(): +- for callee_id in function.calls.keys(): ++ for function in compat_itervalues(self.functions): ++ for callee_id in compat_keys(function.calls): + call = function.calls[callee_id] + if callee_id not in self.functions or call.weight is not None and call.weight < edge_thres: + del function.calls[callee_id] + + def dump(self): +- for function in self.functions.itervalues(): ++ for function in compat_itervalues(self.functions): + sys.stderr.write('Function %s:\n' % (function.name,)) + self._dump_events(function.events) +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + callee = self.functions[call.callee_id] + sys.stderr.write(' Call %s:\n' % (callee.name,)) + self._dump_events(call.events) +@@ -537,7 +558,7 @@ class Profile(Object): + sys.stderr.write(' Function %s\n' % (function.name,)) + + def _dump_events(self, events): +- for event, value in events.iteritems(): ++ for event, value in compat_iteritems(events): + sys.stderr.write(' %s: %s\n' % (event.name, event.format(value))) + + +@@ -695,7 +716,7 @@ class XmlTokenizer: + self.final = len(data) < size + try: + self.parser.Parse(data, self.final) +- except xml.parsers.expat.ExpatError, e: ++ except xml.parsers.expat.ExpatError as e: + #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS: + if e.code == 3: + pass +@@ -801,7 +822,7 @@ class GprofParser(Parser): + """Extract a structure from a match object, while translating the types in the process.""" + attrs = {} + groupdict = mo.groupdict() +- for name, value in groupdict.iteritems(): ++ for name, value in compat_iteritems(groupdict): + if value is None: + value = None + elif self._int_re.match(value): +@@ -977,7 +998,7 @@ class GprofParser(Parser): + for index in self.cycles.iterkeys(): + cycles[index] = Cycle() + +- for entry in self.functions.itervalues(): ++ for entry in compat_itervalues(self.functions): + # populate the function + function = Function(entry.index, entry.name) + function[TIME] = entry.self +@@ -1019,7 +1040,7 @@ class GprofParser(Parser): + + profile[TIME] = profile[TIME] + function[TIME] + +- for cycle in cycles.itervalues(): ++ for cycle in compat_itervalues(cycles): + profile.add_cycle(cycle) + + # Compute derived events +@@ -1350,7 +1371,7 @@ class OprofileParser(LineParser): + self.update_subentries_dict(callees_total, callees) + + def update_subentries_dict(self, totals, partials): +- for partial in partials.itervalues(): ++ for partial in compat_itervalues(partials): + try: + total = totals[partial.id] + except KeyError: +@@ -1372,7 +1393,7 @@ class OprofileParser(LineParser): + + # populate the profile + profile[SAMPLES] = 0 +- for _callers, _function, _callees in self.entries.itervalues(): ++ for _callers, _function, _callees in compat_itervalues(self.entries): + function = Function(_function.id, _function.name) + function[SAMPLES] = _function.samples + profile.add_function(function) +@@ -1384,10 +1405,10 @@ class OprofileParser(LineParser): + function.module = os.path.basename(_function.image) + + total_callee_samples = 0 +- for _callee in _callees.itervalues(): ++ for _callee in compat_itervalues(_callees): + total_callee_samples += _callee.samples + +- for _callee in _callees.itervalues(): ++ for _callee in compat_itervalues(_callees): + if not _callee.self: + call = Call(_callee.id) + call[SAMPLES2] = _callee.samples +@@ -1548,7 +1569,7 @@ class SysprofParser(XmlParser): + profile = Profile() + + profile[SAMPLES] = 0 +- for id, object in objects.iteritems(): ++ for id, object in compat_iteritems(objects): + # Ignore fake objects (process names, modules, "Everything", "kernel", etc.) + if object['self'] == 0: + continue +@@ -1558,7 +1579,7 @@ class SysprofParser(XmlParser): + profile.add_function(function) + profile[SAMPLES] += function[SAMPLES] + +- for id, node in nodes.iteritems(): ++ for id, node in compat_iteritems(nodes): + # Ignore fake calls + if node['self'] == 0: + continue +@@ -1672,7 +1693,7 @@ class SharkParser(LineParser): + + profile = Profile() + profile[SAMPLES] = 0 +- for _function, _callees in self.entries.itervalues(): ++ for _function, _callees in compat_itervalues(self.entries): + function = Function(_function.id, _function.name) + function[SAMPLES] = _function.samples + profile.add_function(function) +@@ -1681,7 +1702,7 @@ class SharkParser(LineParser): + if _function.image: + function.module = os.path.basename(_function.image) + +- for _callee in _callees.itervalues(): ++ for _callee in compat_itervalues(_callees): + call = Call(_callee.id) + call[SAMPLES] = _callee.samples + function.add_call(call) +@@ -1965,7 +1986,8 @@ class PstatsParser: + self.profile = Profile() + self.function_ids = {} + +- def get_function_name(self, (filename, line, name)): ++ def get_function_name(self, info): ++ filename, line, name = info + module = os.path.splitext(filename)[0] + module = os.path.basename(module) + return "%s:%d:%s" % (module, line, name) +@@ -1986,14 +2008,14 @@ class PstatsParser: + def parse(self): + self.profile[TIME] = 0.0 + self.profile[TOTAL_TIME] = self.stats.total_tt +- for fn, (cc, nc, tt, ct, callers) in self.stats.stats.iteritems(): ++ for fn, (cc, nc, tt, ct, callers) in compat_iteritems(self.stats.stats): + callee = self.get_function(fn) + callee.called = nc + callee[TOTAL_TIME] = ct + callee[TIME] = tt + self.profile[TIME] += tt + self.profile[TOTAL_TIME] = max(self.profile[TOTAL_TIME], ct) +- for fn, value in callers.iteritems(): ++ for fn, value in compat_iteritems(callers): + caller = self.get_function(fn) + call = Call(callee.id) + if isinstance(value, tuple): +@@ -2190,7 +2212,7 @@ class DotWriter: + self.attr('node', fontname=fontname, shape="box", style="filled", fontcolor="white", width=0, height=0) + self.attr('edge', fontname=fontname) + +- for function in profile.functions.itervalues(): ++ for function in compat_itervalues(profile.functions): + labels = [] + if function.process is not None: + labels.append(function.process) +@@ -2202,7 +2224,7 @@ class DotWriter: + label = event.format(function[event]) + labels.append(label) + if function.called is not None: +- labels.append(u"%u\xd7" % (function.called,)) ++ labels.append(CALL_TIMES_FORMAT % (function.called,)) + + if function.weight is not None: + weight = function.weight +@@ -2217,7 +2239,7 @@ class DotWriter: + fontsize = "%.2f" % theme.node_fontsize(weight), + ) + +- for call in function.calls.itervalues(): ++ for call in compat_itervalues(function.calls): + callee = profile.functions[call.callee_id] + + labels = [] +@@ -2278,7 +2300,7 @@ class DotWriter: + return + self.write(' [') + first = True +- for name, value in attrs.iteritems(): ++ for name, value in compat_iteritems(attrs): + if first: + first = False + else: +@@ -2291,7 +2313,7 @@ class DotWriter: + def id(self, id): + if isinstance(id, (int, float)): + s = str(id) +- elif isinstance(id, basestring): ++ elif isinstance(id, compat_basestring): + if id.isalnum() and not id.startswith('0x'): + s = id + else: +@@ -2300,7 +2322,8 @@ class DotWriter: + raise TypeError + self.write(s) + +- def color(self, (r, g, b)): ++ def color(self, rgb): ++ r, g, b = rgb + + def float2int(f): + if f <= 0.0: +@@ -2312,7 +2335,8 @@ class DotWriter: + return "#" + "".join(["%02x" % float2int(c) for c in (r, g, b)]) + + def escape(self, s): +- s = s.encode('utf-8') ++ if not PYTHON_3: ++ s = s.encode('utf-8') + s = s.replace('\\', r'\\') + s = s.replace('\n', r'\n') + s = s.replace('\t', r'\t') +@@ -2505,7 +2529,7 @@ class Main: + profile = self.profile + profile.prune(self.options.node_thres/100.0, self.options.edge_thres/100.0) + +- for function in profile.functions.itervalues(): ++ for function in compat_itervalues(profile.functions): + function.name = self.compress_function_name(function.name) + + dot.graph(profile, self.theme) +-- +1.7.3.2 + diff --git a/dev-util/gprof2dot/files/gprof2dot-0_p20130517-py3-xrange.patch b/dev-util/gprof2dot/files/gprof2dot-0_p20130517-py3-xrange.patch new file mode 100644 index 000000000000..228b1d8230d7 --- /dev/null +++ b/dev-util/gprof2dot/files/gprof2dot-0_p20130517-py3-xrange.patch @@ -0,0 +1,10 @@ +--- gprof2dot.py 2013-05-29 19:18:46.217823810 +0300 ++++ gprof2dot.py 2013-06-16 12:12:28.095478734 +0300 +@@ -39,6 +39,7 @@ + def compat_keys(x): return list(x.keys()) # keys() is a generator in Python 3 + basestring = str # No class basestring in Python 3 + unichr = chr # No unichr in Python 3 ++ xrange = range # No xrange in Python 3 + else: + PYTHON_3 = False + def compat_iteritems(x): return x.iteritems() diff --git a/dev-util/gprof2dot/gprof2dot-0_p20130517.ebuild b/dev-util/gprof2dot/gprof2dot-0_p20130517.ebuild new file mode 100644 index 000000000000..ec4cff4d96dc --- /dev/null +++ b/dev-util/gprof2dot/gprof2dot-0_p20130517.ebuild @@ -0,0 +1,51 @@ +# Copyright 1999-2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +EAPI="5" + +PYTHON_COMPAT=( python{2_7,3_3,3_4} ) +PYTHON_REQ_USE='xml' + +inherit eutils python-r1 + +DESCRIPTION="Converts profiling output to dot graphs" +HOMEPAGE="http://code.google.com/p/jrfonseca/wiki/Gprof2Dot" +SRC_URI="http://www.hartwork.org/public/${P}.tar.xz" + +LICENSE="GPL-3" +SLOT="0" +KEYWORDS="~amd64 ~x86" +IUSE="" + +DEPEND="" +RDEPEND="" + +REQUIRED_USE="${PYTHON_REQUIRED_USE}" + +src_prepare() { + epatch "${FILESDIR}"/${P}-py3-xrange.patch +} + +_make_call_script() { + cat <<-EOF >"${D}/$1" + #! /usr/bin/env python + from gprof2dot import Main + Main().main() + EOF + + fperms a+x "$1" || die +} + +src_install() { + abi_specific_install() { + insinto "$(python_get_sitedir)" + doins ${PN}.py || die + python_optimize || die + } + python_parallel_foreach_impl abi_specific_install + + dodir /usr/bin || die + _make_call_script /usr/bin/${PN} || die + python_replicate_script "${D}"/usr/bin/${PN} || die +} diff --git a/dev-util/gprof2dot/metadata.xml b/dev-util/gprof2dot/metadata.xml new file mode 100644 index 000000000000..16bcc9f800a2 --- /dev/null +++ b/dev-util/gprof2dot/metadata.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd"> +<pkgmetadata> + <maintainer> + <email>sping@gentoo.org</email> + <name>Sebastian Pipping</name> + </maintainer> +</pkgmetadata> |