summaryrefslogtreecommitdiffstats
path: root/manifest_xml.py
diff options
context:
space:
mode:
Diffstat (limited to 'manifest_xml.py')
-rw-r--r--manifest_xml.py331
1 files changed, 243 insertions, 88 deletions
diff --git a/manifest_xml.py b/manifest_xml.py
index 1d02f9d4..26cc14f6 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -13,53 +13,75 @@
13# See the License for the specific language governing permissions and 13# See the License for the specific language governing permissions and
14# limitations under the License. 14# limitations under the License.
15 15
16import itertools
16import os 17import os
18import re
17import sys 19import sys
20import urlparse
18import xml.dom.minidom 21import xml.dom.minidom
19 22
20from git_config import GitConfig 23from git_config import GitConfig, IsId
21from git_config import IsId 24from project import RemoteSpec, Project, MetaProject, R_HEADS, HEAD
22from manifest import Manifest
23from project import RemoteSpec
24from project import Project
25from project import MetaProject
26from project import R_HEADS
27from project import HEAD
28from error import ManifestParseError 25from error import ManifestParseError
29 26
30MANIFEST_FILE_NAME = 'manifest.xml' 27MANIFEST_FILE_NAME = 'manifest.xml'
31LOCAL_MANIFEST_NAME = 'local_manifest.xml' 28LOCAL_MANIFEST_NAME = 'local_manifest.xml'
32R_M = 'refs/remotes/m/' 29
30urlparse.uses_relative.extend(['ssh', 'git'])
31urlparse.uses_netloc.extend(['ssh', 'git'])
33 32
34class _Default(object): 33class _Default(object):
35 """Project defaults within the manifest.""" 34 """Project defaults within the manifest."""
36 35
37 revisionExpr = None 36 revisionExpr = None
38 remote = None 37 remote = None
38 sync_j = 1
39 sync_c = False
39 40
40class _XmlRemote(object): 41class _XmlRemote(object):
41 def __init__(self, 42 def __init__(self,
42 name, 43 name,
44 alias=None,
43 fetch=None, 45 fetch=None,
46 manifestUrl=None,
44 review=None): 47 review=None):
45 self.name = name 48 self.name = name
46 self.fetchUrl = fetch 49 self.fetchUrl = fetch
50 self.manifestUrl = manifestUrl
51 self.remoteAlias = alias
47 self.reviewUrl = review 52 self.reviewUrl = review
53 self.resolvedFetchUrl = self._resolveFetchUrl()
54
55 def _resolveFetchUrl(self):
56 url = self.fetchUrl.rstrip('/')
57 manifestUrl = self.manifestUrl.rstrip('/')
58 # urljoin will get confused if there is no scheme in the base url
59 # ie, if manifestUrl is of the form <hostname:port>
60 if manifestUrl.find(':') != manifestUrl.find('/') - 1:
61 manifestUrl = 'gopher://' + manifestUrl
62 url = urlparse.urljoin(manifestUrl, url)
63 return re.sub(r'^gopher://', '', url)
48 64
49 def ToRemoteSpec(self, projectName): 65 def ToRemoteSpec(self, projectName):
50 url = self.fetchUrl 66 url = self.resolvedFetchUrl.rstrip('/') + '/' + projectName
51 while url.endswith('/'): 67 remoteName = self.name
52 url = url[:-1] 68 if self.remoteAlias:
53 url += '/%s.git' % projectName 69 remoteName = self.remoteAlias
54 return RemoteSpec(self.name, url, self.reviewUrl) 70 return RemoteSpec(remoteName, url, self.reviewUrl)
55 71
56class XmlManifest(Manifest): 72class XmlManifest(object):
57 """manages the repo configuration file""" 73 """manages the repo configuration file"""
58 74
59 def __init__(self, repodir): 75 def __init__(self, repodir):
60 Manifest.__init__(self, repodir) 76 self.repodir = os.path.abspath(repodir)
77 self.topdir = os.path.dirname(self.repodir)
78 self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME)
79 self.globalConfig = GitConfig.ForUser()
80
81 self.repoProject = MetaProject(self, 'repo',
82 gitdir = os.path.join(repodir, 'repo/.git'),
83 worktree = os.path.join(repodir, 'repo'))
61 84
62 self._manifestFile = os.path.join(repodir, MANIFEST_FILE_NAME)
63 self.manifestProject = MetaProject(self, 'manifests', 85 self.manifestProject = MetaProject(self, 'manifests',
64 gitdir = os.path.join(repodir, 'manifests.git'), 86 gitdir = os.path.join(repodir, 'manifests.git'),
65 worktree = os.path.join(repodir, 'manifests')) 87 worktree = os.path.join(repodir, 'manifests'))
@@ -73,13 +95,13 @@ class XmlManifest(Manifest):
73 if not os.path.isfile(path): 95 if not os.path.isfile(path):
74 raise ManifestParseError('manifest %s not found' % name) 96 raise ManifestParseError('manifest %s not found' % name)
75 97
76 old = self._manifestFile 98 old = self.manifestFile
77 try: 99 try:
78 self._manifestFile = path 100 self.manifestFile = path
79 self._Unload() 101 self._Unload()
80 self._Load() 102 self._Load()
81 finally: 103 finally:
82 self._manifestFile = old 104 self.manifestFile = old
83 105
84 def Link(self, name): 106 def Link(self, name):
85 """Update the repo metadata to use a different manifest. 107 """Update the repo metadata to use a different manifest.
@@ -87,9 +109,9 @@ class XmlManifest(Manifest):
87 self.Override(name) 109 self.Override(name)
88 110
89 try: 111 try:
90 if os.path.exists(self._manifestFile): 112 if os.path.exists(self.manifestFile):
91 os.remove(self._manifestFile) 113 os.remove(self.manifestFile)
92 os.symlink('manifests/%s' % name, self._manifestFile) 114 os.symlink('manifests/%s' % name, self.manifestFile)
93 except OSError, e: 115 except OSError, e:
94 raise ManifestParseError('cannot link manifest %s' % name) 116 raise ManifestParseError('cannot link manifest %s' % name)
95 117
@@ -104,6 +126,13 @@ class XmlManifest(Manifest):
104 def Save(self, fd, peg_rev=False): 126 def Save(self, fd, peg_rev=False):
105 """Write the current manifest out to the given file descriptor. 127 """Write the current manifest out to the given file descriptor.
106 """ 128 """
129 mp = self.manifestProject
130
131 groups = mp.config.GetString('manifest.groups')
132 if not groups:
133 groups = 'default'
134 groups = [x for x in re.split(r'[,\s]+', groups) if x]
135
107 doc = xml.dom.minidom.Document() 136 doc = xml.dom.minidom.Document()
108 root = doc.createElement('manifest') 137 root = doc.createElement('manifest')
109 doc.appendChild(root) 138 doc.appendChild(root)
@@ -134,6 +163,12 @@ class XmlManifest(Manifest):
134 if d.revisionExpr: 163 if d.revisionExpr:
135 have_default = True 164 have_default = True
136 e.setAttribute('revision', d.revisionExpr) 165 e.setAttribute('revision', d.revisionExpr)
166 if d.sync_j > 1:
167 have_default = True
168 e.setAttribute('sync-j', '%d' % d.sync_j)
169 if d.sync_c:
170 have_default = True
171 e.setAttribute('sync-c', 'true')
137 if have_default: 172 if have_default:
138 root.appendChild(e) 173 root.appendChild(e)
139 root.appendChild(doc.createTextNode('')) 174 root.appendChild(doc.createTextNode(''))
@@ -149,6 +184,10 @@ class XmlManifest(Manifest):
149 184
150 for p in sort_projects: 185 for p in sort_projects:
151 p = self.projects[p] 186 p = self.projects[p]
187
188 if not p.MatchesGroups(groups):
189 continue
190
152 e = doc.createElement('project') 191 e = doc.createElement('project')
153 root.appendChild(e) 192 root.appendChild(e)
154 e.setAttribute('name', p.name) 193 e.setAttribute('name', p.name)
@@ -172,6 +211,29 @@ class XmlManifest(Manifest):
172 ce.setAttribute('dest', c.dest) 211 ce.setAttribute('dest', c.dest)
173 e.appendChild(ce) 212 e.appendChild(ce)
174 213
214 default_groups = ['default', 'name:%s' % p.name, 'path:%s' % p.relpath]
215 egroups = [g for g in p.groups if g not in default_groups]
216 if egroups:
217 e.setAttribute('groups', ','.join(egroups))
218
219 for a in p.annotations:
220 if a.keep == "true":
221 ae = doc.createElement('annotation')
222 ae.setAttribute('name', a.name)
223 ae.setAttribute('value', a.value)
224 e.appendChild(ae)
225
226 if p.sync_c:
227 e.setAttribute('sync-c', 'true')
228
229 if self._repo_hooks_project:
230 root.appendChild(doc.createTextNode(''))
231 e = doc.createElement('repo-hooks')
232 e.setAttribute('in-project', self._repo_hooks_project.name)
233 e.setAttribute('enabled-list',
234 ' '.join(self._repo_hooks_project.enabled_repo_hooks))
235 root.appendChild(e)
236
175 doc.writexml(fd, '', ' ', '\n', 'UTF-8') 237 doc.writexml(fd, '', ' ', '\n', 'UTF-8')
176 238
177 @property 239 @property
@@ -190,6 +252,11 @@ class XmlManifest(Manifest):
190 return self._default 252 return self._default
191 253
192 @property 254 @property
255 def repo_hooks_project(self):
256 self._Load()
257 return self._repo_hooks_project
258
259 @property
193 def notice(self): 260 def notice(self):
194 self._Load() 261 self._Load()
195 return self._notice 262 return self._notice
@@ -199,21 +266,16 @@ class XmlManifest(Manifest):
199 self._Load() 266 self._Load()
200 return self._manifest_server 267 return self._manifest_server
201 268
202 def InitBranch(self): 269 @property
203 m = self.manifestProject 270 def IsMirror(self):
204 if m.CurrentBranch is None: 271 return self.manifestProject.config.GetBoolean('repo.mirror')
205 return m.StartBranch('default')
206 return True
207
208 def SetMRefs(self, project):
209 if self.branch:
210 project._InitAnyMRef(R_M + self.branch)
211 272
212 def _Unload(self): 273 def _Unload(self):
213 self._loaded = False 274 self._loaded = False
214 self._projects = {} 275 self._projects = {}
215 self._remotes = {} 276 self._remotes = {}
216 self._default = None 277 self._default = None
278 self._repo_hooks_project = None
217 self._notice = None 279 self._notice = None
218 self.branch = None 280 self.branch = None
219 self._manifest_server = None 281 self._manifest_server = None
@@ -221,24 +283,20 @@ class XmlManifest(Manifest):
221 def _Load(self): 283 def _Load(self):
222 if not self._loaded: 284 if not self._loaded:
223 m = self.manifestProject 285 m = self.manifestProject
224 b = m.GetBranch(m.CurrentBranch) 286 b = m.GetBranch(m.CurrentBranch).merge
225 if b.remote and b.remote.name:
226 m.remote.name = b.remote.name
227 b = b.merge
228 if b is not None and b.startswith(R_HEADS): 287 if b is not None and b.startswith(R_HEADS):
229 b = b[len(R_HEADS):] 288 b = b[len(R_HEADS):]
230 self.branch = b 289 self.branch = b
231 290
232 self._ParseManifest(True) 291 nodes = []
292 nodes.append(self._ParseManifestXml(self.manifestFile,
293 self.manifestProject.worktree))
233 294
234 local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME) 295 local = os.path.join(self.repodir, LOCAL_MANIFEST_NAME)
235 if os.path.exists(local): 296 if os.path.exists(local):
236 try: 297 nodes.append(self._ParseManifestXml(local, self.repodir))
237 real = self._manifestFile 298
238 self._manifestFile = local 299 self._ParseManifest(nodes)
239 self._ParseManifest(False)
240 finally:
241 self._manifestFile = real
242 300
243 if self.IsMirror: 301 if self.IsMirror:
244 self._AddMetaProjectMirror(self.repoProject) 302 self._AddMetaProjectMirror(self.repoProject)
@@ -246,73 +304,117 @@ class XmlManifest(Manifest):
246 304
247 self._loaded = True 305 self._loaded = True
248 306
249 def _ParseManifest(self, is_root_file): 307 def _ParseManifestXml(self, path, include_root):
250 root = xml.dom.minidom.parse(self._manifestFile) 308 root = xml.dom.minidom.parse(path)
251 if not root or not root.childNodes: 309 if not root or not root.childNodes:
252 raise ManifestParseError, \ 310 raise ManifestParseError("no root node in %s" % (path,))
253 "no root node in %s" % \
254 self._manifestFile
255 311
256 config = root.childNodes[0] 312 config = root.childNodes[0]
257 if config.nodeName != 'manifest': 313 if config.nodeName != 'manifest':
258 raise ManifestParseError, \ 314 raise ManifestParseError("no <manifest> in %s" % (path,))
259 "no <manifest> in %s" % \
260 self._manifestFile
261 315
316 nodes = []
262 for node in config.childNodes: 317 for node in config.childNodes:
263 if node.nodeName == 'remove-project': 318 if node.nodeName == 'include':
264 name = self._reqatt(node, 'name') 319 name = self._reqatt(node, 'name')
265 try: 320 fp = os.path.join(include_root, name)
266 del self._projects[name] 321 if not os.path.isfile(fp):
267 except KeyError: 322 raise ManifestParseError, \
268 raise ManifestParseError, \ 323 "include %s doesn't exist or isn't a file" % \
269 'project %s not found' % \ 324 (name,)
270 (name) 325 try:
326 nodes.extend(self._ParseManifestXml(fp, include_root))
327 # should isolate this to the exact exception, but that's
328 # tricky. actual parsing implementation may vary.
329 except (KeyboardInterrupt, RuntimeError, SystemExit):
330 raise
331 except Exception, e:
332 raise ManifestParseError(
333 "failed parsing included manifest %s: %s", (name, e))
334 else:
335 nodes.append(node)
336 return nodes
271 337
272 for node in config.childNodes: 338 def _ParseManifest(self, node_list):
339 for node in itertools.chain(*node_list):
273 if node.nodeName == 'remote': 340 if node.nodeName == 'remote':
274 remote = self._ParseRemote(node) 341 remote = self._ParseRemote(node)
275 if self._remotes.get(remote.name): 342 if self._remotes.get(remote.name):
276 raise ManifestParseError, \ 343 raise ManifestParseError(
277 'duplicate remote %s in %s' % \ 344 'duplicate remote %s in %s' %
278 (remote.name, self._manifestFile) 345 (remote.name, self.manifestFile))
279 self._remotes[remote.name] = remote 346 self._remotes[remote.name] = remote
280 347
281 for node in config.childNodes: 348 for node in itertools.chain(*node_list):
282 if node.nodeName == 'default': 349 if node.nodeName == 'default':
283 if self._default is not None: 350 if self._default is not None:
284 raise ManifestParseError, \ 351 raise ManifestParseError(
285 'duplicate default in %s' % \ 352 'duplicate default in %s' %
286 (self._manifestFile) 353 (self.manifestFile))
287 self._default = self._ParseDefault(node) 354 self._default = self._ParseDefault(node)
288 if self._default is None: 355 if self._default is None:
289 self._default = _Default() 356 self._default = _Default()
290 357
291 for node in config.childNodes: 358 for node in itertools.chain(*node_list):
292 if node.nodeName == 'notice': 359 if node.nodeName == 'notice':
293 if self._notice is not None: 360 if self._notice is not None:
294 raise ManifestParseError, \ 361 raise ManifestParseError(
295 'duplicate notice in %s' % \ 362 'duplicate notice in %s' %
296 (self.manifestFile) 363 (self.manifestFile))
297 self._notice = self._ParseNotice(node) 364 self._notice = self._ParseNotice(node)
298 365
299 for node in config.childNodes: 366 for node in itertools.chain(*node_list):
300 if node.nodeName == 'manifest-server': 367 if node.nodeName == 'manifest-server':
301 url = self._reqatt(node, 'url') 368 url = self._reqatt(node, 'url')
302 if self._manifest_server is not None: 369 if self._manifest_server is not None:
303 raise ManifestParseError, \ 370 raise ManifestParseError(
304 'duplicate manifest-server in %s' % \ 371 'duplicate manifest-server in %s' %
305 (self.manifestFile) 372 (self.manifestFile))
306 self._manifest_server = url 373 self._manifest_server = url
307 374
308 for node in config.childNodes: 375 for node in itertools.chain(*node_list):
309 if node.nodeName == 'project': 376 if node.nodeName == 'project':
310 project = self._ParseProject(node) 377 project = self._ParseProject(node)
311 if self._projects.get(project.name): 378 if self._projects.get(project.name):
312 raise ManifestParseError, \ 379 raise ManifestParseError(
313 'duplicate project %s in %s' % \ 380 'duplicate project %s in %s' %
314 (project.name, self._manifestFile) 381 (project.name, self.manifestFile))
315 self._projects[project.name] = project 382 self._projects[project.name] = project
383 if node.nodeName == 'repo-hooks':
384 # Get the name of the project and the (space-separated) list of enabled.
385 repo_hooks_project = self._reqatt(node, 'in-project')
386 enabled_repo_hooks = self._reqatt(node, 'enabled-list').split()
387
388 # Only one project can be the hooks project
389 if self._repo_hooks_project is not None:
390 raise ManifestParseError(
391 'duplicate repo-hooks in %s' %
392 (self.manifestFile))
393
394 # Store a reference to the Project.
395 try:
396 self._repo_hooks_project = self._projects[repo_hooks_project]
397 except KeyError:
398 raise ManifestParseError(
399 'project %s not found for repo-hooks' %
400 (repo_hooks_project))
401
402 # Store the enabled hooks in the Project object.
403 self._repo_hooks_project.enabled_repo_hooks = enabled_repo_hooks
404 if node.nodeName == 'remove-project':
405 name = self._reqatt(node, 'name')
406 try:
407 del self._projects[name]
408 except KeyError:
409 raise ManifestParseError(
410 'project %s not found' %
411 (name))
412
413 # If the manifest removes the hooks project, treat it as if it deleted
414 # the repo-hooks element too.
415 if self._repo_hooks_project and (self._repo_hooks_project.name == name):
416 self._repo_hooks_project = None
417
316 418
317 def _AddMetaProjectMirror(self, m): 419 def _AddMetaProjectMirror(self, m):
318 name = None 420 name = None
@@ -321,7 +423,7 @@ class XmlManifest(Manifest):
321 raise ManifestParseError, 'refusing to mirror %s' % m_url 423 raise ManifestParseError, 'refusing to mirror %s' % m_url
322 424
323 if self._default and self._default.remote: 425 if self._default and self._default.remote:
324 url = self._default.remote.fetchUrl 426 url = self._default.remote.resolvedFetchUrl
325 if not url.endswith('/'): 427 if not url.endswith('/'):
326 url += '/' 428 url += '/'
327 if m_url.startswith(url): 429 if m_url.startswith(url):
@@ -330,7 +432,8 @@ class XmlManifest(Manifest):
330 432
331 if name is None: 433 if name is None:
332 s = m_url.rindex('/') + 1 434 s = m_url.rindex('/') + 1
333 remote = _XmlRemote('origin', m_url[:s]) 435 manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
436 remote = _XmlRemote('origin', fetch=m_url[:s], manifestUrl=manifestUrl)
334 name = m_url[s:] 437 name = m_url[s:]
335 438
336 if name.endswith('.git'): 439 if name.endswith('.git'):
@@ -354,11 +457,15 @@ class XmlManifest(Manifest):
354 reads a <remote> element from the manifest file 457 reads a <remote> element from the manifest file
355 """ 458 """
356 name = self._reqatt(node, 'name') 459 name = self._reqatt(node, 'name')
460 alias = node.getAttribute('alias')
461 if alias == '':
462 alias = None
357 fetch = self._reqatt(node, 'fetch') 463 fetch = self._reqatt(node, 'fetch')
358 review = node.getAttribute('review') 464 review = node.getAttribute('review')
359 if review == '': 465 if review == '':
360 review = None 466 review = None
361 return _XmlRemote(name, fetch, review) 467 manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
468 return _XmlRemote(name, alias, fetch, manifestUrl, review)
362 469
363 def _ParseDefault(self, node): 470 def _ParseDefault(self, node):
364 """ 471 """
@@ -369,6 +476,18 @@ class XmlManifest(Manifest):
369 d.revisionExpr = node.getAttribute('revision') 476 d.revisionExpr = node.getAttribute('revision')
370 if d.revisionExpr == '': 477 if d.revisionExpr == '':
371 d.revisionExpr = None 478 d.revisionExpr = None
479
480 sync_j = node.getAttribute('sync-j')
481 if sync_j == '' or sync_j is None:
482 d.sync_j = 1
483 else:
484 d.sync_j = int(sync_j)
485
486 sync_c = node.getAttribute('sync-c')
487 if not sync_c:
488 d.sync_c = False
489 else:
490 d.sync_c = sync_c.lower() in ("yes", "true", "1")
372 return d 491 return d
373 492
374 def _ParseNotice(self, node): 493 def _ParseNotice(self, node):
@@ -422,7 +541,7 @@ class XmlManifest(Manifest):
422 if remote is None: 541 if remote is None:
423 raise ManifestParseError, \ 542 raise ManifestParseError, \
424 "no remote for project %s within %s" % \ 543 "no remote for project %s within %s" % \
425 (name, self._manifestFile) 544 (name, self.manifestFile)
426 545
427 revisionExpr = node.getAttribute('revision') 546 revisionExpr = node.getAttribute('revision')
428 if not revisionExpr: 547 if not revisionExpr:
@@ -430,7 +549,7 @@ class XmlManifest(Manifest):
430 if not revisionExpr: 549 if not revisionExpr:
431 raise ManifestParseError, \ 550 raise ManifestParseError, \
432 "no revision for project %s within %s" % \ 551 "no revision for project %s within %s" % \
433 (name, self._manifestFile) 552 (name, self.manifestFile)
434 553
435 path = node.getAttribute('path') 554 path = node.getAttribute('path')
436 if not path: 555 if not path:
@@ -438,7 +557,27 @@ class XmlManifest(Manifest):
438 if path.startswith('/'): 557 if path.startswith('/'):
439 raise ManifestParseError, \ 558 raise ManifestParseError, \
440 "project %s path cannot be absolute in %s" % \ 559 "project %s path cannot be absolute in %s" % \
441 (name, self._manifestFile) 560 (name, self.manifestFile)
561
562 rebase = node.getAttribute('rebase')
563 if not rebase:
564 rebase = True
565 else:
566 rebase = rebase.lower() in ("yes", "true", "1")
567
568 sync_c = node.getAttribute('sync-c')
569 if not sync_c:
570 sync_c = False
571 else:
572 sync_c = sync_c.lower() in ("yes", "true", "1")
573
574 groups = ''
575 if node.hasAttribute('groups'):
576 groups = node.getAttribute('groups')
577 groups = [x for x in re.split('[,\s]+', groups) if x]
578
579 default_groups = ['default', 'name:%s' % name, 'path:%s' % path]
580 groups.extend(set(default_groups).difference(groups))
442 581
443 if self.IsMirror: 582 if self.IsMirror:
444 relpath = None 583 relpath = None
@@ -455,11 +594,16 @@ class XmlManifest(Manifest):
455 worktree = worktree, 594 worktree = worktree,
456 relpath = path, 595 relpath = path,
457 revisionExpr = revisionExpr, 596 revisionExpr = revisionExpr,
458 revisionId = None) 597 revisionId = None,
598 rebase = rebase,
599 groups = groups,
600 sync_c = sync_c)
459 601
460 for n in node.childNodes: 602 for n in node.childNodes:
461 if n.nodeName == 'copyfile': 603 if n.nodeName == 'copyfile':
462 self._ParseCopyFile(project, n) 604 self._ParseCopyFile(project, n)
605 if n.nodeName == 'annotation':
606 self._ParseAnnotation(project, n)
463 607
464 return project 608 return project
465 609
@@ -471,6 +615,17 @@ class XmlManifest(Manifest):
471 # dest is relative to the top of the tree 615 # dest is relative to the top of the tree
472 project.AddCopyFile(src, dest, os.path.join(self.topdir, dest)) 616 project.AddCopyFile(src, dest, os.path.join(self.topdir, dest))
473 617
618 def _ParseAnnotation(self, project, node):
619 name = self._reqatt(node, 'name')
620 value = self._reqatt(node, 'value')
621 try:
622 keep = self._reqatt(node, 'keep').lower()
623 except ManifestParseError:
624 keep = "true"
625 if keep != "true" and keep != "false":
626 raise ManifestParseError, "optional \"keep\" attribute must be \"true\" or \"false\""
627 project.AddAnnotation(name, value, keep)
628
474 def _get_remote(self, node): 629 def _get_remote(self, node):
475 name = node.getAttribute('remote') 630 name = node.getAttribute('remote')
476 if not name: 631 if not name:
@@ -480,7 +635,7 @@ class XmlManifest(Manifest):
480 if not v: 635 if not v:
481 raise ManifestParseError, \ 636 raise ManifestParseError, \
482 "remote %s not defined in %s" % \ 637 "remote %s not defined in %s" % \
483 (name, self._manifestFile) 638 (name, self.manifestFile)
484 return v 639 return v
485 640
486 def _reqatt(self, node, attname): 641 def _reqatt(self, node, attname):
@@ -491,5 +646,5 @@ class XmlManifest(Manifest):
491 if not v: 646 if not v:
492 raise ManifestParseError, \ 647 raise ManifestParseError, \
493 "no %s in <%s> within %s" % \ 648 "no %s in <%s> within %s" % \
494 (attname, node.nodeName, self._manifestFile) 649 (attname, node.nodeName, self.manifestFile)
495 return v 650 return v