blob: 9c882af69e361b7744e822eae2170bcde4659d1f [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
Colin Cross23acdd32012-04-21 00:33:54 -070017import itertools
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import os
Conley Owensdb728cd2011-09-26 16:34:01 -070019import re
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import sys
David Pursehouse59bbb582013-05-17 10:49:33 +090021import xml.dom.minidom
22
23from pyversion import is_python3
24if is_python3():
Chirayu Desai217ea7d2013-03-01 19:14:38 +053025 import urllib.parse
David Pursehouse59bbb582013-05-17 10:49:33 +090026else:
Chirayu Desai217ea7d2013-03-01 19:14:38 +053027 import imp
28 import urlparse
29 urllib = imp.new_module('urllib')
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053030 urllib.parse = urlparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031
Simran Basib9a1b732015-08-20 12:19:28 -070032import gitc_utils
David Pursehousee15c65a2012-08-22 10:46:11 +090033from git_config import GitConfig
David Pursehousee00aa6b2012-09-11 14:33:51 +090034from git_refs import R_HEADS, HEAD
35from project import RemoteSpec, Project, MetaProject
Julien Camperguedd654222014-01-09 16:21:37 +010036from error import ManifestParseError, ManifestInvalidRevisionError
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070037
38MANIFEST_FILE_NAME = 'manifest.xml'
Shawn O. Pearce5cc66792008-10-23 16:19:27 -070039LOCAL_MANIFEST_NAME = 'local_manifest.xml'
David Pursehouse2d5a0df2012-11-13 02:50:36 +090040LOCAL_MANIFESTS_DIR_NAME = 'local_manifests'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070041
Anthony Kingcb07ba72015-03-28 23:26:04 +000042# urljoin gets confused if the scheme is not known.
43urllib.parse.uses_relative.extend(['ssh', 'git', 'persistent-https', 'rpc'])
44urllib.parse.uses_netloc.extend(['ssh', 'git', 'persistent-https', 'rpc'])
Conley Owensdb728cd2011-09-26 16:34:01 -070045
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070046class _Default(object):
47 """Project defaults within the manifest."""
48
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -070049 revisionExpr = None
Conley Owensb6a16e62013-09-25 15:06:09 -070050 destBranchExpr = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070051 remote = None
Shawn O. Pearce6392c872011-09-22 17:44:31 -070052 sync_j = 1
Anatol Pomazau79770d22012-04-20 14:41:59 -070053 sync_c = False
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080054 sync_s = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070055
Julien Campergue74879922013-10-09 14:38:46 +020056 def __eq__(self, other):
57 return self.__dict__ == other.__dict__
58
59 def __ne__(self, other):
60 return self.__dict__ != other.__dict__
61
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070062class _XmlRemote(object):
63 def __init__(self,
64 name,
Yestin Sunb292b982012-07-02 07:32:50 -070065 alias=None,
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070066 fetch=None,
Steve Raed6480452016-08-10 15:00:00 -070067 pushUrl=None,
Conley Owensdb728cd2011-09-26 16:34:01 -070068 manifestUrl=None,
Anthony King36ea2fb2014-05-06 11:54:01 +010069 review=None,
Jonathan Nieder93719792015-03-17 11:29:58 -070070 revision=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070071 self.name = name
72 self.fetchUrl = fetch
Steve Raed6480452016-08-10 15:00:00 -070073 self.pushUrl = pushUrl
Conley Owensdb728cd2011-09-26 16:34:01 -070074 self.manifestUrl = manifestUrl
Yestin Sunb292b982012-07-02 07:32:50 -070075 self.remoteAlias = alias
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070076 self.reviewUrl = review
Anthony King36ea2fb2014-05-06 11:54:01 +010077 self.revision = revision
Conley Owensceea3682011-10-20 10:45:47 -070078 self.resolvedFetchUrl = self._resolveFetchUrl()
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070079
David Pursehouse717ece92012-11-13 08:49:16 +090080 def __eq__(self, other):
81 return self.__dict__ == other.__dict__
82
83 def __ne__(self, other):
84 return self.__dict__ != other.__dict__
85
Conley Owensceea3682011-10-20 10:45:47 -070086 def _resolveFetchUrl(self):
87 url = self.fetchUrl.rstrip('/')
Conley Owensdb728cd2011-09-26 16:34:01 -070088 manifestUrl = self.manifestUrl.rstrip('/')
Conley Owens2d0f5082014-01-31 15:03:51 -080089 # urljoin will gets confused over quite a few things. The ones we care
90 # about here are:
91 # * no scheme in the base url, like
Anthony Kingcb07ba72015-03-28 23:26:04 +000092 # We handle no scheme by replacing it with an obscure protocol, gopher
93 # and then replacing it with the original when we are done.
94
Conley Owensdb728cd2011-09-26 16:34:01 -070095 if manifestUrl.find(':') != manifestUrl.find('/') - 1:
Conley Owens4ccad752015-04-29 10:45:37 -070096 url = urllib.parse.urljoin('gopher://' + manifestUrl, url)
97 url = re.sub(r'^gopher://', '', url)
Anthony Kingcb07ba72015-03-28 23:26:04 +000098 else:
99 url = urllib.parse.urljoin(manifestUrl, url)
Shawn Pearcea9f11b32013-01-02 15:40:48 -0800100 return url
Conley Owensceea3682011-10-20 10:45:47 -0700101
102 def ToRemoteSpec(self, projectName):
Conley Owens9d8f9142011-10-20 14:36:35 -0700103 url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName
Yestin Sunb292b982012-07-02 07:32:50 -0700104 remoteName = self.name
Conley Owens1e7ab2a2013-10-08 17:26:57 -0700105 if self.remoteAlias:
David Pursehouse37128b62013-10-15 10:48:40 +0900106 remoteName = self.remoteAlias
Dan Willemsen96c2d652016-04-06 16:03:54 -0700107 return RemoteSpec(remoteName,
108 url=url,
Steve Raed6480452016-08-10 15:00:00 -0700109 pushUrl=self.pushUrl,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700110 review=self.reviewUrl,
111 orig_name=self.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700112
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -0700113class XmlManifest(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700114 """manages the repo configuration file"""
115
116 def __init__(self, repodir):
117 self.repodir = os.path.abspath(repodir)
118 self.topdir = os.path.dirname(self.repodir)
119 self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700120 self.globalConfig = GitConfig.ForUser()
David Pursehouse4eb285c2013-02-14 16:28:44 +0900121 self.localManifestWarning = False
Simran Basib9a1b732015-08-20 12:19:28 -0700122 self.isGitcClient = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700123
124 self.repoProject = MetaProject(self, 'repo',
125 gitdir = os.path.join(repodir, 'repo/.git'),
126 worktree = os.path.join(repodir, 'repo'))
127
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700128 self.manifestProject = MetaProject(self, 'manifests',
Shawn O. Pearcef5c25a62008-11-04 08:11:53 -0800129 gitdir = os.path.join(repodir, 'manifests.git'),
130 worktree = os.path.join(repodir, 'manifests'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700131
132 self._Unload()
133
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700134 def Override(self, name):
135 """Use a different manifest, just for the current instantiation.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136 """
137 path = os.path.join(self.manifestProject.worktree, name)
138 if not os.path.isfile(path):
139 raise ManifestParseError('manifest %s not found' % name)
140
141 old = self.manifestFile
142 try:
143 self.manifestFile = path
144 self._Unload()
145 self._Load()
146 finally:
147 self.manifestFile = old
148
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700149 def Link(self, name):
150 """Update the repo metadata to use a different manifest.
151 """
152 self.Override(name)
153
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700154 try:
Sebastian Frias223bf962012-11-21 19:09:25 +0100155 if os.path.lexists(self.manifestFile):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700156 os.remove(self.manifestFile)
157 os.symlink('manifests/%s' % name, self.manifestFile)
Sebastian Frias223bf962012-11-21 19:09:25 +0100158 except OSError as e:
159 raise ManifestParseError('cannot link manifest %s: %s' % (name, str(e)))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700160
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800161 def _RemoteToXml(self, r, doc, root):
162 e = doc.createElement('remote')
163 root.appendChild(e)
164 e.setAttribute('name', r.name)
165 e.setAttribute('fetch', r.fetchUrl)
Steve Raed6480452016-08-10 15:00:00 -0700166 if r.pushUrl is not None:
167 e.setAttribute('pushurl', r.pushUrl)
Conley Owens1e7ab2a2013-10-08 17:26:57 -0700168 if r.remoteAlias is not None:
169 e.setAttribute('alias', r.remoteAlias)
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800170 if r.reviewUrl is not None:
171 e.setAttribute('review', r.reviewUrl)
Anthony King36ea2fb2014-05-06 11:54:01 +0100172 if r.revision is not None:
173 e.setAttribute('revision', r.revision)
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800174
Josh Triplett884a3872014-06-12 14:57:29 -0700175 def _ParseGroups(self, groups):
176 return [x for x in re.split(r'[,\s]+', groups) if x]
177
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700178 def Save(self, fd, peg_rev=False, peg_rev_upstream=True, groups=None):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800179 """Write the current manifest out to the given file descriptor.
180 """
Colin Cross5acde752012-03-28 20:15:45 -0700181 mp = self.manifestProject
182
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700183 if groups is None:
184 groups = mp.config.GetString('manifest.groups')
Matt Gumbel0c635bb2012-12-21 10:14:53 -0800185 if groups:
Josh Triplett884a3872014-06-12 14:57:29 -0700186 groups = self._ParseGroups(groups)
Colin Cross5acde752012-03-28 20:15:45 -0700187
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800188 doc = xml.dom.minidom.Document()
189 root = doc.createElement('manifest')
190 doc.appendChild(root)
191
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700192 # Save out the notice. There's a little bit of work here to give it the
193 # right whitespace, which assumes that the notice is automatically indented
194 # by 4 by minidom.
195 if self.notice:
196 notice_element = root.appendChild(doc.createElement('notice'))
197 notice_lines = self.notice.splitlines()
198 indented_notice = ('\n'.join(" "*4 + line for line in notice_lines))[4:]
199 notice_element.appendChild(doc.createTextNode(indented_notice))
200
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800201 d = self.default
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800202
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530203 for r in sorted(self.remotes):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800204 self._RemoteToXml(self.remotes[r], doc, root)
205 if self.remotes:
206 root.appendChild(doc.createTextNode(''))
207
208 have_default = False
209 e = doc.createElement('default')
210 if d.remote:
211 have_default = True
212 e.setAttribute('remote', d.remote.name)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700213 if d.revisionExpr:
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800214 have_default = True
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700215 e.setAttribute('revision', d.revisionExpr)
Simon Ruggier7e59de22015-07-24 12:50:06 +0200216 if d.destBranchExpr:
217 have_default = True
218 e.setAttribute('dest-branch', d.destBranchExpr)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700219 if d.sync_j > 1:
220 have_default = True
221 e.setAttribute('sync-j', '%d' % d.sync_j)
Anatol Pomazau79770d22012-04-20 14:41:59 -0700222 if d.sync_c:
223 have_default = True
224 e.setAttribute('sync-c', 'true')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800225 if d.sync_s:
226 have_default = True
227 e.setAttribute('sync-s', 'true')
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800228 if have_default:
229 root.appendChild(e)
230 root.appendChild(doc.createTextNode(''))
231
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700232 if self._manifest_server:
233 e = doc.createElement('manifest-server')
234 e.setAttribute('url', self._manifest_server)
235 root.appendChild(e)
236 root.appendChild(doc.createTextNode(''))
237
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800238 def output_projects(parent, parent_node, projects):
David James8d201162013-10-11 17:03:19 -0700239 for project_name in projects:
240 for project in self._projects[project_name]:
241 output_project(parent, parent_node, project)
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800242
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800243 def output_project(parent, parent_node, p):
Colin Cross5acde752012-03-28 20:15:45 -0700244 if not p.MatchesGroups(groups):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800245 return
246
247 name = p.name
248 relpath = p.relpath
249 if parent:
250 name = self._UnjoinName(parent.name, name)
251 relpath = self._UnjoinRelpath(parent.relpath, relpath)
Colin Cross5acde752012-03-28 20:15:45 -0700252
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800253 e = doc.createElement('project')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800254 parent_node.appendChild(e)
255 e.setAttribute('name', name)
256 if relpath != name:
257 e.setAttribute('path', relpath)
Conley Owensa17d7af2013-10-16 14:38:09 -0700258 remoteName = None
259 if d.remote:
Dan Willemsen96c2d652016-04-06 16:03:54 -0700260 remoteName = d.remote.name
261 if not d.remote or p.remote.orig_name != remoteName:
262 remoteName = p.remote.orig_name
Anthony King36ea2fb2014-05-06 11:54:01 +0100263 e.setAttribute('remote', remoteName)
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800264 if peg_rev:
265 if self.IsMirror:
Brian Harring14a66742012-09-28 20:21:57 -0700266 value = p.bare_git.rev_parse(p.revisionExpr + '^0')
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800267 else:
Brian Harring14a66742012-09-28 20:21:57 -0700268 value = p.work_git.rev_parse(HEAD + '^0')
269 e.setAttribute('revision', value)
Conley Owens551dfec2015-07-10 14:54:54 -0700270 if peg_rev_upstream:
271 if p.upstream:
272 e.setAttribute('upstream', p.upstream)
273 elif value != p.revisionExpr:
274 # Only save the origin if the origin is not a sha1, and the default
275 # isn't our value
276 e.setAttribute('upstream', p.revisionExpr)
Anthony King36ea2fb2014-05-06 11:54:01 +0100277 else:
Dan Willemsen96c2d652016-04-06 16:03:54 -0700278 revision = self.remotes[p.remote.orig_name].revision or d.revisionExpr
Anthony King36ea2fb2014-05-06 11:54:01 +0100279 if not revision or revision != p.revisionExpr:
280 e.setAttribute('revision', p.revisionExpr)
Mani Chandel7a91d512014-07-24 16:27:08 +0530281 if p.upstream and p.upstream != p.revisionExpr:
282 e.setAttribute('upstream', p.upstream)
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800283
Simon Ruggier7e59de22015-07-24 12:50:06 +0200284 if p.dest_branch and p.dest_branch != d.destBranchExpr:
285 e.setAttribute('dest-branch', p.dest_branch)
286
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800287 for c in p.copyfiles:
288 ce = doc.createElement('copyfile')
289 ce.setAttribute('src', c.src)
290 ce.setAttribute('dest', c.dest)
291 e.appendChild(ce)
292
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500293 for l in p.linkfiles:
294 le = doc.createElement('linkfile')
295 le.setAttribute('src', l.src)
296 le.setAttribute('dest', l.dest)
297 e.appendChild(le)
298
Conley Owensbb1b5f52012-08-13 13:11:18 -0700299 default_groups = ['all', 'name:%s' % p.name, 'path:%s' % p.relpath]
Dmitry Fink17f85ea2012-08-06 14:52:29 -0700300 egroups = [g for g in p.groups if g not in default_groups]
Conley Owens971de8e2012-04-16 10:36:08 -0700301 if egroups:
302 e.setAttribute('groups', ','.join(egroups))
Colin Cross5acde752012-03-28 20:15:45 -0700303
James W. Mills24c13082012-04-12 15:04:13 -0500304 for a in p.annotations:
305 if a.keep == "true":
306 ae = doc.createElement('annotation')
307 ae.setAttribute('name', a.name)
308 ae.setAttribute('value', a.value)
309 e.appendChild(ae)
310
Anatol Pomazau79770d22012-04-20 14:41:59 -0700311 if p.sync_c:
312 e.setAttribute('sync-c', 'true')
313
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800314 if p.sync_s:
315 e.setAttribute('sync-s', 'true')
316
Dan Willemsen88409222015-08-17 15:29:10 -0700317 if p.clone_depth:
318 e.setAttribute('clone-depth', str(p.clone_depth))
319
Simran Basib9a1b732015-08-20 12:19:28 -0700320 self._output_manifest_project_extras(p, e)
321
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800322 if p.subprojects:
David James8d201162013-10-11 17:03:19 -0700323 subprojects = set(subp.name for subp in p.subprojects)
324 output_projects(p, e, list(sorted(subprojects)))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800325
David James8d201162013-10-11 17:03:19 -0700326 projects = set(p.name for p in self._paths.values() if not p.parent)
327 output_projects(None, root, list(sorted(projects)))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800328
Doug Anderson37282b42011-03-04 11:54:18 -0800329 if self._repo_hooks_project:
330 root.appendChild(doc.createTextNode(''))
331 e = doc.createElement('repo-hooks')
332 e.setAttribute('in-project', self._repo_hooks_project.name)
333 e.setAttribute('enabled-list',
334 ' '.join(self._repo_hooks_project.enabled_repo_hooks))
335 root.appendChild(e)
336
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800337 doc.writexml(fd, '', ' ', '\n', 'UTF-8')
338
Simran Basib9a1b732015-08-20 12:19:28 -0700339 def _output_manifest_project_extras(self, p, e):
340 """Manifests can modify e if they support extra project attributes."""
341 pass
342
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700343 @property
David James8d201162013-10-11 17:03:19 -0700344 def paths(self):
345 self._Load()
346 return self._paths
347
348 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700349 def projects(self):
350 self._Load()
Anthony Kingd58bfe52014-05-05 23:30:49 +0100351 return list(self._paths.values())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700352
353 @property
354 def remotes(self):
355 self._Load()
356 return self._remotes
357
358 @property
359 def default(self):
360 self._Load()
361 return self._default
362
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800363 @property
Doug Anderson37282b42011-03-04 11:54:18 -0800364 def repo_hooks_project(self):
365 self._Load()
366 return self._repo_hooks_project
367
368 @property
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700369 def notice(self):
370 self._Load()
371 return self._notice
372
373 @property
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700374 def manifest_server(self):
375 self._Load()
Shawn O. Pearce34fb20f2011-11-30 13:41:02 -0800376 return self._manifest_server
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700377
378 @property
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800379 def IsMirror(self):
380 return self.manifestProject.config.GetBoolean('repo.mirror')
381
Julien Campergue335f5ef2013-10-16 11:02:35 +0200382 @property
383 def IsArchive(self):
384 return self.manifestProject.config.GetBoolean('repo.archive')
385
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700386 def _Unload(self):
387 self._loaded = False
388 self._projects = {}
David James8d201162013-10-11 17:03:19 -0700389 self._paths = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700390 self._remotes = {}
391 self._default = None
Doug Anderson37282b42011-03-04 11:54:18 -0800392 self._repo_hooks_project = None
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700393 self._notice = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700394 self.branch = None
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700395 self._manifest_server = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700396
397 def _Load(self):
398 if not self._loaded:
Shawn O. Pearce2450a292008-11-04 08:22:07 -0800399 m = self.manifestProject
400 b = m.GetBranch(m.CurrentBranch).merge
Shawn O. Pearce21c5c342009-06-25 16:47:30 -0700401 if b is not None and b.startswith(R_HEADS):
Shawn O. Pearce2450a292008-11-04 08:22:07 -0800402 b = b[len(R_HEADS):]
403 self.branch = b
404
Colin Cross23acdd32012-04-21 00:33:54 -0700405 nodes = []
Brian Harring475a47d2012-06-07 20:05:35 -0700406 nodes.append(self._ParseManifestXml(self.manifestFile,
407 self.manifestProject.worktree))
Shawn O. Pearce5cc66792008-10-23 16:19:27 -0700408
409 local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME)
410 if os.path.exists(local):
David Pursehouse4eb285c2013-02-14 16:28:44 +0900411 if not self.localManifestWarning:
412 self.localManifestWarning = True
413 print('warning: %s is deprecated; put local manifests in `%s` instead'
414 % (LOCAL_MANIFEST_NAME, os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)),
415 file=sys.stderr)
Brian Harring475a47d2012-06-07 20:05:35 -0700416 nodes.append(self._ParseManifestXml(local, self.repodir))
Colin Cross23acdd32012-04-21 00:33:54 -0700417
David Pursehouse2d5a0df2012-11-13 02:50:36 +0900418 local_dir = os.path.abspath(os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME))
419 try:
David Pursehouse52f1e5d2012-11-14 04:53:24 +0900420 for local_file in sorted(os.listdir(local_dir)):
David Pursehouse2d5a0df2012-11-13 02:50:36 +0900421 if local_file.endswith('.xml'):
David Pursehouse5f434ed2012-11-22 13:48:10 +0900422 local = os.path.join(local_dir, local_file)
423 nodes.append(self._ParseManifestXml(local, self.repodir))
David Pursehouse2d5a0df2012-11-13 02:50:36 +0900424 except OSError:
425 pass
426
Joe Onorato26e24752013-01-11 12:35:53 -0800427 try:
428 self._ParseManifest(nodes)
429 except ManifestParseError as e:
430 # There was a problem parsing, unload ourselves in case they catch
431 # this error and try again later, we will show the correct error
432 self._Unload()
433 raise e
Shawn O. Pearce5cc66792008-10-23 16:19:27 -0700434
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800435 if self.IsMirror:
436 self._AddMetaProjectMirror(self.repoProject)
437 self._AddMetaProjectMirror(self.manifestProject)
438
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700439 self._loaded = True
440
Brian Harring475a47d2012-06-07 20:05:35 -0700441 def _ParseManifestXml(self, path, include_root):
David Pursehousef7fc8a92012-11-13 04:00:28 +0900442 try:
443 root = xml.dom.minidom.parse(path)
David Pursehouse2d5a0df2012-11-13 02:50:36 +0900444 except (OSError, xml.parsers.expat.ExpatError) as e:
David Pursehousef7fc8a92012-11-13 04:00:28 +0900445 raise ManifestParseError("error parsing manifest %s: %s" % (path, e))
446
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700447 if not root or not root.childNodes:
Brian Harring26448742011-04-28 05:04:41 -0700448 raise ManifestParseError("no root node in %s" % (path,))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700449
Jooncheol Park34acdd22012-08-27 02:25:59 +0900450 for manifest in root.childNodes:
451 if manifest.nodeName == 'manifest':
452 break
453 else:
Brian Harring26448742011-04-28 05:04:41 -0700454 raise ManifestParseError("no in %s" % (path,))
455
Colin Cross23acdd32012-04-21 00:33:54 -0700456 nodes = []
David Pursehouse4f7bdea2012-10-22 12:50:15 +0900457 for node in manifest.childNodes: # pylint:disable=W0631
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900458 # We only get here if manifest is initialised
David Pursehousec1b86a22012-11-14 11:36:51 +0900459 if node.nodeName == 'include':
460 name = self._reqatt(node, 'name')
461 fp = os.path.join(include_root, name)
462 if not os.path.isfile(fp):
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530463 raise ManifestParseError("include %s doesn't exist or isn't a file"
464 % (name,))
David Pursehousec1b86a22012-11-14 11:36:51 +0900465 try:
466 nodes.extend(self._ParseManifestXml(fp, include_root))
467 # should isolate this to the exact exception, but that's
468 # tricky. actual parsing implementation may vary.
469 except (KeyboardInterrupt, RuntimeError, SystemExit):
470 raise
471 except Exception as e:
472 raise ManifestParseError(
473 "failed parsing included manifest %s: %s", (name, e))
474 else:
475 nodes.append(node)
Colin Cross23acdd32012-04-21 00:33:54 -0700476 return nodes
Brian Harring26448742011-04-28 05:04:41 -0700477
Colin Cross23acdd32012-04-21 00:33:54 -0700478 def _ParseManifest(self, node_list):
479 for node in itertools.chain(*node_list):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700480 if node.nodeName == 'remote':
481 remote = self._ParseRemote(node)
David Pursehouse717ece92012-11-13 08:49:16 +0900482 if remote:
483 if remote.name in self._remotes:
484 if remote != self._remotes[remote.name]:
485 raise ManifestParseError(
486 'remote %s already exists with different attributes' %
487 (remote.name))
488 else:
489 self._remotes[remote.name] = remote
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700490
Colin Cross23acdd32012-04-21 00:33:54 -0700491 for node in itertools.chain(*node_list):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700492 if node.nodeName == 'default':
Julien Campergue74879922013-10-09 14:38:46 +0200493 new_default = self._ParseDefault(node)
494 if self._default is None:
495 self._default = new_default
496 elif new_default != self._default:
David Pursehouse37128b62013-10-15 10:48:40 +0900497 raise ManifestParseError('duplicate default in %s' %
498 (self.manifestFile))
Julien Campergue74879922013-10-09 14:38:46 +0200499
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700500 if self._default is None:
501 self._default = _Default()
502
Colin Cross23acdd32012-04-21 00:33:54 -0700503 for node in itertools.chain(*node_list):
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700504 if node.nodeName == 'notice':
505 if self._notice is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800506 raise ManifestParseError(
507 'duplicate notice in %s' %
508 (self.manifestFile))
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700509 self._notice = self._ParseNotice(node)
510
Colin Cross23acdd32012-04-21 00:33:54 -0700511 for node in itertools.chain(*node_list):
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700512 if node.nodeName == 'manifest-server':
513 url = self._reqatt(node, 'url')
514 if self._manifest_server is not None:
David Pursehousec1b86a22012-11-14 11:36:51 +0900515 raise ManifestParseError(
516 'duplicate manifest-server in %s' %
517 (self.manifestFile))
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700518 self._manifest_server = url
519
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800520 def recursively_add_projects(project):
David James8d201162013-10-11 17:03:19 -0700521 projects = self._projects.setdefault(project.name, [])
522 if project.relpath is None:
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800523 raise ManifestParseError(
David James8d201162013-10-11 17:03:19 -0700524 'missing path for %s in %s' %
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800525 (project.name, self.manifestFile))
David James8d201162013-10-11 17:03:19 -0700526 if project.relpath in self._paths:
527 raise ManifestParseError(
528 'duplicate path %s in %s' %
529 (project.relpath, self.manifestFile))
530 self._paths[project.relpath] = project
531 projects.append(project)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800532 for subproject in project.subprojects:
533 recursively_add_projects(subproject)
534
Colin Cross23acdd32012-04-21 00:33:54 -0700535 for node in itertools.chain(*node_list):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700536 if node.nodeName == 'project':
537 project = self._ParseProject(node)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800538 recursively_add_projects(project)
Josh Triplett884a3872014-06-12 14:57:29 -0700539 if node.nodeName == 'extend-project':
540 name = self._reqatt(node, 'name')
541
542 if name not in self._projects:
543 raise ManifestParseError('extend-project element specifies non-existent '
544 'project: %s' % name)
545
546 path = node.getAttribute('path')
547 groups = node.getAttribute('groups')
548 if groups:
549 groups = self._ParseGroups(groups)
550
551 for p in self._projects[name]:
552 if path and p.relpath != path:
553 continue
554 if groups:
555 p.groups.extend(groups)
Doug Anderson37282b42011-03-04 11:54:18 -0800556 if node.nodeName == 'repo-hooks':
557 # Get the name of the project and the (space-separated) list of enabled.
558 repo_hooks_project = self._reqatt(node, 'in-project')
559 enabled_repo_hooks = self._reqatt(node, 'enabled-list').split()
560
561 # Only one project can be the hooks project
562 if self._repo_hooks_project is not None:
563 raise ManifestParseError(
564 'duplicate repo-hooks in %s' %
565 (self.manifestFile))
566
567 # Store a reference to the Project.
568 try:
David James8d201162013-10-11 17:03:19 -0700569 repo_hooks_projects = self._projects[repo_hooks_project]
Doug Anderson37282b42011-03-04 11:54:18 -0800570 except KeyError:
571 raise ManifestParseError(
572 'project %s not found for repo-hooks' %
573 (repo_hooks_project))
574
David James8d201162013-10-11 17:03:19 -0700575 if len(repo_hooks_projects) != 1:
576 raise ManifestParseError(
577 'internal error parsing repo-hooks in %s' %
578 (self.manifestFile))
579 self._repo_hooks_project = repo_hooks_projects[0]
580
Doug Anderson37282b42011-03-04 11:54:18 -0800581 # Store the enabled hooks in the Project object.
582 self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks
Colin Cross23acdd32012-04-21 00:33:54 -0700583 if node.nodeName == 'remove-project':
584 name = self._reqatt(node, 'name')
David Jamesb8433df2014-01-30 10:11:17 -0800585
586 if name not in self._projects:
David Pursehousef9107482012-11-16 19:12:32 +0900587 raise ManifestParseError('remove-project element specifies non-existent '
588 'project: %s' % name)
Colin Cross23acdd32012-04-21 00:33:54 -0700589
David Jamesb8433df2014-01-30 10:11:17 -0800590 for p in self._projects[name]:
591 del self._paths[p.relpath]
592 del self._projects[name]
593
Colin Cross23acdd32012-04-21 00:33:54 -0700594 # If the manifest removes the hooks project, treat it as if it deleted
595 # the repo-hooks element too.
596 if self._repo_hooks_project and (self._repo_hooks_project.name == name):
597 self._repo_hooks_project = None
598
Doug Anderson37282b42011-03-04 11:54:18 -0800599
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800600 def _AddMetaProjectMirror(self, m):
601 name = None
602 m_url = m.GetRemote(m.remote.name).url
603 if m_url.endswith('/.git'):
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530604 raise ManifestParseError('refusing to mirror %s' % m_url)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800605
606 if self._default and self._default.remote:
Conley Owensceea3682011-10-20 10:45:47 -0700607 url = self._default.remote.resolvedFetchUrl
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800608 if not url.endswith('/'):
609 url += '/'
610 if m_url.startswith(url):
611 remote = self._default.remote
612 name = m_url[len(url):]
613
614 if name is None:
615 s = m_url.rindex('/') + 1
Conley Owensdb728cd2011-09-26 16:34:01 -0700616 manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
Shawn O. Pearcef35b2d92012-08-02 11:46:22 -0700617 remote = _XmlRemote('origin', fetch=m_url[:s], manifestUrl=manifestUrl)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800618 name = m_url[s:]
619
620 if name.endswith('.git'):
621 name = name[:-4]
622
623 if name not in self._projects:
624 m.PreSync()
625 gitdir = os.path.join(self.topdir, '%s.git' % name)
626 project = Project(manifest = self,
627 name = name,
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700628 remote = remote.ToRemoteSpec(name),
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800629 gitdir = gitdir,
David James8d201162013-10-11 17:03:19 -0700630 objdir = gitdir,
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800631 worktree = None,
Kwanhong Leeccd218c2014-02-17 13:07:32 +0900632 relpath = name or None,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700633 revisionExpr = m.revisionExpr,
634 revisionId = None)
David James8d201162013-10-11 17:03:19 -0700635 self._projects[project.name] = [project]
Kwanhong Leeccd218c2014-02-17 13:07:32 +0900636 self._paths[project.relpath] = project
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800637
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700638 def _ParseRemote(self, node):
639 """
640 reads a element from the manifest file
641 """
642 name = self._reqatt(node, 'name')
Yestin Sunb292b982012-07-02 07:32:50 -0700643 alias = node.getAttribute('alias')
644 if alias == '':
645 alias = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700646 fetch = self._reqatt(node, 'fetch')
Steve Raed6480452016-08-10 15:00:00 -0700647 pushUrl = node.getAttribute('pushurl')
648 if pushUrl == '':
649 pushUrl = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700650 review = node.getAttribute('review')
Shawn O. Pearceae6e0942008-11-06 10:25:35 -0800651 if review == '':
652 review = None
Anthony King36ea2fb2014-05-06 11:54:01 +0100653 revision = node.getAttribute('revision')
654 if revision == '':
655 revision = None
Conley Owensdb728cd2011-09-26 16:34:01 -0700656 manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
Steve Raed6480452016-08-10 15:00:00 -0700657 return _XmlRemote(name, alias, fetch, pushUrl, manifestUrl, review, revision)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700658
659 def _ParseDefault(self, node):
660 """
661 reads a element from the manifest file
662 """
663 d = _Default()
664 d.remote = self._get_remote(node)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700665 d.revisionExpr = node.getAttribute('revision')
666 if d.revisionExpr == '':
667 d.revisionExpr = None
Anatol Pomazau79770d22012-04-20 14:41:59 -0700668
Bryan Jacobsf609f912013-05-06 13:36:24 -0400669 d.destBranchExpr = node.getAttribute('dest-branch') or None
670
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700671 sync_j = node.getAttribute('sync-j')
672 if sync_j == '' or sync_j is None:
673 d.sync_j = 1
674 else:
675 d.sync_j = int(sync_j)
Anatol Pomazau79770d22012-04-20 14:41:59 -0700676
677 sync_c = node.getAttribute('sync-c')
678 if not sync_c:
679 d.sync_c = False
680 else:
681 d.sync_c = sync_c.lower() in ("yes", "true", "1")
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800682
683 sync_s = node.getAttribute('sync-s')
684 if not sync_s:
685 d.sync_s = False
686 else:
687 d.sync_s = sync_s.lower() in ("yes", "true", "1")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700688 return d
689
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700690 def _ParseNotice(self, node):
691 """
692 reads a element from the manifest file
693
694 The element is distinct from other tags in the XML in that the
695 data is conveyed between the start and end tag (it's not an empty-element
696 tag).
697
698 The white space (carriage returns, indentation) for the notice element is
699 relevant and is parsed in a way that is based on how python docstrings work.
700 In fact, the code is remarkably similar to here:
701 http://www.python.org/dev/peps/pep-0257/
702 """
703 # Get the data out of the node...
704 notice = node.childNodes[0].data
705
706 # Figure out minimum indentation, skipping the first line (the same line
707 # as the tag)...
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530708 minIndent = sys.maxsize
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700709 lines = notice.splitlines()
710 for line in lines[1:]:
711 lstrippedLine = line.lstrip()
712 if lstrippedLine:
713 indent = len(line) - len(lstrippedLine)
714 minIndent = min(indent, minIndent)
715
716 # Strip leading / trailing blank lines and also indentation.
717 cleanLines = [lines[0].strip()]
718 for line in lines[1:]:
719 cleanLines.append(line[minIndent:].rstrip())
720
721 # Clear completely blank lines from front and back...
722 while cleanLines and not cleanLines[0]:
723 del cleanLines[0]
724 while cleanLines and not cleanLines[-1]:
725 del cleanLines[-1]
726
727 return '\n'.join(cleanLines)
728
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800729 def _JoinName(self, parent_name, name):
730 return os.path.join(parent_name, name)
731
732 def _UnjoinName(self, parent_name, name):
733 return os.path.relpath(name, parent_name)
734
Simran Basib9a1b732015-08-20 12:19:28 -0700735 def _ParseProject(self, node, parent = None, **extra_proj_attrs):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700736 """
737 reads a element from the manifest file
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700738 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700739 name = self._reqatt(node, 'name')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800740 if parent:
741 name = self._JoinName(parent.name, name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742
743 remote = self._get_remote(node)
744 if remote is None:
745 remote = self._default.remote
746 if remote is None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530747 raise ManifestParseError("no remote for project %s within %s" %
748 (name, self.manifestFile))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700749
Anthony King36ea2fb2014-05-06 11:54:01 +0100750 revisionExpr = node.getAttribute('revision') or remote.revision
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700751 if not revisionExpr:
752 revisionExpr = self._default.revisionExpr
753 if not revisionExpr:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530754 raise ManifestParseError("no revision for project %s within %s" %
755 (name, self.manifestFile))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700756
757 path = node.getAttribute('path')
758 if not path:
759 path = name
760 if path.startswith('/'):
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530761 raise ManifestParseError("project %s path cannot be absolute in %s" %
762 (name, self.manifestFile))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700763
Mike Pontillod3153822012-02-28 11:53:24 -0800764 rebase = node.getAttribute('rebase')
765 if not rebase:
766 rebase = True
767 else:
768 rebase = rebase.lower() in ("yes", "true", "1")
769
Anatol Pomazau79770d22012-04-20 14:41:59 -0700770 sync_c = node.getAttribute('sync-c')
771 if not sync_c:
772 sync_c = False
773 else:
774 sync_c = sync_c.lower() in ("yes", "true", "1")
775
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800776 sync_s = node.getAttribute('sync-s')
777 if not sync_s:
778 sync_s = self._default.sync_s
779 else:
780 sync_s = sync_s.lower() in ("yes", "true", "1")
781
David Pursehouseede7f122012-11-27 22:25:30 +0900782 clone_depth = node.getAttribute('clone-depth')
783 if clone_depth:
784 try:
785 clone_depth = int(clone_depth)
786 if clone_depth <= 0:
787 raise ValueError()
788 except ValueError:
789 raise ManifestParseError('invalid clone-depth %s in %s' %
790 (clone_depth, self.manifestFile))
791
Bryan Jacobsf609f912013-05-06 13:36:24 -0400792 dest_branch = node.getAttribute('dest-branch') or self._default.destBranchExpr
793
Brian Harring14a66742012-09-28 20:21:57 -0700794 upstream = node.getAttribute('upstream')
795
Conley Owens971de8e2012-04-16 10:36:08 -0700796 groups = ''
797 if node.hasAttribute('groups'):
798 groups = node.getAttribute('groups')
Josh Triplett884a3872014-06-12 14:57:29 -0700799 groups = self._ParseGroups(groups)
Brian Harring7da13142012-06-15 02:24:20 -0700800
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800801 if parent is None:
David James8d201162013-10-11 17:03:19 -0700802 relpath, worktree, gitdir, objdir = self.GetProjectPaths(name, path)
Shawn O. Pearcecd81dd62012-10-26 12:18:00 -0700803 else:
David James8d201162013-10-11 17:03:19 -0700804 relpath, worktree, gitdir, objdir = \
805 self.GetSubprojectPaths(parent, name, path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800806
807 default_groups = ['all', 'name:%s' % name, 'path:%s' % relpath]
808 groups.extend(set(default_groups).difference(groups))
Shawn O. Pearcecd81dd62012-10-26 12:18:00 -0700809
Scott Fandb83b1b2013-02-28 09:34:14 +0800810 if self.IsMirror and node.hasAttribute('force-path'):
811 if node.getAttribute('force-path').lower() in ("yes", "true", "1"):
812 gitdir = os.path.join(self.topdir, '%s.git' % path)
813
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700814 project = Project(manifest = self,
815 name = name,
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700816 remote = remote.ToRemoteSpec(name),
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700817 gitdir = gitdir,
David James8d201162013-10-11 17:03:19 -0700818 objdir = objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700819 worktree = worktree,
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800820 relpath = relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700821 revisionExpr = revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800822 revisionId = None,
Colin Cross5acde752012-03-28 20:15:45 -0700823 rebase = rebase,
Anatol Pomazau79770d22012-04-20 14:41:59 -0700824 groups = groups,
Brian Harring14a66742012-09-28 20:21:57 -0700825 sync_c = sync_c,
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800826 sync_s = sync_s,
David Pursehouseede7f122012-11-27 22:25:30 +0900827 clone_depth = clone_depth,
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800828 upstream = upstream,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400829 parent = parent,
Simran Basib9a1b732015-08-20 12:19:28 -0700830 dest_branch = dest_branch,
831 **extra_proj_attrs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700832
833 for n in node.childNodes:
Shawn O. Pearce242b5262009-05-19 13:00:29 -0700834 if n.nodeName == 'copyfile':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700835 self._ParseCopyFile(project, n)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500836 if n.nodeName == 'linkfile':
837 self._ParseLinkFile(project, n)
James W. Mills24c13082012-04-12 15:04:13 -0500838 if n.nodeName == 'annotation':
839 self._ParseAnnotation(project, n)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800840 if n.nodeName == 'project':
841 project.subprojects.append(self._ParseProject(n, parent = project))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700842
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700843 return project
844
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800845 def GetProjectPaths(self, name, path):
846 relpath = path
847 if self.IsMirror:
848 worktree = None
849 gitdir = os.path.join(self.topdir, '%s.git' % name)
David James8d201162013-10-11 17:03:19 -0700850 objdir = gitdir
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800851 else:
852 worktree = os.path.join(self.topdir, path).replace('\\', '/')
853 gitdir = os.path.join(self.repodir, 'projects', '%s.git' % path)
David James8d201162013-10-11 17:03:19 -0700854 objdir = os.path.join(self.repodir, 'project-objects', '%s.git' % name)
855 return relpath, worktree, gitdir, objdir
856
857 def GetProjectsWithName(self, name):
858 return self._projects.get(name, [])
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800859
860 def GetSubprojectName(self, parent, submodule_path):
861 return os.path.join(parent.name, submodule_path)
862
863 def _JoinRelpath(self, parent_relpath, relpath):
864 return os.path.join(parent_relpath, relpath)
865
866 def _UnjoinRelpath(self, parent_relpath, relpath):
867 return os.path.relpath(relpath, parent_relpath)
868
David James8d201162013-10-11 17:03:19 -0700869 def GetSubprojectPaths(self, parent, name, path):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800870 relpath = self._JoinRelpath(parent.relpath, path)
871 gitdir = os.path.join(parent.gitdir, 'subprojects', '%s.git' % path)
David James8d201162013-10-11 17:03:19 -0700872 objdir = os.path.join(parent.gitdir, 'subproject-objects', '%s.git' % name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800873 if self.IsMirror:
874 worktree = None
875 else:
876 worktree = os.path.join(parent.worktree, path).replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700877 return relpath, worktree, gitdir, objdir
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800878
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879 def _ParseCopyFile(self, project, node):
880 src = self._reqatt(node, 'src')
881 dest = self._reqatt(node, 'dest')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800882 if not self.IsMirror:
883 # src is project relative;
884 # dest is relative to the top of the tree
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800885 project.AddCopyFile(src, dest, os.path.join(self.topdir, dest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700886
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500887 def _ParseLinkFile(self, project, node):
888 src = self._reqatt(node, 'src')
889 dest = self._reqatt(node, 'dest')
890 if not self.IsMirror:
891 # src is project relative;
892 # dest is relative to the top of the tree
893 project.AddLinkFile(src, dest, os.path.join(self.topdir, dest))
894
James W. Mills24c13082012-04-12 15:04:13 -0500895 def _ParseAnnotation(self, project, node):
896 name = self._reqatt(node, 'name')
897 value = self._reqatt(node, 'value')
898 try:
899 keep = self._reqatt(node, 'keep').lower()
900 except ManifestParseError:
901 keep = "true"
902 if keep != "true" and keep != "false":
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530903 raise ManifestParseError('optional "keep" attribute must be '
904 '"true" or "false"')
James W. Mills24c13082012-04-12 15:04:13 -0500905 project.AddAnnotation(name, value, keep)
906
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700907 def _get_remote(self, node):
908 name = node.getAttribute('remote')
909 if not name:
910 return None
911
912 v = self._remotes.get(name)
913 if not v:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530914 raise ManifestParseError("remote %s not defined in %s" %
915 (name, self.manifestFile))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700916 return v
917
918 def _reqatt(self, node, attname):
919 """
920 reads a required attribute from the node.
921 """
922 v = node.getAttribute(attname)
923 if not v:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530924 raise ManifestParseError("no %s in <%s> within %s" %
925 (attname, node.nodeName, self.manifestFile))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700926 return v
Julien Camperguedd654222014-01-09 16:21:37 +0100927
928 def projectsDiff(self, manifest):
929 """return the projects differences between two manifests.
930
931 The diff will be from self to given manifest.
932
933 """
934 fromProjects = self.paths
935 toProjects = manifest.paths
936
Anthony King7446c592014-05-06 09:19:39 +0100937 fromKeys = sorted(fromProjects.keys())
938 toKeys = sorted(toProjects.keys())
Julien Camperguedd654222014-01-09 16:21:37 +0100939
940 diff = {'added': [], 'removed': [], 'changed': [], 'unreachable': []}
941
942 for proj in fromKeys:
943 if not proj in toKeys:
944 diff['removed'].append(fromProjects[proj])
945 else:
946 fromProj = fromProjects[proj]
947 toProj = toProjects[proj]
948 try:
949 fromRevId = fromProj.GetCommitRevisionId()
950 toRevId = toProj.GetCommitRevisionId()
951 except ManifestInvalidRevisionError:
952 diff['unreachable'].append((fromProj, toProj))
953 else:
954 if fromRevId != toRevId:
955 diff['changed'].append((fromProj, toProj))
956 toKeys.remove(proj)
957
958 for proj in toKeys:
959 diff['added'].append(toProjects[proj])
960
961 return diff
Simran Basib9a1b732015-08-20 12:19:28 -0700962
963
964class GitcManifest(XmlManifest):
965
966 def __init__(self, repodir, gitc_client_name):
967 """Initialize the GitcManifest object."""
968 super(GitcManifest, self).__init__(repodir)
969 self.isGitcClient = True
970 self.gitc_client_name = gitc_client_name
Simran Basi8ce50412015-08-28 14:25:44 -0700971 self.gitc_client_dir = os.path.join(gitc_utils.get_gitc_manifest_dir(),
Simran Basib9a1b732015-08-20 12:19:28 -0700972 gitc_client_name)
973 self.manifestFile = os.path.join(self.gitc_client_dir, '.manifest')
974
975 def _ParseProject(self, node, parent = None):
976 """Override _ParseProject and add support for GITC specific attributes."""
977 return super(GitcManifest, self)._ParseProject(
978 node, parent=parent, old_revision=node.getAttribute('old-revision'))
979
980 def _output_manifest_project_extras(self, p, e):
981 """Output GITC Specific Project attributes"""
982 if p.old_revision:
Stefan Beller66851062016-06-17 16:40:08 -0700983 e.setAttribute('old-revision', str(p.old_revision))
Simran Basib9a1b732015-08-20 12:19:28 -0700984