| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 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 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 raise gclient_utils.Error('deps_file name must not be a path, just a ' | 178 raise gclient_utils.Error('deps_file name must not be a path, just a ' |
| 179 'filename. %s' % self.deps_file) | 179 'filename. %s' % self.deps_file) |
| 180 | 180 |
| 181 def LateOverride(self, url): | 181 def LateOverride(self, url): |
| 182 """Resolves the parsed url from url. | 182 """Resolves the parsed url from url. |
| 183 | 183 |
| 184 Manages From() keyword accordingly. Do not touch self.parsed_url nor | 184 Manages From() keyword accordingly. Do not touch self.parsed_url nor |
| 185 self.url because it may called with other urls due to From().""" | 185 self.url because it may called with other urls due to From().""" |
| 186 overriden_url = self.get_custom_deps(self.name, url) | 186 overriden_url = self.get_custom_deps(self.name, url) |
| 187 if overriden_url != url: | 187 if overriden_url != url: |
| 188 logging.debug('%s, %s was overriden to %s' % (self.name, url, | 188 logging.info('%s, %s was overriden to %s' % (self.name, url, |
| 189 overriden_url)) | 189 overriden_url)) |
| 190 return overriden_url | 190 return overriden_url |
| 191 elif isinstance(url, self.FromImpl): | 191 elif isinstance(url, self.FromImpl): |
| 192 ref = [dep for dep in self.tree(True) if url.module_name == dep.name] | 192 ref = [dep for dep in self.tree(True) if url.module_name == dep.name] |
| 193 if not ref: | 193 if not ref: |
| 194 raise gclient_utils.Error('Failed to find one reference to %s. %s' % ( | 194 raise gclient_utils.Error('Failed to find one reference to %s. %s' % ( |
| 195 url.module_name, ref)) | 195 url.module_name, ref)) |
| 196 # It may happen that len(ref) > 1 but it's no big deal. | 196 # It may happen that len(ref) > 1 but it's no big deal. |
| 197 ref = ref[0] | 197 ref = ref[0] |
| 198 sub_target = url.sub_target_name or self.name | 198 sub_target = url.sub_target_name or self.name |
| 199 # Make sure the referenced dependency DEPS file is loaded and file the | 199 # Make sure the referenced dependency DEPS file is loaded and file the |
| 200 # inner referenced dependency. | 200 # inner referenced dependency. |
| 201 ref.ParseDepsFile(False) | 201 ref.ParseDepsFile(False) |
| 202 found_dep = None | 202 found_dep = None |
| 203 for d in ref.dependencies: | 203 for d in ref.dependencies: |
| 204 if d.name == sub_target: | 204 if d.name == sub_target: |
| 205 found_dep = d | 205 found_dep = d |
| 206 break | 206 break |
| 207 if not found_dep: | 207 if not found_dep: |
| 208 raise gclient_utils.Error( | 208 raise gclient_utils.Error( |
| 209 'Couldn\'t find %s in %s, referenced by %s' % ( | 209 'Couldn\'t find %s in %s, referenced by %s\n%s' % ( |
| 210 sub_target, ref.name, self.name)) | 210 sub_target, ref.name, self.name, str(self.root_parent()))) |
| 211 # Call LateOverride() again. | 211 # Call LateOverride() again. |
| 212 parsed_url = found_dep.LateOverride(found_dep.url) | 212 parsed_url = found_dep.LateOverride(found_dep.url) |
| 213 logging.debug('%s, %s to %s' % (self.name, url, parsed_url)) | 213 logging.info('%s, %s to %s' % (self.name, url, parsed_url)) |
| 214 return parsed_url | 214 return parsed_url |
| 215 elif isinstance(url, basestring): | 215 elif isinstance(url, basestring): |
| 216 parsed_url = urlparse.urlparse(url) | 216 parsed_url = urlparse.urlparse(url) |
| 217 if not parsed_url[0]: | 217 if not parsed_url[0]: |
| 218 # A relative url. Fetch the real base. | 218 # A relative url. Fetch the real base. |
| 219 path = parsed_url[2] | 219 path = parsed_url[2] |
| 220 if not path.startswith('/'): | 220 if not path.startswith('/'): |
| 221 raise gclient_utils.Error( | 221 raise gclient_utils.Error( |
| 222 'relative DEPS entry \'%s\' must begin with a slash' % url) | 222 'relative DEPS entry \'%s\' must begin with a slash' % url) |
| 223 # Create a scm just to query the full url. | 223 # Create a scm just to query the full url. |
| 224 parent_url = self.parent.parsed_url | 224 parent_url = self.parent.parsed_url |
| 225 if isinstance(parent_url, self.FileImpl): | 225 if isinstance(parent_url, self.FileImpl): |
| 226 parent_url = parent_url.file_location | 226 parent_url = parent_url.file_location |
| 227 scm = gclient_scm.CreateSCM(parent_url, self.root_dir(), None) | 227 scm = gclient_scm.CreateSCM(parent_url, self.root_dir(), None) |
| 228 parsed_url = scm.FullUrlForRelativeUrl(url) | 228 parsed_url = scm.FullUrlForRelativeUrl(url) |
| 229 else: | 229 else: |
| 230 parsed_url = url | 230 parsed_url = url |
| 231 logging.debug('%s, %s -> %s' % (self.name, url, parsed_url)) | 231 logging.info('%s, %s -> %s' % (self.name, url, parsed_url)) |
| 232 return parsed_url | 232 return parsed_url |
| 233 elif isinstance(url, self.FileImpl): | 233 elif isinstance(url, self.FileImpl): |
| 234 parsed_url = url | 234 parsed_url = url |
| 235 logging.debug('%s, %s -> %s (File)' % (self.name, url, parsed_url)) | 235 logging.info('%s, %s -> %s (File)' % (self.name, url, parsed_url)) |
| 236 return parsed_url | 236 return parsed_url |
| 237 elif url is None: | 237 elif url is None: |
| 238 return None | 238 return None |
| 239 else: | 239 else: |
| 240 raise gclient_utils.Error('Unkown url type') | 240 raise gclient_utils.Error('Unkown url type') |
| 241 | 241 |
| 242 def ParseDepsFile(self, direct_reference): | 242 def ParseDepsFile(self, direct_reference): |
| 243 """Parses the DEPS file for this dependency.""" | 243 """Parses the DEPS file for this dependency.""" |
| 244 if direct_reference: | 244 if direct_reference: |
| 245 # Maybe it was referenced earlier by a From() keyword but it's now | 245 # Maybe it was referenced earlier by a From() keyword but it's now |
| 246 # directly referenced. | 246 # directly referenced. |
| 247 self.direct_reference = direct_reference | 247 self.direct_reference = direct_reference |
| 248 if self.deps_parsed: | 248 if self.deps_parsed: |
| 249 logging.debug('%s was already parsed' % self.name) |
| 249 return | 250 return |
| 250 self.deps_parsed = True | 251 self.deps_parsed = True |
| 251 filepath = os.path.join(self.root_dir(), self.name, self.deps_file) | 252 filepath = os.path.join(self.root_dir(), self.name, self.deps_file) |
| 252 if not os.path.isfile(filepath): | 253 if not os.path.isfile(filepath): |
| 254 logging.info('%s: No DEPS file found at %s' % (self.name, filepath)) |
| 253 return | 255 return |
| 254 deps_content = gclient_utils.FileRead(filepath) | 256 deps_content = gclient_utils.FileRead(filepath) |
| 257 logging.debug(deps_content) |
| 255 | 258 |
| 256 # Eval the content. | 259 # Eval the content. |
| 257 # One thing is unintuitive, vars= {} must happen before Var() use. | 260 # One thing is unintuitive, vars= {} must happen before Var() use. |
| 258 local_scope = {} | 261 local_scope = {} |
| 259 var = self.VarImpl(self.custom_vars, local_scope) | 262 var = self.VarImpl(self.custom_vars, local_scope) |
| 260 global_scope = { | 263 global_scope = { |
| 261 'File': self.FileImpl, | 264 'File': self.FileImpl, |
| 262 'From': self.FromImpl, | 265 'From': self.FromImpl, |
| 263 'Var': var.Lookup, | 266 'Var': var.Lookup, |
| 264 'deps_os': {}, | 267 'deps_os': {}, |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 rel_deps = {} | 301 rel_deps = {} |
| 299 for d, url in deps.items(): | 302 for d, url in deps.items(): |
| 300 # normpath is required to allow DEPS to use .. in their | 303 # normpath is required to allow DEPS to use .. in their |
| 301 # dependency local path. | 304 # dependency local path. |
| 302 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url | 305 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url |
| 303 deps = rel_deps | 306 deps = rel_deps |
| 304 | 307 |
| 305 # Convert the deps into real Dependency. | 308 # Convert the deps into real Dependency. |
| 306 for name, url in deps.iteritems(): | 309 for name, url in deps.iteritems(): |
| 307 if name in [s.name for s in self.dependencies]: | 310 if name in [s.name for s in self.dependencies]: |
| 308 raise | 311 raise gclient_utils.Error( |
| 312 'The same name "%s" appears multiple times in the deps section' % |
| 313 name) |
| 309 self.dependencies.append(Dependency(self, name, url, None, None, None, | 314 self.dependencies.append(Dependency(self, name, url, None, None, None, |
| 310 None)) | 315 None)) |
| 311 # Sorting by name would in theory make the whole thing coherent, since | 316 # Sorting by name would in theory make the whole thing coherent, since |
| 312 # subdirectories will be sorted after the parent directory, but that doens't | 317 # subdirectories will be sorted after the parent directory, but that doens't |
| 313 # work with From() that fetch from a dependency with a name being sorted | 318 # work with From() that fetch from a dependency with a name being sorted |
| 314 # later. But if this would be removed right now, many projects wouldn't be | 319 # later. But if this would be removed right now, many projects wouldn't be |
| 315 # able to sync anymore. | 320 # able to sync anymore. |
| 316 self.dependencies.sort(key=lambda x: x.name) | 321 self.dependencies.sort(key=lambda x: x.name) |
| 317 logging.info('Loaded: %s' % str(self)) | 322 logging.debug('Loaded: %s' % str(self)) |
| 318 | 323 |
| 319 def RunCommandRecursively(self, options, revision_overrides, | 324 def RunCommandRecursively(self, options, revision_overrides, |
| 320 command, args, pm): | 325 command, args, pm): |
| 321 """Runs 'command' before parsing the DEPS in case it's a initial checkout | 326 """Runs 'command' before parsing the DEPS in case it's a initial checkout |
| 322 or a revert.""" | 327 or a revert.""" |
| 323 assert self._file_list == [] | 328 assert self._file_list == [] |
| 324 # When running runhooks, there's no need to consult the SCM. | 329 # When running runhooks, there's no need to consult the SCM. |
| 325 # All known hooks are expected to run unconditionally regardless of working | 330 # All known hooks are expected to run unconditionally regardless of working |
| 326 # copy state, so skip the SCM status check. | 331 # copy state, so skip the SCM status check. |
| 327 run_scm = command not in ('runhooks', None) | 332 run_scm = command not in ('runhooks', None) |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 403 pattern = re.compile(hook_dict['pattern']) | 408 pattern = re.compile(hook_dict['pattern']) |
| 404 matching_file_list = [f for f in file_list if pattern.search(f)] | 409 matching_file_list = [f for f in file_list if pattern.search(f)] |
| 405 if matching_file_list: | 410 if matching_file_list: |
| 406 self._RunHookAction(hook_dict, matching_file_list) | 411 self._RunHookAction(hook_dict, matching_file_list) |
| 407 if self.recursion_limit(): | 412 if self.recursion_limit(): |
| 408 for s in self.dependencies: | 413 for s in self.dependencies: |
| 409 s.RunHooksRecursively(options) | 414 s.RunHooksRecursively(options) |
| 410 | 415 |
| 411 def _RunHookAction(self, hook_dict, matching_file_list): | 416 def _RunHookAction(self, hook_dict, matching_file_list): |
| 412 """Runs the action from a single hook.""" | 417 """Runs the action from a single hook.""" |
| 418 # A single DEPS file can specify multiple hooks so this function can be |
| 419 # called multiple times on a single Dependency. |
| 420 #assert self.hooks_ran == False |
| 413 self.hooks_ran = True | 421 self.hooks_ran = True |
| 414 logging.info(hook_dict) | 422 logging.debug(hook_dict) |
| 415 logging.info(matching_file_list) | 423 logging.debug(matching_file_list) |
| 416 command = hook_dict['action'][:] | 424 command = hook_dict['action'][:] |
| 417 if command[0] == 'python': | 425 if command[0] == 'python': |
| 418 # If the hook specified "python" as the first item, the action is a | 426 # If the hook specified "python" as the first item, the action is a |
| 419 # Python script. Run it by starting a new copy of the same | 427 # Python script. Run it by starting a new copy of the same |
| 420 # interpreter. | 428 # interpreter. |
| 421 command[0] = sys.executable | 429 command[0] = sys.executable |
| 422 | 430 |
| 423 if '$matching_files' in command: | 431 if '$matching_files' in command: |
| 424 splice_index = command.index('$matching_files') | 432 splice_index = command.index('$matching_files') |
| 425 command[splice_index:splice_index + 1] = matching_file_list | 433 command[splice_index:splice_index + 1] = matching_file_list |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 return self.custom_deps.get(name, url) | 467 return self.custom_deps.get(name, url) |
| 460 | 468 |
| 461 def file_list(self): | 469 def file_list(self): |
| 462 result = self._file_list[:] | 470 result = self._file_list[:] |
| 463 for d in self.dependencies: | 471 for d in self.dependencies: |
| 464 result.extend(d.file_list()) | 472 result.extend(d.file_list()) |
| 465 return result | 473 return result |
| 466 | 474 |
| 467 def __str__(self): | 475 def __str__(self): |
| 468 out = [] | 476 out = [] |
| 469 for i in ('name', 'url', 'safesync_url', 'custom_deps', 'custom_vars', | 477 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', |
| 470 'deps_hooks', '_file_list', 'processed', | 478 'custom_vars', 'deps_hooks', '_file_list', 'processed', |
| 471 'hooks_ran'): | 479 'hooks_ran', 'deps_parsed'): |
| 472 # 'deps_file' | 480 # 'deps_file' |
| 473 if self.__dict__[i]: | 481 if self.__dict__[i]: |
| 474 out.append('%s: %s' % (i, self.__dict__[i])) | 482 out.append('%s: %s' % (i, self.__dict__[i])) |
| 475 | 483 |
| 476 for d in self.dependencies: | 484 for d in self.dependencies: |
| 477 out.extend([' ' + x for x in str(d).splitlines()]) | 485 out.extend([' ' + x for x in str(d).splitlines()]) |
| 478 out.append('') | 486 out.append('') |
| 479 return '\n'.join(out) | 487 return '\n'.join(out) |
| 480 | 488 |
| 481 def __repr__(self): | 489 def __repr__(self): |
| 482 return '%s: %s' % (self.name, self.url) | 490 return '%s: %s' % (self.name, self.url) |
| 483 | 491 |
| 484 def hierarchy(self): | 492 def hierarchy(self): |
| 485 """Returns a human-readable hierarchical reference to a Dependency.""" | 493 """Returns a human-readable hierarchical reference to a Dependency.""" |
| 486 out = '%s(%s)' % (self.name, self.url) | 494 out = '%s(%s)' % (self.name, self.url) |
| 487 i = self.parent | 495 i = self.parent |
| 488 while i and i.name: | 496 while i and i.name: |
| 489 out = '%s(%s) -> %s' % (i.name, i.url, out) | 497 out = '%s(%s) -> %s' % (i.name, i.url, out) |
| 490 i = i.parent | 498 i = i.parent |
| 491 return out | 499 return out |
| 492 | 500 |
| 501 def root_parent(self): |
| 502 """Returns the root object, normally a GClient object.""" |
| 503 d = self |
| 504 while d.parent: |
| 505 d = d.parent |
| 506 return d |
| 507 |
| 493 | 508 |
| 494 class GClient(Dependency): | 509 class GClient(Dependency): |
| 495 """Object that represent a gclient checkout. A tree of Dependency(), one per | 510 """Object that represent a gclient checkout. A tree of Dependency(), one per |
| 496 solution or DEPS entry.""" | 511 solution or DEPS entry.""" |
| 497 | 512 |
| 498 DEPS_OS_CHOICES = { | 513 DEPS_OS_CHOICES = { |
| 499 "win32": "win", | 514 "win32": "win", |
| 500 "win": "win", | 515 "win": "win", |
| 501 "cygwin": "win", | 516 "cygwin": "win", |
| 502 "darwin": "mac", | 517 "darwin": "mac", |
| (...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 769 # Print the snapshot configuration file | 784 # Print the snapshot configuration file |
| 770 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient}) | 785 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient}) |
| 771 else: | 786 else: |
| 772 entries = sorted(self.tree(False), key=lambda i: i.name) | 787 entries = sorted(self.tree(False), key=lambda i: i.name) |
| 773 for entry in entries: | 788 for entry in entries: |
| 774 entry_url = GetURLAndRev(entry.name, entry.parsed_url) | 789 entry_url = GetURLAndRev(entry.name, entry.parsed_url) |
| 775 line = '%s: %s' % (entry.name, entry_url) | 790 line = '%s: %s' % (entry.name, entry_url) |
| 776 if not entry is entries[-1]: | 791 if not entry is entries[-1]: |
| 777 line += ';' | 792 line += ';' |
| 778 print line | 793 print line |
| 779 logging.debug(str(self)) | 794 logging.info(str(self)) |
| 780 | 795 |
| 781 def ParseDepsFile(self, direct_reference): | 796 def ParseDepsFile(self, direct_reference): |
| 782 """No DEPS to parse for a .gclient file.""" | 797 """No DEPS to parse for a .gclient file.""" |
| 783 self.direct_reference = True | 798 self.direct_reference = True |
| 784 self.deps_parsed = True | 799 self.deps_parsed = True |
| 785 | 800 |
| 786 def root_dir(self): | 801 def root_dir(self): |
| 787 """Root directory of gclient checkout.""" | 802 """Root directory of gclient checkout.""" |
| 788 return self._root_dir | 803 return self._root_dir |
| 789 | 804 |
| (...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1179 return CMDhelp(parser, argv) | 1194 return CMDhelp(parser, argv) |
| 1180 except gclient_utils.Error, e: | 1195 except gclient_utils.Error, e: |
| 1181 print >> sys.stderr, 'Error: %s' % str(e) | 1196 print >> sys.stderr, 'Error: %s' % str(e) |
| 1182 return 1 | 1197 return 1 |
| 1183 | 1198 |
| 1184 | 1199 |
| 1185 if '__main__' == __name__: | 1200 if '__main__' == __name__: |
| 1186 sys.exit(Main(sys.argv[1:])) | 1201 sys.exit(Main(sys.argv[1:])) |
| 1187 | 1202 |
| 1188 # vim: ts=2:sw=2:tw=80:et: | 1203 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |