summaryrefslogtreecommitdiffstats
path: root/manifest_xml.py
diff options
context:
space:
mode:
Diffstat (limited to 'manifest_xml.py')
-rw-r--r--manifest_xml.py106
1 files changed, 76 insertions, 30 deletions
diff --git a/manifest_xml.py b/manifest_xml.py
index 3c8fadd6..130e17c2 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -38,8 +38,9 @@ MANIFEST_FILE_NAME = 'manifest.xml'
38LOCAL_MANIFEST_NAME = 'local_manifest.xml' 38LOCAL_MANIFEST_NAME = 'local_manifest.xml'
39LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' 39LOCAL_MANIFESTS_DIR_NAME = 'local_manifests'
40 40
41urllib.parse.uses_relative.extend(['ssh', 'git']) 41# urljoin gets confused if the scheme is not known.
42urllib.parse.uses_netloc.extend(['ssh', 'git']) 42urllib.parse.uses_relative.extend(['ssh', 'git', 'persistent-https', 'rpc'])
43urllib.parse.uses_netloc.extend(['ssh', 'git', 'persistent-https', 'rpc'])
43 44
44class _Default(object): 45class _Default(object):
45 """Project defaults within the manifest.""" 46 """Project defaults within the manifest."""
@@ -63,12 +64,14 @@ class _XmlRemote(object):
63 alias=None, 64 alias=None,
64 fetch=None, 65 fetch=None,
65 manifestUrl=None, 66 manifestUrl=None,
66 review=None): 67 review=None,
68 revision=None):
67 self.name = name 69 self.name = name
68 self.fetchUrl = fetch 70 self.fetchUrl = fetch
69 self.manifestUrl = manifestUrl 71 self.manifestUrl = manifestUrl
70 self.remoteAlias = alias 72 self.remoteAlias = alias
71 self.reviewUrl = review 73 self.reviewUrl = review
74 self.revision = revision
72 self.resolvedFetchUrl = self._resolveFetchUrl() 75 self.resolvedFetchUrl = self._resolveFetchUrl()
73 76
74 def __eq__(self, other): 77 def __eq__(self, other):
@@ -83,17 +86,14 @@ class _XmlRemote(object):
83 # urljoin will gets confused over quite a few things. The ones we care 86 # urljoin will gets confused over quite a few things. The ones we care
84 # about here are: 87 # about here are:
85 # * no scheme in the base url, like <hostname:port> 88 # * no scheme in the base url, like <hostname:port>
86 # * persistent-https:// 89 # We handle no scheme by replacing it with an obscure protocol, gopher
87 # We handle this by replacing these with obscure protocols 90 # and then replacing it with the original when we are done.
88 # and then replacing them with the original when we are done. 91
89 # gopher -> <none>
90 # wais -> persistent-https
91 if manifestUrl.find(':') != manifestUrl.find('/') - 1: 92 if manifestUrl.find(':') != manifestUrl.find('/') - 1:
92 manifestUrl = 'gopher://' + manifestUrl 93 url = urllib.parse.urljoin('gopher://' + manifestUrl, url)
93 manifestUrl = re.sub(r'^persistent-https://', 'wais://', manifestUrl) 94 url = re.sub(r'^gopher://', '', url)
94 url = urllib.parse.urljoin(manifestUrl, url) 95 else:
95 url = re.sub(r'^gopher://', '', url) 96 url = urllib.parse.urljoin(manifestUrl, url)
96 url = re.sub(r'^wais://', 'persistent-https://', url)
97 return url 97 return url
98 98
99 def ToRemoteSpec(self, projectName): 99 def ToRemoteSpec(self, projectName):
@@ -159,6 +159,11 @@ class XmlManifest(object):
159 e.setAttribute('alias', r.remoteAlias) 159 e.setAttribute('alias', r.remoteAlias)
160 if r.reviewUrl is not None: 160 if r.reviewUrl is not None:
161 e.setAttribute('review', r.reviewUrl) 161 e.setAttribute('review', r.reviewUrl)
162 if r.revision is not None:
163 e.setAttribute('revision', r.revision)
164
165 def _ParseGroups(self, groups):
166 return [x for x in re.split(r'[,\s]+', groups) if x]
162 167
163 def Save(self, fd, peg_rev=False, peg_rev_upstream=True): 168 def Save(self, fd, peg_rev=False, peg_rev_upstream=True):
164 """Write the current manifest out to the given file descriptor. 169 """Write the current manifest out to the given file descriptor.
@@ -167,7 +172,7 @@ class XmlManifest(object):
167 172
168 groups = mp.config.GetString('manifest.groups') 173 groups = mp.config.GetString('manifest.groups')
169 if groups: 174 if groups:
170 groups = [x for x in re.split(r'[,\s]+', groups) if x] 175 groups = self._ParseGroups(groups)
171 176
172 doc = xml.dom.minidom.Document() 177 doc = xml.dom.minidom.Document()
173 root = doc.createElement('manifest') 178 root = doc.createElement('manifest')
@@ -240,20 +245,27 @@ class XmlManifest(object):
240 if d.remote: 245 if d.remote:
241 remoteName = d.remote.remoteAlias or d.remote.name 246 remoteName = d.remote.remoteAlias or d.remote.name
242 if not d.remote or p.remote.name != remoteName: 247 if not d.remote or p.remote.name != remoteName:
243 e.setAttribute('remote', p.remote.name) 248 remoteName = p.remote.name
249 e.setAttribute('remote', remoteName)
244 if peg_rev: 250 if peg_rev:
245 if self.IsMirror: 251 if self.IsMirror:
246 value = p.bare_git.rev_parse(p.revisionExpr + '^0') 252 value = p.bare_git.rev_parse(p.revisionExpr + '^0')
247 else: 253 else:
248 value = p.work_git.rev_parse(HEAD + '^0') 254 value = p.work_git.rev_parse(HEAD + '^0')
249 e.setAttribute('revision', value) 255 e.setAttribute('revision', value)
250 if peg_rev_upstream and value != p.revisionExpr: 256 if peg_rev_upstream:
251 # Only save the origin if the origin is not a sha1, and the default 257 if p.upstream:
252 # isn't our value, and the if the default doesn't already have that 258 e.setAttribute('upstream', p.upstream)
253 # covered. 259 elif value != p.revisionExpr:
254 e.setAttribute('upstream', p.revisionExpr) 260 # Only save the origin if the origin is not a sha1, and the default
255 elif not d.revisionExpr or p.revisionExpr != d.revisionExpr: 261 # isn't our value
256 e.setAttribute('revision', p.revisionExpr) 262 e.setAttribute('upstream', p.revisionExpr)
263 else:
264 revision = self.remotes[remoteName].revision or d.revisionExpr
265 if not revision or revision != p.revisionExpr:
266 e.setAttribute('revision', p.revisionExpr)
267 if p.upstream and p.upstream != p.revisionExpr:
268 e.setAttribute('upstream', p.upstream)
257 269
258 for c in p.copyfiles: 270 for c in p.copyfiles:
259 ce = doc.createElement('copyfile') 271 ce = doc.createElement('copyfile')
@@ -261,6 +273,12 @@ class XmlManifest(object):
261 ce.setAttribute('dest', c.dest) 273 ce.setAttribute('dest', c.dest)
262 e.appendChild(ce) 274 e.appendChild(ce)
263 275
276 for l in p.linkfiles:
277 le = doc.createElement('linkfile')
278 le.setAttribute('src', l.src)
279 le.setAttribute('dest', l.dest)
280 e.appendChild(le)
281
264 default_groups = ['all', 'name:%s' % p.name, 'path:%s' % p.relpath] 282 default_groups = ['all', 'name:%s' % p.name, 'path:%s' % p.relpath]
265 egroups = [g for g in p.groups if g not in default_groups] 283 egroups = [g for g in p.groups if g not in default_groups]
266 if egroups: 284 if egroups:
@@ -304,7 +322,7 @@ class XmlManifest(object):
304 @property 322 @property
305 def projects(self): 323 def projects(self):
306 self._Load() 324 self._Load()
307 return self._paths.values() 325 return list(self._paths.values())
308 326
309 @property 327 @property
310 def remotes(self): 328 def remotes(self):
@@ -492,6 +510,23 @@ class XmlManifest(object):
492 if node.nodeName == 'project': 510 if node.nodeName == 'project':
493 project = self._ParseProject(node) 511 project = self._ParseProject(node)
494 recursively_add_projects(project) 512 recursively_add_projects(project)
513 if node.nodeName == 'extend-project':
514 name = self._reqatt(node, 'name')
515
516 if name not in self._projects:
517 raise ManifestParseError('extend-project element specifies non-existent '
518 'project: %s' % name)
519
520 path = node.getAttribute('path')
521 groups = node.getAttribute('groups')
522 if groups:
523 groups = self._ParseGroups(groups)
524
525 for p in self._projects[name]:
526 if path and p.relpath != path:
527 continue
528 if groups:
529 p.groups.extend(groups)
495 if node.nodeName == 'repo-hooks': 530 if node.nodeName == 'repo-hooks':
496 # Get the name of the project and the (space-separated) list of enabled. 531 # Get the name of the project and the (space-separated) list of enabled.
497 repo_hooks_project = self._reqatt(node, 'in-project') 532 repo_hooks_project = self._reqatt(node, 'in-project')
@@ -586,8 +621,11 @@ class XmlManifest(object):
586 review = node.getAttribute('review') 621 review = node.getAttribute('review')
587 if review == '': 622 if review == '':
588 review = None 623 review = None
624 revision = node.getAttribute('revision')
625 if revision == '':
626 revision = None
589 manifestUrl = self.manifestProject.config.GetString('remote.origin.url') 627 manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
590 return _XmlRemote(name, alias, fetch, manifestUrl, review) 628 return _XmlRemote(name, alias, fetch, manifestUrl, review, revision)
591 629
592 def _ParseDefault(self, node): 630 def _ParseDefault(self, node):
593 """ 631 """
@@ -680,7 +718,7 @@ class XmlManifest(object):
680 raise ManifestParseError("no remote for project %s within %s" % 718 raise ManifestParseError("no remote for project %s within %s" %
681 (name, self.manifestFile)) 719 (name, self.manifestFile))
682 720
683 revisionExpr = node.getAttribute('revision') 721 revisionExpr = node.getAttribute('revision') or remote.revision
684 if not revisionExpr: 722 if not revisionExpr:
685 revisionExpr = self._default.revisionExpr 723 revisionExpr = self._default.revisionExpr
686 if not revisionExpr: 724 if not revisionExpr:
@@ -729,7 +767,7 @@ class XmlManifest(object):
729 groups = '' 767 groups = ''
730 if node.hasAttribute('groups'): 768 if node.hasAttribute('groups'):
731 groups = node.getAttribute('groups') 769 groups = node.getAttribute('groups')
732 groups = [x for x in re.split(r'[,\s]+', groups) if x] 770 groups = self._ParseGroups(groups)
733 771
734 if parent is None: 772 if parent is None:
735 relpath, worktree, gitdir, objdir = self.GetProjectPaths(name, path) 773 relpath, worktree, gitdir, objdir = self.GetProjectPaths(name, path)
@@ -765,6 +803,8 @@ class XmlManifest(object):
765 for n in node.childNodes: 803 for n in node.childNodes:
766 if n.nodeName == 'copyfile': 804 if n.nodeName == 'copyfile':
767 self._ParseCopyFile(project, n) 805 self._ParseCopyFile(project, n)
806 if n.nodeName == 'linkfile':
807 self._ParseLinkFile(project, n)
768 if n.nodeName == 'annotation': 808 if n.nodeName == 'annotation':
769 self._ParseAnnotation(project, n) 809 self._ParseAnnotation(project, n)
770 if n.nodeName == 'project': 810 if n.nodeName == 'project':
@@ -814,6 +854,14 @@ class XmlManifest(object):
814 # dest is relative to the top of the tree 854 # dest is relative to the top of the tree
815 project.AddCopyFile(src, dest, os.path.join(self.topdir, dest)) 855 project.AddCopyFile(src, dest, os.path.join(self.topdir, dest))
816 856
857 def _ParseLinkFile(self, project, node):
858 src = self._reqatt(node, 'src')
859 dest = self._reqatt(node, 'dest')
860 if not self.IsMirror:
861 # src is project relative;
862 # dest is relative to the top of the tree
863 project.AddLinkFile(src, dest, os.path.join(self.topdir, dest))
864
817 def _ParseAnnotation(self, project, node): 865 def _ParseAnnotation(self, project, node):
818 name = self._reqatt(node, 'name') 866 name = self._reqatt(node, 'name')
819 value = self._reqatt(node, 'value') 867 value = self._reqatt(node, 'value')
@@ -856,10 +904,8 @@ class XmlManifest(object):
856 fromProjects = self.paths 904 fromProjects = self.paths
857 toProjects = manifest.paths 905 toProjects = manifest.paths
858 906
859 fromKeys = fromProjects.keys() 907 fromKeys = sorted(fromProjects.keys())
860 fromKeys.sort() 908 toKeys = sorted(toProjects.keys())
861 toKeys = toProjects.keys()
862 toKeys.sort()
863 909
864 diff = {'added': [], 'removed': [], 'changed': [], 'unreachable': []} 910 diff = {'added': [], 'removed': [], 'changed': [], 'unreachable': []}
865 911