1
# Copyright (C) 2004 Aaron Bentley
2
# <aaron.bentley@utoronto.ca>
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
__docformat__ = "restructuredtext"
19
__doc__ = "Utility functionality not part of core Arch"
33
def iter_alias(my_file):
35
Iterate through aliases in the supplied file.
37
:param my_file: The name of the file to read. May include ~/ notation.
39
:rtype: iterator of list of string
41
tmp = os.path.expanduser(my_file)
42
if not os.access(tmp, os.R_OK) or not os.path.isfile(tmp):
44
for line in open(tmp):
45
line=line.rstrip("\n")
46
if line.startswith("#") or len(line)==0 or line.isspace():
48
parts=string.split(line, "=")
49
parts[0]=parts[0].strip()
50
parts[1]=parts[1].strip()
52
raise errors.CantParseAlias(line)
54
if parts[1][-1]=="\"":
55
parts[1]=parts[1][1:-1]
57
raise errors.CantParseAlias(line)
58
check_alias(line, parts)
62
def check_alias(line, parts):
63
"""Ensures that a given alias name is suitable for use"""
64
if '@' in parts[0] or '/' in parts[0] or '--' in parts[0]:
65
raise errors.ForbiddenAliasSyntax(line)
68
def iter_all_alias(tree):
70
Iterate all relevent aliases.
72
An alias name may appear more than once in the output. The last value
73
to appear is the one that should be used. Iterates through ~/.aba/aliases
76
:param tree: The working tree to seek aliases in (may be None)
77
:type tree: `arch.ArchSourceTree`
78
:rtype: iterator of list of string
80
for parts in iter_alias("~/.aba/aliases"):
85
tmp=str(tree)+"/{arch}/+aliases"
86
if os.access(tmp, os.R_OK) and os.path.isfile(tmp):
89
tmp=str(tree)+"/{arch}/=aliases"
90
if os.access(tmp, os.R_OK) and os.path.isfile(tmp):
93
for parts in iter_alias(treefile):
97
def compact_alias(spec, tree):
99
for parts in iter_all_alias(tree):
100
if spec.startswith(parts[1]):
101
newalias=parts[0]+spec[len(parts[1]):]
102
aliases.append(newalias)
106
def shortest_alias(spec, tree):
107
"""Convenience function to return the shortest alias for a spec.
109
:param spec: The spec to compact
110
:type spec: Convertible to str
111
:param tree: The working tree to use for local alias lookup
112
:type tree: `arch.WorkingTree`
113
:return: The shortest alias, or None if no aliases were found
114
:rtype: str or NoneType
116
aliases = compact_alias(str(spec), tree)
117
return util.shortest(aliases)
120
def alias_or_version(version, tree, full=True):
121
"""Return the shortest alias or version name for a version.
123
:param version: The spec to compact
124
:type version: `arch.Version`
125
:param tree: The working tree to use for local alias lookup
126
:type tree: `arch.WorkingTree`
127
:param full: Use the full version name (use nonarch if false).
129
:return: The shortest alias, or the version name
132
alias = shortest_alias(version, tree)
133
if alias is not None:
136
return version.fullname
138
return version.nonarch
141
def iter_partners(tree, skip_version=None):
142
"""Generate an iterator of partner versions.
143
If the tree contains {arch}/+partner-versions, it is used. Otherwise,
144
{arch}/=partner-versions is used.
146
:param tree: The tree to find partner versions for
147
:type tree: `arch.ArchSourceTree`
148
:return: An iterator of partner versions for the tree
149
:rtype: iterator of `arch.Version`
151
partnerfile=str(tree)+"/{arch}/+partner-versions"
152
if not os.access(partnerfile, os.R_OK) or not os.path.isfile(partnerfile):
153
partnerfile=str(tree)+"/{arch}/=partner-versions"
154
if not os.access(partnerfile, os.R_OK) or not \
155
os.path.isfile(partnerfile):
158
for line in open(partnerfile):
159
version = arch.Version(line.rstrip("\n"))
160
if version == skip_version:
165
def iter_partner_revisions(tree, skip_version):
166
"""Iterates through the archive-latest revisions of all partner versions.
168
:param tree: The tree to get revisions for
169
:type tree: `arch.ArchSourceTree`
170
:param skip_version: A version to skip (typically the tree version)
171
:type skip_version: `arch.Version`
173
for partner in iter_partners(tree, skip_version):
174
arch_compound.ensure_archive_registered(partner.archive)
176
yield partner.iter_revisions(True).next()
177
except StopIteration, e:
181
def sc_lookup(archive):
182
"""Uses James Blackwell's mirror stats page to look up archive locations.
184
:param archive: The Archive to find a location for
185
:type archive: str or `arch.Archive`
186
:return: home and mirror archive (either or both may be none)
187
:rtype: tuple of str or NoneType
190
mirrorpage = urllib2.urlopen("http://sourcecontrol.net/?m=mirrorstats")
192
raise LookupError(e, archive, "sourcecontrol.net")
193
expression = re.compile("Name : <a href=\"[^\"]*\">%s</a>" % archive)
194
for line in mirrorpage:
195
if expression.search(line) is not None:
196
home = re.search("Home : <a href=\"([^\"]*)\">", line)
199
mirror = re.search("Mirror: <a href=\"([^\"]*)\">", line)
200
if mirror is not None:
201
mirror = mirror.group(1)
206
def bh_lookup(archive):
207
"""Uses Gergely Nagy's archive registry to look up archive locations.
209
:param archive: The Archive to find a location for
210
:type archive: str or `arch.Archive`
211
:return: home and mirror archive (either or both may be none)
212
:rtype: tuple of str or NoneType
214
url = "http://bonehunter.rulez.org/~arch-registry/%s"\
217
page = urllib2.urlopen(url)
219
raise LookupError(e, archive, "http://bonehunter.rulez.org")
221
if line.startswith("Archive not found"):
226
loc = loc.rstrip("\n")
232
def bh_submit(archive, location):
233
"""Submit an archive location to Gergely Nagy's archive registry.
235
:param archive: The Archive to find a location for
236
:type archive: str or `arch.Archive`
237
:param location: The location to use for the archive
240
data = urllib.urlencode({"name": str(archive), "location": location})
241
url = "http://bonehunter.rulez.org/~arch-registry/submit.cgi"
242
result = urllib2.urlopen(url, data).next().rstrip()
243
if result != "Archive %s registered with location %s." % \
244
(str(archive), location):
245
raise errors.SubmitError(str(archive), location, url, result)
247
def iter_micro(tree, my_version=None):
248
if my_version is None:
249
my_version = tree.tree_version
250
for version in iter_partners(tree, tree.tree_version):
251
miss = arch_core.iter_missing(tree, version)
252
for log in arch_compound.iter_log_match(miss, "Microbranch",
257
def submit_version(tree):
258
"""Determine the submit version of this tree
260
:param tree: The tree to determine the version of
261
:type tree: `arch.ArchSourceTree`
262
:return: The submit version, or None
263
:rtype: `arch.Version`
265
submit_file = tree+"/{arch}/+submit-version"
266
if not os.access(submit_file, os.R_OK):
268
line = open(submit_file).next()
269
return arch.Version(line.rstrip("\n"))
272
def submit_revision(tree):
273
"""If this tree has new merges from the submit version, return latest
275
:param tree: The tree to determine the revision of
276
:type tree: `arch.ArchSourceTree`
277
:return: the latest submit revision merged, or None
278
:rtype: `arch.Revision`
280
submit_ver = submit_version(tree)
281
if submit_ver is not None:
282
for log in arch_core.iter_new_merges(tree, tree.tree_version, True):
283
if log.revision.version == submit_ver:
288
def comp_revision(tree):
289
"""Return the base revision to compare with. For most trees, this is just
290
the latest revision of the tree version, but for submit trees with submit
291
version merges, it's different.
292
:param tree: The tree to determine the revision of
293
:type tree: `arch.ArchSourceTree`
294
:return: the latest submit revision merged, or None
295
:rtype: `arch.Revision`
297
submit_rev = submit_revision(tree)
298
if submit_rev is not None:
302
return arch_compound.tree_latest(tree)
303
except errors.NoVersionRevisions, e:
304
raise errors.CantDetermineRevision("", "This tree shows no commits"
309
def log_template_path(tree):
310
"""Return the path of the template to use for commit logs, if any. Tries
311
tree and .arch-params.
313
:param tree: The tree to find the template for
314
:type tree: `arch.WorkingTree`
315
:return: The path or None if no template exists
316
:rtype: str or NoneType
318
template = tree+"/{arch}/=log-template"
319
if os.path.exists(template):
323
template = os.path.expanduser("~/.arch-params/=log-template")
324
if os.path.exists(template):
329
def version_difference(version, compare_version):
330
"""Compares two versions, and returns the part of the name that differs
331
:param version: The version to describe
332
:type version: `arch.Version`
333
:param compare_version: The version to compare to
334
:type compare_version: `arch.Version`
336
if version.archive == compare_version.archive:
337
p_version = arch.NameParser(version)
338
pc_version = arch.NameParser(compare_version)
339
if p_version.get_category() != pc_version.get_category():
340
return version.nonarch
341
elif p_version.get_branch() != pc_version.get_branch() and\
342
p_version.get_branch() != "":
343
return "%s--%s" % (p_version.get_branch(),
344
p_version.get_version())
346
return p_version.get_version()
348
elif version.nonarch == compare_version.nonarch:
349
return str(version.archive)
354
def merge_summary(new_merges, tree_version):
355
"""Produce a nice summary line for a merge commit log.
356
:param new_merges: iterator of newly-merged logs
357
:type new_merges: iter of `arch.Patchlog`
358
:param tree_version: The tree version to produce a summary for
359
:type tree_version: `arch.Version`
360
:return: the summary, or "" if there were no new merges.
363
if len(new_merges) == 0:
365
if len(new_merges) == 1:
366
summary = new_merges[0].summary
371
for merge in new_merges:
372
if arch.my_id() != merge.creator:
373
credit = re.sub("<.*>", "", merge.creator).rstrip(" ");
375
credit = version_difference(merge.revision.version,
377
if not credit in log_credits:
378
log_credits.append(credit)
379
return ("%s (%s)") % (summary, ", ".join(log_credits))
381
# arch-tag: 56d164d9-53ad-40f4-af41-985f505ea8d8