diff options
Diffstat (limited to 'manifest_xml.py')
| -rw-r--r-- | manifest_xml.py | 106 |
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' | |||
| 38 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' | 38 | LOCAL_MANIFEST_NAME = 'local_manifest.xml' |
| 39 | LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' | 39 | LOCAL_MANIFESTS_DIR_NAME = 'local_manifests' |
| 40 | 40 | ||
| 41 | urllib.parse.uses_relative.extend(['ssh', 'git']) | 41 | # urljoin gets confused if the scheme is not known. |
| 42 | urllib.parse.uses_netloc.extend(['ssh', 'git']) | 42 | urllib.parse.uses_relative.extend(['ssh', 'git', 'persistent-https', 'rpc']) |
| 43 | urllib.parse.uses_netloc.extend(['ssh', 'git', 'persistent-https', 'rpc']) | ||
| 43 | 44 | ||
| 44 | class _Default(object): | 45 | class _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 | ||
