OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Meta checkout manager supporting both Subversion and GIT. | 6 """Meta checkout manager supporting both Subversion and GIT. |
7 | 7 |
8 Files | 8 Files |
9 .gclient : Current client configuration, written by 'config' command. | 9 .gclient : Current client configuration, written by 'config' command. |
10 Format is a Python script defining 'solutions', a list whose | 10 Format is a Python script defining 'solutions', a list whose |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 elif var_name in self._local_scope.get("vars", {}): | 134 elif var_name in self._local_scope.get("vars", {}): |
135 return self._local_scope["vars"][var_name] | 135 return self._local_scope["vars"][var_name] |
136 raise gclient_utils.Error("Var is not defined: %s" % var_name) | 136 raise gclient_utils.Error("Var is not defined: %s" % var_name) |
137 | 137 |
138 | 138 |
139 class Dependency(GClientKeywords, gclient_utils.WorkItem): | 139 class Dependency(GClientKeywords, gclient_utils.WorkItem): |
140 """Object that represents a dependency checkout.""" | 140 """Object that represents a dependency checkout.""" |
141 | 141 |
142 def __init__(self, parent, name, url, safesync_url, custom_deps, | 142 def __init__(self, parent, name, url, safesync_url, custom_deps, |
143 custom_vars, deps_file, should_process): | 143 custom_vars, deps_file, should_process): |
| 144 # Warning: this function can be called from any thread. Both |
| 145 # self.dependencies and self.requirements are read and modified from |
| 146 # multiple threads at the same time. Sad. |
144 GClientKeywords.__init__(self) | 147 GClientKeywords.__init__(self) |
145 gclient_utils.WorkItem.__init__(self) | 148 gclient_utils.WorkItem.__init__(self) |
146 self.parent = parent | 149 self.parent = parent |
147 self.name = name | 150 self.name = name |
148 self.url = url | 151 self.url = url |
149 self.parsed_url = None | 152 self.parsed_url = None |
150 # These 2 are only set in .gclient and not in DEPS files. | 153 # These 2 are only set in .gclient and not in DEPS files. |
151 self.safesync_url = safesync_url | 154 self.safesync_url = safesync_url |
152 self.custom_vars = custom_vars or {} | 155 self.custom_vars = custom_vars or {} |
153 self.custom_deps = custom_deps or {} | 156 self.custom_deps = custom_deps or {} |
154 self.deps_hooks = [] | 157 self.deps_hooks = [] |
155 self.dependencies = [] | 158 self.dependencies = [] |
156 self.deps_file = deps_file | 159 self.deps_file = deps_file |
157 # A cache of the files affected by the current operation, necessary for | 160 # A cache of the files affected by the current operation, necessary for |
158 # hooks. | 161 # hooks. |
159 self._file_list = [] | 162 self._file_list = [] |
160 # If it is not set to True, the dependency wasn't processed for its child | 163 # If it is not set to True, the dependency wasn't processed for its child |
161 # dependency, i.e. its DEPS wasn't read. | 164 # dependency, i.e. its DEPS wasn't read. |
162 self.deps_parsed = False | 165 self.deps_parsed = False |
163 # This dependency should be processed, i.e. checked out | 166 # This dependency should be processed, i.e. checked out |
164 self.should_process = should_process | 167 self.should_process = should_process |
165 # This dependency has been processed, i.e. checked out | 168 # This dependency has been processed, i.e. checked out |
166 self.processed = False | 169 self.processed = False |
167 # This dependency had its hook run | 170 # This dependency had its hook run |
168 self.hooks_ran = False | 171 self.hooks_ran = False |
169 # Required dependencies to run before running this one: | 172 # Required dependencies to run before running this one: |
170 self.requirements = [] | 173 self.requirements = set() |
171 if self.parent and self.parent.name: | 174 |
172 self.requirements.append(self.parent.name) | 175 self._FindDependencies() |
173 if isinstance(self.url, self.FromImpl): | |
174 self.requirements.append(self.url.module_name) | |
175 | 176 |
176 # Sanity checks | 177 # Sanity checks |
177 if not self.name and self.parent: | 178 if not self.name and self.parent: |
178 raise gclient_utils.Error('Dependency without name') | 179 raise gclient_utils.Error('Dependency without name') |
179 if not isinstance(self.url, | 180 if not isinstance(self.url, |
180 (basestring, self.FromImpl, self.FileImpl, None.__class__)): | 181 (basestring, self.FromImpl, self.FileImpl, None.__class__)): |
181 raise gclient_utils.Error('dependency url must be either a string, None, ' | 182 raise gclient_utils.Error('dependency url must be either a string, None, ' |
182 'File() or From() instead of %s' % | 183 'File() or From() instead of %s' % |
183 self.url.__class__.__name__) | 184 self.url.__class__.__name__) |
184 if '/' in self.deps_file or '\\' in self.deps_file: | 185 if '/' in self.deps_file or '\\' in self.deps_file: |
185 raise gclient_utils.Error('deps_file name must not be a path, just a ' | 186 raise gclient_utils.Error('deps_file name must not be a path, just a ' |
186 'filename. %s' % self.deps_file) | 187 'filename. %s' % self.deps_file) |
187 | 188 |
| 189 def _FindDependencies(self): |
| 190 """Setup self.requirements and find any other dependency who would have self |
| 191 as a requirement. |
| 192 """ |
| 193 # self.parent is implicitly a requirement. This will be recursive by |
| 194 # definition. |
| 195 if self.parent and self.parent.name: |
| 196 self.requirements.add(self.parent.name) |
| 197 |
| 198 # For a tree with at least 2 levels*, the leaf node needs to depend |
| 199 # on the level higher up in an orderly way. |
| 200 # This becomes messy for >2 depth as the DEPS file format is a dictionary, |
| 201 # thus unsorted, while the .gclient format is a list thus sorted. |
| 202 # |
| 203 # * _recursion_limit is hard coded 2 and there is no hope to change this |
| 204 # value. |
| 205 # |
| 206 # Interestingly enough, the following condition only works in the case we |
| 207 # want: self is a 2nd level node. 3nd level node wouldn't need this since |
| 208 # they already have their parent as a requirement. |
| 209 if self.parent in self.root_parent().dependencies: |
| 210 root_deps = self.root_parent().dependencies |
| 211 for i in range(0, root_deps.index(self.parent)): |
| 212 value = root_deps[i] |
| 213 if value.name: |
| 214 self.requirements.add(value.name) |
| 215 |
| 216 if isinstance(self.url, self.FromImpl): |
| 217 self.requirements.add(self.url.module_name) |
| 218 |
| 219 if self.name: |
| 220 def yield_full_tree(root): |
| 221 """Depth-first recursion.""" |
| 222 yield root |
| 223 for i in root.dependencies: |
| 224 for j in yield_full_tree(i): |
| 225 yield j |
| 226 |
| 227 for obj in yield_full_tree(self.root_parent()): |
| 228 if obj is self or not obj.name: |
| 229 continue |
| 230 # Step 1: Find any requirements self may need. |
| 231 if self.name.startswith(posixpath.join(obj.name, '')): |
| 232 self.requirements.add(obj.name) |
| 233 # Step 2: Find any requirements self may impose. |
| 234 if obj.name.startswith(posixpath.join(self.name, '')): |
| 235 obj.requirements.add(self.name) |
| 236 |
188 def LateOverride(self, url): | 237 def LateOverride(self, url): |
189 """Resolves the parsed url from url. | 238 """Resolves the parsed url from url. |
190 | 239 |
191 Manages From() keyword accordingly. Do not touch self.parsed_url nor | 240 Manages From() keyword accordingly. Do not touch self.parsed_url nor |
192 self.url because it may called with other urls due to From().""" | 241 self.url because it may called with other urls due to From().""" |
193 assert self.parsed_url == None or not self.should_process, self.parsed_url | 242 assert self.parsed_url == None or not self.should_process, self.parsed_url |
194 overriden_url = self.get_custom_deps(self.name, url) | 243 overriden_url = self.get_custom_deps(self.name, url) |
195 if overriden_url != url: | 244 if overriden_url != url: |
196 logging.info('%s, %s was overriden to %s' % (self.name, url, | 245 logging.info('%s, %s was overriden to %s' % (self.name, url, |
197 overriden_url)) | 246 overriden_url)) |
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
399 maybeGetParentRevision(options) | 448 maybeGetParentRevision(options) |
400 scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name) | 449 scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name) |
401 scm.RunCommand(command, options, args, self._file_list) | 450 scm.RunCommand(command, options, args, self._file_list) |
402 maybeConvertToDateRevision(options) | 451 maybeConvertToDateRevision(options) |
403 self._file_list = [os.path.join(self.name, f.strip()) | 452 self._file_list = [os.path.join(self.name, f.strip()) |
404 for f in self._file_list] | 453 for f in self._file_list] |
405 self.processed = True | 454 self.processed = True |
406 if self.recursion_limit() > 0: | 455 if self.recursion_limit() > 0: |
407 # Then we can parse the DEPS file. | 456 # Then we can parse the DEPS file. |
408 self.ParseDepsFile() | 457 self.ParseDepsFile() |
409 # Adjust the implicit dependency requirement; e.g. if a DEPS file contains | |
410 # both src/foo and src/foo/bar, src/foo/bar is implicitly dependent of | |
411 # src/foo. Yes, it's O(n^2)... It's important to do that before | |
412 # enqueueing them. | |
413 for s in self.dependencies: | |
414 for s2 in self.dependencies: | |
415 if s is s2: | |
416 continue | |
417 if s.name.startswith(posixpath.join(s2.name, '')): | |
418 s.requirements.append(s2.name) | |
419 | 458 |
420 # Parse the dependencies of this dependency. | 459 # Parse the dependencies of this dependency. |
421 for s in self.dependencies: | 460 for s in self.dependencies: |
422 work_queue.enqueue(s) | 461 work_queue.enqueue(s) |
423 | 462 |
424 def RunHooksRecursively(self, options): | 463 def RunHooksRecursively(self, options): |
425 """Evaluates all hooks, running actions as needed. run() | 464 """Evaluates all hooks, running actions as needed. run() |
426 must have been called before to load the DEPS.""" | 465 must have been called before to load the DEPS.""" |
427 assert self.hooks_ran == False | 466 assert self.hooks_ran == False |
428 if not self.should_process or self.recursion_limit() <= 0: | 467 if not self.should_process or self.recursion_limit() <= 0: |
(...skipping 879 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1308 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1347 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
1309 print >> sys.stderr, 'Error: %s' % str(e) | 1348 print >> sys.stderr, 'Error: %s' % str(e) |
1310 return 1 | 1349 return 1 |
1311 | 1350 |
1312 | 1351 |
1313 if '__main__' == __name__: | 1352 if '__main__' == __name__: |
1314 fix_encoding.fix_encoding() | 1353 fix_encoding.fix_encoding() |
1315 sys.exit(Main(sys.argv[1:])) | 1354 sys.exit(Main(sys.argv[1:])) |
1316 | 1355 |
1317 # vim: ts=2:sw=2:tw=80:et: | 1356 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |