blob: 476472fb47468285d227f65989709e060d0e887b [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
16import os
Conley Owensdb728cd2011-09-26 16:34:01 -070017import re
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import sys
Conley Owensdb728cd2011-09-26 16:34:01 -070019import urlparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import xml.dom.minidom
21
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022from git_config import GitConfig, IsId
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070023from project import RemoteSpec, Project, MetaProject, R_HEADS, HEAD
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024from error import ManifestParseError
25
26MANIFEST_FILE_NAME = 'manifest.xml'
Shawn O. Pearce5cc66792008-10-23 16:19:27 -070027LOCAL_MANIFEST_NAME = 'local_manifest.xml'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070028
Conley Owensdb728cd2011-09-26 16:34:01 -070029urlparse.uses_relative.extend(['ssh', 'git'])
30urlparse.uses_netloc.extend(['ssh', 'git'])
31
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032class _Default(object):
33 """Project defaults within the manifest."""
34
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -070035 revisionExpr = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070036 remote = None
Shawn O. Pearce6392c872011-09-22 17:44:31 -070037 sync_j = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070038
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070039class _XmlRemote(object):
40 def __init__(self,
41 name,
42 fetch=None,
Conley Owensdb728cd2011-09-26 16:34:01 -070043 manifestUrl=None,
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070044 review=None):
45 self.name = name
46 self.fetchUrl = fetch
Conley Owensdb728cd2011-09-26 16:34:01 -070047 self.manifestUrl = manifestUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070048 self.reviewUrl = review
49
50 def ToRemoteSpec(self, projectName):
Conley Owensdb728cd2011-09-26 16:34:01 -070051 url = self.fetchUrl.rstrip('/') + '/' + projectName + '.git'
52 manifestUrl = self.manifestUrl.rstrip('/')
53 # urljoin will get confused if there is no scheme in the base url
54 # ie, if manifestUrl is of the form
55 if manifestUrl.find(':') != manifestUrl.find('/') - 1:
56 manifestUrl = 'gopher://' + manifestUrl
57 url = urlparse.urljoin(manifestUrl, url)
58 url = re.sub(r'^gopher://', '', url)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -070059 return RemoteSpec(self.name, url, self.reviewUrl)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070060
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -070061class XmlManifest(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070062 """manages the repo configuration file"""
63
64 def __init__(self, repodir):
65 self.repodir = os.path.abspath(repodir)
66 self.topdir = os.path.dirname(self.repodir)
67 self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068 self.globalConfig = GitConfig.ForUser()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070069
70 self.repoProject = MetaProject(self, 'repo',
71 gitdir = os.path.join(repodir, 'repo/.git'),
72 worktree = os.path.join(repodir, 'repo'))
73
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070074 self.manifestProject = MetaProject(self, 'manifests',
Shawn O. Pearcef5c25a62008-11-04 08:11:53 -080075 gitdir = os.path.join(repodir, 'manifests.git'),
76 worktree = os.path.join(repodir, 'manifests'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070077
78 self._Unload()
79
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070080 def Override(self, name):
81 """Use a different manifest, just for the current instantiation.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082 """
83 path = os.path.join(self.manifestProject.worktree, name)
84 if not os.path.isfile(path):
85 raise ManifestParseError('manifest %s not found' % name)
86
87 old = self.manifestFile
88 try:
89 self.manifestFile = path
90 self._Unload()
91 self._Load()
92 finally:
93 self.manifestFile = old
94
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070095 def Link(self, name):
96 """Update the repo metadata to use a different manifest.
97 """
98 self.Override(name)
99
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700100 try:
101 if os.path.exists(self.manifestFile):
102 os.remove(self.manifestFile)
103 os.symlink('manifests/%s' % name, self.manifestFile)
104 except OSError, e:
105 raise ManifestParseError('cannot link manifest %s' % name)
106
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800107 def _RemoteToXml(self, r, doc, root):
108 e = doc.createElement('remote')
109 root.appendChild(e)
110 e.setAttribute('name', r.name)
111 e.setAttribute('fetch', r.fetchUrl)
112 if r.reviewUrl is not None:
113 e.setAttribute('review', r.reviewUrl)
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800114
115 def Save(self, fd, peg_rev=False):
116 """Write the current manifest out to the given file descriptor.
117 """
118 doc = xml.dom.minidom.Document()
119 root = doc.createElement('manifest')
120 doc.appendChild(root)
121
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700122 # Save out the notice. There's a little bit of work here to give it the
123 # right whitespace, which assumes that the notice is automatically indented
124 # by 4 by minidom.
125 if self.notice:
126 notice_element = root.appendChild(doc.createElement('notice'))
127 notice_lines = self.notice.splitlines()
128 indented_notice = ('\n'.join(" "*4 + line for line in notice_lines))[4:]
129 notice_element.appendChild(doc.createTextNode(indented_notice))
130
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800131 d = self.default
132 sort_remotes = list(self.remotes.keys())
133 sort_remotes.sort()
134
135 for r in sort_remotes:
136 self._RemoteToXml(self.remotes[r], doc, root)
137 if self.remotes:
138 root.appendChild(doc.createTextNode(''))
139
140 have_default = False
141 e = doc.createElement('default')
142 if d.remote:
143 have_default = True
144 e.setAttribute('remote', d.remote.name)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700145 if d.revisionExpr:
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800146 have_default = True
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700147 e.setAttribute('revision', d.revisionExpr)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700148 if d.sync_j > 1:
149 have_default = True
150 e.setAttribute('sync-j', '%d' % d.sync_j)
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800151 if have_default:
152 root.appendChild(e)
153 root.appendChild(doc.createTextNode(''))
154
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700155 if self._manifest_server:
156 e = doc.createElement('manifest-server')
157 e.setAttribute('url', self._manifest_server)
158 root.appendChild(e)
159 root.appendChild(doc.createTextNode(''))
160
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800161 sort_projects = list(self.projects.keys())
162 sort_projects.sort()
163
164 for p in sort_projects:
165 p = self.projects[p]
166 e = doc.createElement('project')
167 root.appendChild(e)
168 e.setAttribute('name', p.name)
169 if p.relpath != p.name:
170 e.setAttribute('path', p.relpath)
171 if not d.remote or p.remote.name != d.remote.name:
172 e.setAttribute('remote', p.remote.name)
173 if peg_rev:
174 if self.IsMirror:
175 e.setAttribute('revision',
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700176 p.bare_git.rev_parse(p.revisionExpr + '^0'))
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800177 else:
178 e.setAttribute('revision',
179 p.work_git.rev_parse(HEAD + '^0'))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700180 elif not d.revisionExpr or p.revisionExpr != d.revisionExpr:
181 e.setAttribute('revision', p.revisionExpr)
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800182
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800183 for c in p.copyfiles:
184 ce = doc.createElement('copyfile')
185 ce.setAttribute('src', c.src)
186 ce.setAttribute('dest', c.dest)
187 e.appendChild(ce)
188
Doug Anderson37282b42011-03-04 11:54:18 -0800189 if self._repo_hooks_project:
190 root.appendChild(doc.createTextNode(''))
191 e = doc.createElement('repo-hooks')
192 e.setAttribute('in-project', self._repo_hooks_project.name)
193 e.setAttribute('enabled-list',
194 ' '.join(self._repo_hooks_project.enabled_repo_hooks))
195 root.appendChild(e)
196
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800197 doc.writexml(fd, '', ' ', '\n', 'UTF-8')
198
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700199 @property
200 def projects(self):
201 self._Load()
202 return self._projects
203
204 @property
205 def remotes(self):
206 self._Load()
207 return self._remotes
208
209 @property
210 def default(self):
211 self._Load()
212 return self._default
213
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800214 @property
Doug Anderson37282b42011-03-04 11:54:18 -0800215 def repo_hooks_project(self):
216 self._Load()
217 return self._repo_hooks_project
218
219 @property
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700220 def notice(self):
221 self._Load()
222 return self._notice
223
224 @property
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700225 def manifest_server(self):
226 self._Load()
227 return self._manifest_server
228
229 @property
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800230 def IsMirror(self):
231 return self.manifestProject.config.GetBoolean('repo.mirror')
232
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700233 def _Unload(self):
234 self._loaded = False
235 self._projects = {}
236 self._remotes = {}
237 self._default = None
Doug Anderson37282b42011-03-04 11:54:18 -0800238 self._repo_hooks_project = None
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700239 self._notice = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240 self.branch = None
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700241 self._manifest_server = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242
243 def _Load(self):
244 if not self._loaded:
Shawn O. Pearce2450a292008-11-04 08:22:07 -0800245 m = self.manifestProject
246 b = m.GetBranch(m.CurrentBranch).merge
Shawn O. Pearce21c5c342009-06-25 16:47:30 -0700247 if b is not None and b.startswith(R_HEADS):
Shawn O. Pearce2450a292008-11-04 08:22:07 -0800248 b = b[len(R_HEADS):]
249 self.branch = b
250
Shawn O. Pearce5cc66792008-10-23 16:19:27 -0700251 self._ParseManifest(True)
252
253 local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME)
254 if os.path.exists(local):
255 try:
256 real = self.manifestFile
257 self.manifestFile = local
258 self._ParseManifest(False)
259 finally:
260 self.manifestFile = real
261
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800262 if self.IsMirror:
263 self._AddMetaProjectMirror(self.repoProject)
264 self._AddMetaProjectMirror(self.manifestProject)
265
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266 self._loaded = True
267
Shawn O. Pearce5cc66792008-10-23 16:19:27 -0700268 def _ParseManifest(self, is_root_file):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700269 root = xml.dom.minidom.parse(self.manifestFile)
270 if not root or not root.childNodes:
Doug Anderson37282b42011-03-04 11:54:18 -0800271 raise ManifestParseError(
272 "no root node in %s" %
273 self.manifestFile)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700274
275 config = root.childNodes[0]
276 if config.nodeName != 'manifest':
Doug Anderson37282b42011-03-04 11:54:18 -0800277 raise ManifestParseError(
278 "no in %s" %
279 self.manifestFile)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700280
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700281 for node in config.childNodes:
Shawn O. Pearce03eaf072008-11-20 11:42:22 -0800282 if node.nodeName == 'remove-project':
283 name = self._reqatt(node, 'name')
284 try:
285 del self._projects[name]
286 except KeyError:
Doug Anderson37282b42011-03-04 11:54:18 -0800287 raise ManifestParseError(
288 'project %s not found' %
289 (name))
290
291 # If the manifest removes the hooks project, treat it as if it deleted
292 # the repo-hooks element too.
293 if self._repo_hooks_project and (self._repo_hooks_project.name == name):
294 self._repo_hooks_project = None
Shawn O. Pearce03eaf072008-11-20 11:42:22 -0800295
296 for node in config.childNodes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700297 if node.nodeName == 'remote':
298 remote = self._ParseRemote(node)
299 if self._remotes.get(remote.name):
Doug Anderson37282b42011-03-04 11:54:18 -0800300 raise ManifestParseError(
301 'duplicate remote %s in %s' %
302 (remote.name, self.manifestFile))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700303 self._remotes[remote.name] = remote
304
305 for node in config.childNodes:
306 if node.nodeName == 'default':
307 if self._default is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800308 raise ManifestParseError(
309 'duplicate default in %s' %
310 (self.manifestFile))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700311 self._default = self._ParseDefault(node)
312 if self._default is None:
313 self._default = _Default()
314
315 for node in config.childNodes:
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700316 if node.nodeName == 'notice':
317 if self._notice is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800318 raise ManifestParseError(
319 'duplicate notice in %s' %
320 (self.manifestFile))
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700321 self._notice = self._ParseNotice(node)
322
323 for node in config.childNodes:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700324 if node.nodeName == 'manifest-server':
325 url = self._reqatt(node, 'url')
326 if self._manifest_server is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800327 raise ManifestParseError(
328 'duplicate manifest-server in %s' %
329 (self.manifestFile))
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700330 self._manifest_server = url
331
332 for node in config.childNodes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700333 if node.nodeName == 'project':
334 project = self._ParseProject(node)
335 if self._projects.get(project.name):
Doug Anderson37282b42011-03-04 11:54:18 -0800336 raise ManifestParseError(
337 'duplicate project %s in %s' %
338 (project.name, self.manifestFile))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700339 self._projects[project.name] = project
340
Doug Anderson37282b42011-03-04 11:54:18 -0800341 for node in config.childNodes:
342 if node.nodeName == 'repo-hooks':
343 # Get the name of the project and the (space-separated) list of enabled.
344 repo_hooks_project = self._reqatt(node, 'in-project')
345 enabled_repo_hooks = self._reqatt(node, 'enabled-list').split()
346
347 # Only one project can be the hooks project
348 if self._repo_hooks_project is not None:
349 raise ManifestParseError(
350 'duplicate repo-hooks in %s' %
351 (self.manifestFile))
352
353 # Store a reference to the Project.
354 try:
355 self._repo_hooks_project = self._projects[repo_hooks_project]
356 except KeyError:
357 raise ManifestParseError(
358 'project %s not found for repo-hooks' %
359 (repo_hooks_project))
360
361 # Store the enabled hooks in the Project object.
362 self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks
363
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800364 def _AddMetaProjectMirror(self, m):
365 name = None
366 m_url = m.GetRemote(m.remote.name).url
367 if m_url.endswith('/.git'):
368 raise ManifestParseError, 'refusing to mirror %s' % m_url
369
370 if self._default and self._default.remote:
371 url = self._default.remote.fetchUrl
372 if not url.endswith('/'):
373 url += '/'
374 if m_url.startswith(url):
375 remote = self._default.remote
376 name = m_url[len(url):]
377
378 if name is None:
379 s = m_url.rindex('/') + 1
Conley Owensdb728cd2011-09-26 16:34:01 -0700380 manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
381 remote = _XmlRemote('origin', m_url[:s], manifestUrl)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800382 name = m_url[s:]
383
384 if name.endswith('.git'):
385 name = name[:-4]
386
387 if name not in self._projects:
388 m.PreSync()
389 gitdir = os.path.join(self.topdir, '%s.git' % name)
390 project = Project(manifest = self,
391 name = name,
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700392 remote = remote.ToRemoteSpec(name),
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800393 gitdir = gitdir,
394 worktree = None,
395 relpath = None,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700396 revisionExpr = m.revisionExpr,
397 revisionId = None)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800398 self._projects[project.name] = project
399
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700400 def _ParseRemote(self, node):
401 """
402 reads a element from the manifest file
403 """
404 name = self._reqatt(node, 'name')
405 fetch = self._reqatt(node, 'fetch')
406 review = node.getAttribute('review')
Shawn O. Pearceae6e0942008-11-06 10:25:35 -0800407 if review == '':
408 review = None
Conley Owensdb728cd2011-09-26 16:34:01 -0700409 manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
410 return _XmlRemote(name, fetch, manifestUrl, review)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700411
412 def _ParseDefault(self, node):
413 """
414 reads a element from the manifest file
415 """
416 d = _Default()
417 d.remote = self._get_remote(node)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700418 d.revisionExpr = node.getAttribute('revision')
419 if d.revisionExpr == '':
420 d.revisionExpr = None
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700421 sync_j = node.getAttribute('sync-j')
422 if sync_j == '' or sync_j is None:
423 d.sync_j = 1
424 else:
425 d.sync_j = int(sync_j)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700426 return d
427
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700428 def _ParseNotice(self, node):
429 """
430 reads a element from the manifest file
431
432 The element is distinct from other tags in the XML in that the
433 data is conveyed between the start and end tag (it's not an empty-element
434 tag).
435
436 The white space (carriage returns, indentation) for the notice element is
437 relevant and is parsed in a way that is based on how python docstrings work.
438 In fact, the code is remarkably similar to here:
439 http://www.python.org/dev/peps/pep-0257/
440 """
441 # Get the data out of the node...
442 notice = node.childNodes[0].data
443
444 # Figure out minimum indentation, skipping the first line (the same line
445 # as the tag)...
446 minIndent = sys.maxint
447 lines = notice.splitlines()
448 for line in lines[1:]:
449 lstrippedLine = line.lstrip()
450 if lstrippedLine:
451 indent = len(line) - len(lstrippedLine)
452 minIndent = min(indent, minIndent)
453
454 # Strip leading / trailing blank lines and also indentation.
455 cleanLines = [lines[0].strip()]
456 for line in lines[1:]:
457 cleanLines.append(line[minIndent:].rstrip())
458
459 # Clear completely blank lines from front and back...
460 while cleanLines and not cleanLines[0]:
461 del cleanLines[0]
462 while cleanLines and not cleanLines[-1]:
463 del cleanLines[-1]
464
465 return '\n'.join(cleanLines)
466
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700467 def _ParseProject(self, node):
468 """
469 reads a element from the manifest file
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700470 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700471 name = self._reqatt(node, 'name')
472
473 remote = self._get_remote(node)
474 if remote is None:
475 remote = self._default.remote
476 if remote is None:
477 raise ManifestParseError, \
478 "no remote for project %s within %s" % \
479 (name, self.manifestFile)
480
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700481 revisionExpr = node.getAttribute('revision')
482 if not revisionExpr:
483 revisionExpr = self._default.revisionExpr
484 if not revisionExpr:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700485 raise ManifestParseError, \
486 "no revision for project %s within %s" % \
487 (name, self.manifestFile)
488
489 path = node.getAttribute('path')
490 if not path:
491 path = name
492 if path.startswith('/'):
493 raise ManifestParseError, \
494 "project %s path cannot be absolute in %s" % \
495 (name, self.manifestFile)
496
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800497 if self.IsMirror:
498 relpath = None
499 worktree = None
500 gitdir = os.path.join(self.topdir, '%s.git' % name)
501 else:
Anthony Newnamdf14a702011-01-09 17:31:57 -0800502 worktree = os.path.join(self.topdir, path).replace('\\', '/')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800503 gitdir = os.path.join(self.repodir, 'projects/%s.git' % path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700504
505 project = Project(manifest = self,
506 name = name,
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700507 remote = remote.ToRemoteSpec(name),
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700508 gitdir = gitdir,
509 worktree = worktree,
510 relpath = path,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700511 revisionExpr = revisionExpr,
512 revisionId = None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700513
514 for n in node.childNodes:
Shawn O. Pearce242b5262009-05-19 13:00:29 -0700515 if n.nodeName == 'copyfile':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700516 self._ParseCopyFile(project, n)
517
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700518 return project
519
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700520 def _ParseCopyFile(self, project, node):
521 src = self._reqatt(node, 'src')
522 dest = self._reqatt(node, 'dest')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800523 if not self.IsMirror:
524 # src is project relative;
525 # dest is relative to the top of the tree
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800526 project.AddCopyFile(src, dest, os.path.join(self.topdir, dest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700527
528 def _get_remote(self, node):
529 name = node.getAttribute('remote')
530 if not name:
531 return None
532
533 v = self._remotes.get(name)
534 if not v:
535 raise ManifestParseError, \
536 "remote %s not defined in %s" % \
537 (name, self.manifestFile)
538 return v
539
540 def _reqatt(self, node, attname):
541 """
542 reads a required attribute from the node.
543 """
544 v = node.getAttribute(attname)
545 if not v:
546 raise ManifestParseError, \
547 "no %s in <%s> within %s" % \
548 (attname, node.nodeName, self.manifestFile)
549 return v