Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(425)

Side by Side Diff: gclient.py

Issue 3109003: Improve logging and exceptions. (Closed)
Patch Set: Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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:
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698