diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | cf31fe9b4fb650b27e19f5d7ee7297e383660caf (patch) | |
tree | d04ca6a45d579dca5e5469606c48c405aee68f4b /manifest.py | |
download | git-repo-cf31fe9b4fb650b27e19f5d7ee7297e383660caf.tar.gz |
Initial Contributionv1.0
Diffstat (limited to 'manifest.py')
-rw-r--r-- | manifest.py | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/manifest.py b/manifest.py new file mode 100644 index 00000000..45b0f9a5 --- /dev/null +++ b/manifest.py | |||
@@ -0,0 +1,338 @@ | |||
1 | # | ||
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 | |||
16 | import os | ||
17 | import sys | ||
18 | import xml.dom.minidom | ||
19 | |||
20 | from editor import Editor | ||
21 | from git_config import GitConfig, IsId | ||
22 | from import_tar import ImportTar | ||
23 | from import_zip import ImportZip | ||
24 | from project import Project, MetaProject, R_TAGS | ||
25 | from remote import Remote | ||
26 | from error import ManifestParseError | ||
27 | |||
28 | MANIFEST_FILE_NAME = 'manifest.xml' | ||
29 | |||
30 | class _Default(object): | ||
31 | """Project defaults within the manifest.""" | ||
32 | |||
33 | revision = None | ||
34 | remote = None | ||
35 | |||
36 | |||
37 | class Manifest(object): | ||
38 | """manages the repo configuration file""" | ||
39 | |||
40 | def __init__(self, repodir): | ||
41 | self.repodir = os.path.abspath(repodir) | ||
42 | self.topdir = os.path.dirname(self.repodir) | ||
43 | self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME) | ||
44 | |||
45 | self.globalConfig = GitConfig.ForUser() | ||
46 | Editor.globalConfig = self.globalConfig | ||
47 | |||
48 | self.repoProject = MetaProject(self, 'repo', | ||
49 | gitdir = os.path.join(repodir, 'repo/.git'), | ||
50 | worktree = os.path.join(repodir, 'repo')) | ||
51 | |||
52 | wt = os.path.join(repodir, 'manifests') | ||
53 | gd_new = os.path.join(repodir, 'manifests.git') | ||
54 | gd_old = os.path.join(wt, '.git') | ||
55 | if os.path.exists(gd_new) or not os.path.exists(gd_old): | ||
56 | gd = gd_new | ||
57 | else: | ||
58 | gd = gd_old | ||
59 | self.manifestProject = MetaProject(self, 'manifests', | ||
60 | gitdir = gd, | ||
61 | worktree = wt) | ||
62 | |||
63 | self._Unload() | ||
64 | |||
65 | def Link(self, name): | ||
66 | """Update the repo metadata to use a different manifest. | ||
67 | """ | ||
68 | path = os.path.join(self.manifestProject.worktree, name) | ||
69 | if not os.path.isfile(path): | ||
70 | raise ManifestParseError('manifest %s not found' % name) | ||
71 | |||
72 | old = self.manifestFile | ||
73 | try: | ||
74 | self.manifestFile = path | ||
75 | self._Unload() | ||
76 | self._Load() | ||
77 | finally: | ||
78 | self.manifestFile = old | ||
79 | |||
80 | try: | ||
81 | if os.path.exists(self.manifestFile): | ||
82 | os.remove(self.manifestFile) | ||
83 | os.symlink('manifests/%s' % name, self.manifestFile) | ||
84 | except OSError, e: | ||
85 | raise ManifestParseError('cannot link manifest %s' % name) | ||
86 | |||
87 | @property | ||
88 | def projects(self): | ||
89 | self._Load() | ||
90 | return self._projects | ||
91 | |||
92 | @property | ||
93 | def remotes(self): | ||
94 | self._Load() | ||
95 | return self._remotes | ||
96 | |||
97 | @property | ||
98 | def default(self): | ||
99 | self._Load() | ||
100 | return self._default | ||
101 | |||
102 | def _Unload(self): | ||
103 | self._loaded = False | ||
104 | self._projects = {} | ||
105 | self._remotes = {} | ||
106 | self._default = None | ||
107 | self.branch = None | ||
108 | |||
109 | def _Load(self): | ||
110 | if not self._loaded: | ||
111 | self._ParseManifest() | ||
112 | self._loaded = True | ||
113 | |||
114 | def _ParseManifest(self): | ||
115 | root = xml.dom.minidom.parse(self.manifestFile) | ||
116 | if not root or not root.childNodes: | ||
117 | raise ManifestParseError, \ | ||
118 | "no root node in %s" % \ | ||
119 | self.manifestFile | ||
120 | |||
121 | config = root.childNodes[0] | ||
122 | if config.nodeName != 'manifest': | ||
123 | raise ManifestParseError, \ | ||
124 | "no <manifest> in %s" % \ | ||
125 | self.manifestFile | ||
126 | |||
127 | self.branch = config.getAttribute('branch') | ||
128 | if not self.branch: | ||
129 | self.branch = 'default' | ||
130 | |||
131 | for node in config.childNodes: | ||
132 | if node.nodeName == 'remote': | ||
133 | remote = self._ParseRemote(node) | ||
134 | if self._remotes.get(remote.name): | ||
135 | raise ManifestParseError, \ | ||
136 | 'duplicate remote %s in %s' % \ | ||
137 | (remote.name, self.manifestFile) | ||
138 | self._remotes[remote.name] = remote | ||
139 | |||
140 | for node in config.childNodes: | ||
141 | if node.nodeName == 'default': | ||
142 | if self._default is not None: | ||
143 | raise ManifestParseError, \ | ||
144 | 'duplicate default in %s' % \ | ||
145 | (self.manifestFile) | ||
146 | self._default = self._ParseDefault(node) | ||
147 | if self._default is None: | ||
148 | self._default = _Default() | ||
149 | |||
150 | for node in config.childNodes: | ||
151 | if node.nodeName == 'project': | ||
152 | project = self._ParseProject(node) | ||
153 | if self._projects.get(project.name): | ||
154 | raise ManifestParseError, \ | ||
155 | 'duplicate project %s in %s' % \ | ||
156 | (project.name, self.manifestFile) | ||
157 | self._projects[project.name] = project | ||
158 | |||
159 | def _ParseRemote(self, node): | ||
160 | """ | ||
161 | reads a <remote> element from the manifest file | ||
162 | """ | ||
163 | name = self._reqatt(node, 'name') | ||
164 | fetch = self._reqatt(node, 'fetch') | ||
165 | review = node.getAttribute('review') | ||
166 | |||
167 | r = Remote(name=name, | ||
168 | fetch=fetch, | ||
169 | review=review) | ||
170 | |||
171 | for n in node.childNodes: | ||
172 | if n.nodeName == 'require': | ||
173 | r.requiredCommits.append(self._reqatt(n, 'commit')) | ||
174 | |||
175 | return r | ||
176 | |||
177 | def _ParseDefault(self, node): | ||
178 | """ | ||
179 | reads a <default> element from the manifest file | ||
180 | """ | ||
181 | d = _Default() | ||
182 | d.remote = self._get_remote(node) | ||
183 | d.revision = node.getAttribute('revision') | ||
184 | return d | ||
185 | |||
186 | def _ParseProject(self, node): | ||
187 | """ | ||
188 | reads a <project> element from the manifest file | ||
189 | """ | ||
190 | name = self._reqatt(node, 'name') | ||
191 | |||
192 | remote = self._get_remote(node) | ||
193 | if remote is None: | ||
194 | remote = self._default.remote | ||
195 | if remote is None: | ||
196 | raise ManifestParseError, \ | ||
197 | "no remote for project %s within %s" % \ | ||
198 | (name, self.manifestFile) | ||
199 | |||
200 | revision = node.getAttribute('revision') | ||
201 | if not revision: | ||
202 | revision = self._default.revision | ||
203 | if not revision: | ||
204 | raise ManifestParseError, \ | ||
205 | "no revision for project %s within %s" % \ | ||
206 | (name, self.manifestFile) | ||
207 | |||
208 | path = node.getAttribute('path') | ||
209 | if not path: | ||
210 | path = name | ||
211 | if path.startswith('/'): | ||
212 | raise ManifestParseError, \ | ||
213 | "project %s path cannot be absolute in %s" % \ | ||
214 | (name, self.manifestFile) | ||
215 | |||
216 | worktree = os.path.join(self.topdir, path) | ||
217 | gitdir = os.path.join(self.repodir, 'projects/%s.git' % path) | ||
218 | |||
219 | project = Project(manifest = self, | ||
220 | name = name, | ||
221 | remote = remote, | ||
222 | gitdir = gitdir, | ||
223 | worktree = worktree, | ||
224 | relpath = path, | ||
225 | revision = revision) | ||
226 | |||
227 | for n in node.childNodes: | ||
228 | if n.nodeName == 'remote': | ||
229 | r = self._ParseRemote(n) | ||
230 | if project.extraRemotes.get(r.name) \ | ||
231 | or project.remote.name == r.name: | ||
232 | raise ManifestParseError, \ | ||
233 | 'duplicate remote %s in project %s in %s' % \ | ||
234 | (r.name, project.name, self.manifestFile) | ||
235 | project.extraRemotes[r.name] = r | ||
236 | elif n.nodeName == 'copyfile': | ||
237 | self._ParseCopyFile(project, n) | ||
238 | |||
239 | to_resolve = [] | ||
240 | by_version = {} | ||
241 | |||
242 | for n in node.childNodes: | ||
243 | if n.nodeName == 'import': | ||
244 | self._ParseImport(project, n, to_resolve, by_version) | ||
245 | |||
246 | for pair in to_resolve: | ||
247 | sn, pr = pair | ||
248 | try: | ||
249 | sn.SetParent(by_version[pr].commit) | ||
250 | except KeyError: | ||
251 | raise ManifestParseError, \ | ||
252 | 'snapshot %s not in project %s in %s' % \ | ||
253 | (pr, project.name, self.manifestFile) | ||
254 | |||
255 | return project | ||
256 | |||
257 | def _ParseImport(self, project, import_node, to_resolve, by_version): | ||
258 | first_url = None | ||
259 | for node in import_node.childNodes: | ||
260 | if node.nodeName == 'mirror': | ||
261 | first_url = self._reqatt(node, 'url') | ||
262 | break | ||
263 | if not first_url: | ||
264 | raise ManifestParseError, \ | ||
265 | 'mirror url required for project %s in %s' % \ | ||
266 | (project.name, self.manifestFile) | ||
267 | |||
268 | imp = None | ||
269 | for cls in [ImportTar, ImportZip]: | ||
270 | if cls.CanAccept(first_url): | ||
271 | imp = cls() | ||
272 | break | ||
273 | if not imp: | ||
274 | raise ManifestParseError, \ | ||
275 | 'snapshot %s unsupported for project %s in %s' % \ | ||
276 | (first_url, project.name, self.manifestFile) | ||
277 | |||
278 | imp.SetProject(project) | ||
279 | |||
280 | for node in import_node.childNodes: | ||
281 | if node.nodeName == 'remap': | ||
282 | old = node.getAttribute('strip') | ||
283 | new = node.getAttribute('insert') | ||
284 | imp.RemapPath(old, new) | ||
285 | |||
286 | elif node.nodeName == 'mirror': | ||
287 | imp.AddUrl(self._reqatt(node, 'url')) | ||
288 | |||
289 | for node in import_node.childNodes: | ||
290 | if node.nodeName == 'snapshot': | ||
291 | sn = imp.Clone() | ||
292 | sn.SetVersion(self._reqatt(node, 'version')) | ||
293 | sn.SetCommit(node.getAttribute('check')) | ||
294 | |||
295 | pr = node.getAttribute('prior') | ||
296 | if pr: | ||
297 | if IsId(pr): | ||
298 | sn.SetParent(pr) | ||
299 | else: | ||
300 | to_resolve.append((sn, pr)) | ||
301 | |||
302 | rev = R_TAGS + sn.TagName | ||
303 | |||
304 | if rev in project.snapshots: | ||
305 | raise ManifestParseError, \ | ||
306 | 'duplicate snapshot %s for project %s in %s' % \ | ||
307 | (sn.version, project.name, self.manifestFile) | ||
308 | project.snapshots[rev] = sn | ||
309 | by_version[sn.version] = sn | ||
310 | |||
311 | def _ParseCopyFile(self, project, node): | ||
312 | src = self._reqatt(node, 'src') | ||
313 | dest = self._reqatt(node, 'dest') | ||
314 | # src is project relative, and dest is relative to the top of the tree | ||
315 | project.AddCopyFile(src, os.path.join(self.topdir, dest)) | ||
316 | |||
317 | def _get_remote(self, node): | ||
318 | name = node.getAttribute('remote') | ||
319 | if not name: | ||
320 | return None | ||
321 | |||
322 | v = self._remotes.get(name) | ||
323 | if not v: | ||
324 | raise ManifestParseError, \ | ||
325 | "remote %s not defined in %s" % \ | ||
326 | (name, self.manifestFile) | ||
327 | return v | ||
328 | |||
329 | def _reqatt(self, node, attname): | ||
330 | """ | ||
331 | reads a required attribute from the node. | ||
332 | """ | ||
333 | v = node.getAttribute(attname) | ||
334 | if not v: | ||
335 | raise ManifestParseError, \ | ||
336 | "no %s in <%s> within %s" % \ | ||
337 | (attname, node.nodeName, self.manifestFile) | ||
338 | return v | ||