blob: 789d89b9dd78c5603f0597ab1babdb12ce406009 [file] [log] [blame]
maruel@chromium.org725f1c32011-04-01 20:24:54 +00001#!/usr/bin/env python
thakis@chromium.org4f474b62012-01-18 01:31:29 +00002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
maruel@chromium.orgba551772010-02-03 18:21:42 +00003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00005
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00006"""Meta checkout manager supporting both Subversion and GIT.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00007
8Files
9 .gclient : Current client configuration, written by 'config' command.
10 Format is a Python script defining 'solutions', a list whose
11 entries each are maps binding the strings "name" and "url"
12 to strings specifying the name and location of the client
petermayo@chromium.orge79161a2013-07-09 14:40:37 +000013 module, as well as "custom_deps" to a map similar to the deps
14 section of the DEPS file below, as well as "custom_hooks" to
15 a list similar to the hooks sections of the DEPS file below.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000016 .gclient_entries : A cache constructed by 'update' command. Format is a
17 Python script defining 'entries', a list of the names
18 of all modules in the client
19 <module>/DEPS : Python script defining var 'deps' as a map from each requisite
20 submodule name to a URL where it can be found (via one SCM)
21
22Hooks
23 .gclient and DEPS files may optionally contain a list named "hooks" to
24 allow custom actions to be performed based on files that have changed in the
evan@chromium.org67820ef2009-07-27 17:23:00 +000025 working copy as a result of a "sync"/"update" or "revert" operation. This
maruel@chromium.org0b6a0842010-06-15 14:34:19 +000026 can be prevented by using --nohooks (hooks run by default). Hooks can also
maruel@chromium.org5df6a462009-08-28 18:52:26 +000027 be forced to run with the "runhooks" operation. If "sync" is run with
petermayo@chromium.orge79161a2013-07-09 14:40:37 +000028 --force, all known but not suppressed hooks will run regardless of the state
29 of the working copy.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000030
31 Each item in a "hooks" list is a dict, containing these two keys:
32 "pattern" The associated value is a string containing a regular
33 expression. When a file whose pathname matches the expression
34 is checked out, updated, or reverted, the hook's "action" will
35 run.
36 "action" A list describing a command to run along with its arguments, if
37 any. An action command will run at most one time per gclient
38 invocation, regardless of how many files matched the pattern.
39 The action is executed in the same directory as the .gclient
40 file. If the first item in the list is the string "python",
41 the current Python interpreter (sys.executable) will be used
phajdan.jr@chromium.org71b40682009-07-31 23:40:09 +000042 to run the command. If the list contains string "$matching_files"
43 it will be removed from the list and the list will be extended
44 by the list of matching files.
petermayo@chromium.orge79161a2013-07-09 14:40:37 +000045 "name" An optional string specifying the group to which a hook belongs
46 for overriding and organizing.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000047
48 Example:
49 hooks = [
50 { "pattern": "\\.(gif|jpe?g|pr0n|png)$",
51 "action": ["python", "image_indexer.py", "--all"]},
petermayo@chromium.orge79161a2013-07-09 14:40:37 +000052 { "pattern": ".",
53 "name": "gyp",
54 "action": ["python", "src/build/gyp_chromium"]},
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000055 ]
peter@chromium.org1efccc82012-04-27 16:34:38 +000056
57Specifying a target OS
58 An optional key named "target_os" may be added to a gclient file to specify
59 one or more additional operating systems that should be considered when
60 processing the deps_os dict of a DEPS file.
61
62 Example:
63 target_os = [ "android" ]
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +000064
65 If the "target_os_only" key is also present and true, then *only* the
66 operating systems listed in "target_os" will be used.
67
68 Example:
69 target_os = [ "ios" ]
70 target_os_only = True
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000071"""
72
maruel@chromium.org82798cb2012-02-23 18:16:12 +000073__version__ = "0.6.4"
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000074
maruel@chromium.org9e5317a2010-08-13 20:35:11 +000075import copy
maruel@chromium.org754960e2009-09-21 12:31:05 +000076import logging
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000077import optparse
78import os
bradnelson@google.com4949dab2012-04-19 16:41:07 +000079import platform
maruel@chromium.org621939b2010-08-10 20:12:00 +000080import posixpath
msb@chromium.org2e38de72009-09-28 17:04:47 +000081import pprint
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000082import re
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000083import sys
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +000084import time
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000085import urllib
bradnelson@google.com4949dab2012-04-19 16:41:07 +000086import urlparse
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000087
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +000088import breakpad # pylint: disable=W0611
maruel@chromium.orgada4c652009-12-03 15:32:01 +000089
maruel@chromium.org35625c72011-03-23 17:34:02 +000090import fix_encoding
maruel@chromium.org5f3eee32009-09-17 00:34:30 +000091import gclient_scm
92import gclient_utils
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +000093from third_party.repo.progress import Progress
maruel@chromium.org31cb48a2011-04-04 18:01:36 +000094import subprocess2
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +000095from third_party import colorama
96# Import shortcut.
97from third_party.colorama import Fore
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000098
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000099
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000100def attr(attribute, data):
maruel@chromium.org1f7d1182010-05-17 18:17:38 +0000101 """Sets an attribute on a function."""
102 def hook(fn):
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000103 setattr(fn, attribute, data)
maruel@chromium.org1f7d1182010-05-17 18:17:38 +0000104 return fn
105 return hook
maruel@chromium.orge3da35f2010-03-09 21:40:45 +0000106
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000107
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000108## GClient implementation.
109
110
maruel@chromium.org116704f2010-06-11 17:34:38 +0000111class GClientKeywords(object):
112 class FromImpl(object):
113 """Used to implement the From() syntax."""
114
115 def __init__(self, module_name, sub_target_name=None):
116 """module_name is the dep module we want to include from. It can also be
117 the name of a subdirectory to include from.
118
119 sub_target_name is an optional parameter if the module name in the other
120 DEPS file is different. E.g., you might want to map src/net to net."""
121 self.module_name = module_name
122 self.sub_target_name = sub_target_name
123
124 def __str__(self):
125 return 'From(%s, %s)' % (repr(self.module_name),
126 repr(self.sub_target_name))
127
maruel@chromium.org116704f2010-06-11 17:34:38 +0000128 class FileImpl(object):
129 """Used to implement the File('') syntax which lets you sync a single file
maruel@chromium.orge3216c62010-07-08 03:31:43 +0000130 from a SVN repo."""
maruel@chromium.org116704f2010-06-11 17:34:38 +0000131
132 def __init__(self, file_location):
133 self.file_location = file_location
134
135 def __str__(self):
136 return 'File("%s")' % self.file_location
137
138 def GetPath(self):
139 return os.path.split(self.file_location)[0]
140
141 def GetFilename(self):
142 rev_tokens = self.file_location.split('@')
143 return os.path.split(rev_tokens[0])[1]
144
145 def GetRevision(self):
146 rev_tokens = self.file_location.split('@')
147 if len(rev_tokens) > 1:
148 return rev_tokens[1]
149 return None
150
151 class VarImpl(object):
152 def __init__(self, custom_vars, local_scope):
153 self._custom_vars = custom_vars
154 self._local_scope = local_scope
155
156 def Lookup(self, var_name):
157 """Implements the Var syntax."""
158 if var_name in self._custom_vars:
159 return self._custom_vars[var_name]
160 elif var_name in self._local_scope.get("vars", {}):
161 return self._local_scope["vars"][var_name]
162 raise gclient_utils.Error("Var is not defined: %s" % var_name)
163
164
maruel@chromium.org064186c2011-09-27 23:53:33 +0000165class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000166 """Immutable configuration settings."""
167 def __init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000168 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000169 custom_hooks, deps_file, should_process):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000170 GClientKeywords.__init__(self)
171
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000172 # These are not mutable:
173 self._parent = parent
174 self._safesync_url = safesync_url
175 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000176 self._url = url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000177 # 'managed' determines whether or not this dependency is synced/updated by
178 # gclient after gclient checks it out initially. The difference between
179 # 'managed' and 'should_process' is that the user specifies 'managed' via
180 # the --unmanaged command-line flag or a .gclient config, where
181 # 'should_process' is dynamically set by gclient if it goes over its
182 # recursion limit and controls gclient's behavior so it does not misbehave.
183 self._managed = managed
184 self._should_process = should_process
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000185 # This is a mutable value that overrides the normal recursion limit for this
186 # dependency. It is read from the actual DEPS file so cannot be set on
187 # class instantiation.
188 self.recursion_override = None
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000189 # This is a mutable value which has the list of 'target_os' OSes listed in
190 # the current deps file.
191 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000192
193 # These are only set in .gclient and not in DEPS files.
194 self._custom_vars = custom_vars or {}
195 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000196 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000197
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000198 # TODO(iannucci): Remove this when all masters are correctly substituting
199 # the new blink url.
200 if (self._custom_vars.get('webkit_trunk', '') ==
201 'svn://svn-mirror.golo.chromium.org/webkit-readonly/trunk'):
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000202 new_url = 'svn://svn-mirror.golo.chromium.org/blink/trunk'
203 print 'Overwriting Var("webkit_trunk") with %s' % new_url
204 self._custom_vars['webkit_trunk'] = new_url
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000205
maruel@chromium.org064186c2011-09-27 23:53:33 +0000206 # Post process the url to remove trailing slashes.
207 if isinstance(self._url, basestring):
208 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
209 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000210 self._url = self._url.replace('/@', '@')
211 elif not isinstance(self._url,
212 (self.FromImpl, self.FileImpl, None.__class__)):
213 raise gclient_utils.Error(
214 ('dependency url must be either a string, None, '
215 'File() or From() instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000216 # Make any deps_file path platform-appropriate.
217 for sep in ['/', '\\']:
218 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000219
220 @property
221 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000222 return self._deps_file
223
224 @property
225 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000226 return self._managed
227
228 @property
229 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000230 return self._parent
231
232 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000233 def root(self):
234 """Returns the root node, a GClient object."""
235 if not self.parent:
236 # This line is to signal pylint that it could be a GClient instance.
237 return self or GClient(None, None)
238 return self.parent.root
239
240 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000241 def safesync_url(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000242 return self._safesync_url
243
244 @property
245 def should_process(self):
246 """True if this dependency should be processed, i.e. checked out."""
247 return self._should_process
248
249 @property
250 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000251 return self._custom_vars.copy()
252
253 @property
254 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000255 return self._custom_deps.copy()
256
maruel@chromium.org064186c2011-09-27 23:53:33 +0000257 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000258 def custom_hooks(self):
259 return self._custom_hooks[:]
260
261 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000262 def url(self):
263 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000264
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000265 @property
266 def recursion_limit(self):
267 """Returns > 0 if this dependency is not too recursed to be processed."""
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000268 if self.recursion_override is not None:
269 return self.recursion_override
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000270 return max(self.parent.recursion_limit - 1, 0)
271
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000272 @property
273 def target_os(self):
274 if self.local_target_os is not None:
275 return tuple(set(self.local_target_os).union(self.parent.target_os))
276 else:
277 return self.parent.target_os
278
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000279 def get_custom_deps(self, name, url):
280 """Returns a custom deps if applicable."""
281 if self.parent:
282 url = self.parent.get_custom_deps(name, url)
283 # None is a valid return value to disable a dependency.
284 return self.custom_deps.get(name, url)
285
maruel@chromium.org064186c2011-09-27 23:53:33 +0000286
287class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000288 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000289
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000290 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000291 custom_vars, custom_hooks, deps_file, should_process):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000292 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000293 DependencySettings.__init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000294 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000295 custom_hooks, deps_file, should_process)
maruel@chromium.org68988972011-09-20 14:11:42 +0000296
297 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000298 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000299
300 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000301 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000302 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000303 # A cache of the files affected by the current operation, necessary for
304 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000305 self._file_list = []
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000306 # If it is not set to True, the dependency wasn't processed for its child
307 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000308 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000309 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000310 self._processed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000311 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000312 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000313 # This is the scm used to checkout self.url. It may be used by dependencies
314 # to get the datetime of the revision we checked out.
315 self._used_scm = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000316
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000317 if not self.name and self.parent:
318 raise gclient_utils.Error('Dependency without name')
319
maruel@chromium.org470b5432011-10-11 18:18:19 +0000320 @property
321 def requirements(self):
322 """Calculate the list of requirements."""
323 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000324 # self.parent is implicitly a requirement. This will be recursive by
325 # definition.
326 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000327 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000328
329 # For a tree with at least 2 levels*, the leaf node needs to depend
330 # on the level higher up in an orderly way.
331 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
332 # thus unsorted, while the .gclient format is a list thus sorted.
333 #
334 # * _recursion_limit is hard coded 2 and there is no hope to change this
335 # value.
336 #
337 # Interestingly enough, the following condition only works in the case we
338 # want: self is a 2nd level node. 3nd level node wouldn't need this since
339 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000340 if self.parent and self.parent.parent and not self.parent.parent.parent:
341 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000342
343 if isinstance(self.url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000344 requirements.add(self.url.module_name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000345
maruel@chromium.org470b5432011-10-11 18:18:19 +0000346 if self.name:
347 requirements |= set(
348 obj.name for obj in self.root.subtree(False)
349 if (obj is not self
350 and obj.name and
351 self.name.startswith(posixpath.join(obj.name, ''))))
352 requirements = tuple(sorted(requirements))
353 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
354 return requirements
355
356 def verify_validity(self):
357 """Verifies that this Dependency is fine to add as a child of another one.
358
359 Returns True if this entry should be added, False if it is a duplicate of
360 another entry.
361 """
362 logging.info('Dependency(%s).verify_validity()' % self.name)
363 if self.name in [s.name for s in self.parent.dependencies]:
364 raise gclient_utils.Error(
365 'The same name "%s" appears multiple times in the deps section' %
366 self.name)
367 if not self.should_process:
368 # Return early, no need to set requirements.
369 return True
370
371 # This require a full tree traversal with locks.
372 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
373 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000374 self_url = self.LateOverride(self.url)
375 sibling_url = sibling.LateOverride(sibling.url)
376 # Allow to have only one to be None or ''.
377 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000378 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000379 ('Dependency %s specified more than once:\n'
380 ' %s [%s]\n'
381 'vs\n'
382 ' %s [%s]') % (
383 self.name,
384 sibling.hierarchy(),
385 sibling_url,
386 self.hierarchy(),
387 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000388 # In theory we could keep it as a shadow of the other one. In
389 # practice, simply ignore it.
390 logging.warn('Won\'t process duplicate dependency %s' % sibling)
391 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000392 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000393
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000394 def LateOverride(self, url):
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000395 """Resolves the parsed url from url.
396
397 Manages From() keyword accordingly. Do not touch self.parsed_url nor
398 self.url because it may called with other urls due to From()."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000399 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000400 parsed_url = self.get_custom_deps(self.name, url)
401 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000402 logging.info(
403 'Dependency(%s).LateOverride(%s) -> %s' %
404 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000405 return parsed_url
406
407 if isinstance(url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000408 # Requires tree traversal.
maruel@chromium.org68988972011-09-20 14:11:42 +0000409 ref = [
410 dep for dep in self.root.subtree(True) if url.module_name == dep.name
411 ]
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000412 if not ref:
413 raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
414 url.module_name, ref))
415 # It may happen that len(ref) > 1 but it's no big deal.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000416 ref = ref[0]
maruel@chromium.org98d05fa2010-07-22 21:58:01 +0000417 sub_target = url.sub_target_name or self.name
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000418 found_deps = [d for d in ref.dependencies if d.name == sub_target]
419 if len(found_deps) != 1:
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000420 raise gclient_utils.Error(
maruel@chromium.org98023df2011-09-07 18:44:47 +0000421 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
422 sub_target, ref.name, self.name, self.parent.name,
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000423 str(self.root)))
maruel@chromium.org98023df2011-09-07 18:44:47 +0000424
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000425 # Call LateOverride() again.
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000426 found_dep = found_deps[0]
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000427 parsed_url = found_dep.LateOverride(found_dep.url)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000428 logging.info(
maruel@chromium.org470b5432011-10-11 18:18:19 +0000429 'Dependency(%s).LateOverride(%s) -> %s (From)' %
430 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000431 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000432
433 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000434 parsed_url = urlparse.urlparse(url)
435 if not parsed_url[0]:
436 # A relative url. Fetch the real base.
437 path = parsed_url[2]
438 if not path.startswith('/'):
439 raise gclient_utils.Error(
440 'relative DEPS entry \'%s\' must begin with a slash' % url)
441 # Create a scm just to query the full url.
442 parent_url = self.parent.parsed_url
443 if isinstance(parent_url, self.FileImpl):
444 parent_url = parent_url.file_location
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000445 scm = gclient_scm.CreateSCM(parent_url, self.root.root_dir, None)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000446 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000447 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000448 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000449 logging.info(
450 'Dependency(%s).LateOverride(%s) -> %s' %
451 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000452 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000453
454 if isinstance(url, self.FileImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000455 logging.info(
456 'Dependency(%s).LateOverride(%s) -> %s (File)' %
457 (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000458 return url
459
460 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000461 logging.info(
462 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000463 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000464
465 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000466
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000467 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000468 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000469 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000470 assert not self.dependencies
471 # One thing is unintuitive, vars = {} must happen before Var() use.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000472 local_scope = {}
473 var = self.VarImpl(self.custom_vars, local_scope)
474 global_scope = {
475 'File': self.FileImpl,
476 'From': self.FromImpl,
477 'Var': var.Lookup,
478 'deps_os': {},
479 }
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000480 filepath = os.path.join(self.root.root_dir, self.name, self.deps_file)
maruel@chromium.org46304292010-10-28 11:42:00 +0000481 if not os.path.isfile(filepath):
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000482 logging.info(
483 'ParseDepsFile(%s): No %s file found at %s' % (
484 self.name, self.deps_file, filepath))
maruel@chromium.org46304292010-10-28 11:42:00 +0000485 else:
486 deps_content = gclient_utils.FileRead(filepath)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000487 logging.debug('ParseDepsFile(%s) read:\n%s' % (self.name, deps_content))
maruel@chromium.org46304292010-10-28 11:42:00 +0000488 # Eval the content.
489 try:
490 exec(deps_content, global_scope, local_scope)
491 except SyntaxError, e:
492 gclient_utils.SyntaxErrorToError(filepath, e)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000493 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000494 if 'recursion' in local_scope:
495 self.recursion_override = local_scope.get('recursion')
496 logging.warning(
497 'Setting %s recursion to %d.', self.name, self.recursion_limit)
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000498 # If present, save 'target_os' in the local_target_os property.
499 if 'target_os' in local_scope:
500 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000501 # load os specific dependencies if defined. these dependencies may
502 # override or extend the values defined by the 'deps' member.
sivachandra@chromium.orga0ad8ad2012-11-06 19:41:28 +0000503 target_os_deps = {}
maruel@chromium.org271375b2010-06-23 19:17:38 +0000504 if 'deps_os' in local_scope:
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000505 for deps_os_key in self.target_os:
maruel@chromium.org271375b2010-06-23 19:17:38 +0000506 os_deps = local_scope['deps_os'].get(deps_os_key, {})
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000507 if len(self.target_os) > 1:
maruel@chromium.org271375b2010-06-23 19:17:38 +0000508 # Ignore any conflict when including deps for more than one
maruel@chromium.org46304292010-10-28 11:42:00 +0000509 # platform, so we collect the broadest set of dependencies
510 # available. We may end up with the wrong revision of something for
511 # our platform, but this is the best we can do.
sivachandra@chromium.orga0ad8ad2012-11-06 19:41:28 +0000512 target_os_deps.update(
513 [x for x in os_deps.items() if not x[0] in target_os_deps])
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000514 else:
sivachandra@chromium.orga0ad8ad2012-11-06 19:41:28 +0000515 target_os_deps.update(os_deps)
516
517 # deps_os overrides paths from deps
518 deps.update(target_os_deps)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000519
maruel@chromium.org271375b2010-06-23 19:17:38 +0000520 # If a line is in custom_deps, but not in the solution, we want to append
521 # this line to the solution.
522 for d in self.custom_deps:
523 if d not in deps:
524 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000525
526 # If use_relative_paths is set in the DEPS file, regenerate
527 # the dictionary using paths relative to the directory containing
528 # the DEPS file.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000529 use_relative_paths = local_scope.get('use_relative_paths', False)
530 if use_relative_paths:
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000531 rel_deps = {}
532 for d, url in deps.items():
533 # normpath is required to allow DEPS to use .. in their
534 # dependency local path.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000535 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url
536 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000537
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000538 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000539 deps_to_add = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000540 for name, url in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000541 should_process = self.recursion_limit and self.should_process
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000542 deps_to_add.append(Dependency(
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000543 self, name, url, None, None, None, None, None,
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000544 self.deps_file, should_process))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000545 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000546
547 # override named sets of hooks by the custom hooks
548 hooks_to_run = []
549 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
550 for hook in local_scope.get('hooks', []):
551 if hook.get('name', '') not in hook_names_to_suppress:
552 hooks_to_run.append(hook)
553
554 # add the replacements and any additions
555 for hook in self.custom_hooks:
556 if 'action' in hook:
557 hooks_to_run.append(hook)
558
559 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000560 logging.info('ParseDepsFile(%s) done' % self.name)
561
562 def add_dependencies_and_close(self, deps_to_add, hooks):
563 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000564 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000565 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000566 self.add_dependency(dep)
567 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000568
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000569 def maybeGetParentRevision(
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000570 self, command, options, parsed_url, parent_name, revision_overrides):
571 """Uses revision/timestamp of parent if no explicit revision was specified.
572
573 If we are performing an update and --transitive is set, use
574 - the parent's revision if 'self.url' is in the same repository
575 - the parent's timestamp otherwise
576 to update 'self.url'. The used revision/timestamp will be set in
577 'options.revision'.
578 If we have an explicit revision do nothing.
579 """
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000580 if command == 'update' and options.transitive and not options.revision:
581 _, revision = gclient_utils.SplitUrlRevision(parsed_url)
582 if not revision:
583 options.revision = revision_overrides.get(parent_name)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000584 if (options.revision and
585 not gclient_utils.IsDateRevision(options.revision)):
586 assert self.parent and self.parent.used_scm
587 # If this dependency is in the same repository as parent it's url will
588 # start with a slash. If so we take the parent revision instead of
589 # it's timestamp.
590 # (The timestamps of commits in google code are broken -- which can
591 # result in dependencies to be checked out at the wrong revision)
592 if self.url.startswith('/'):
593 if options.verbose:
594 print('Using parent\'s revision %s since we are in the same '
595 'repository.' % options.revision)
596 else:
597 parent_revision_date = self.parent.used_scm.GetRevisionDate(
598 options.revision)
599 options.revision = gclient_utils.MakeDateRevision(
600 parent_revision_date)
601 if options.verbose:
602 print('Using parent\'s revision date %s since we are in a '
603 'different repository.' % options.revision)
604 revision_overrides[self.name] = options.revision
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000605
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000606 # Arguments number differs from overridden method
607 # pylint: disable=W0221
maruel@chromium.org3742c842010-09-09 19:27:14 +0000608 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000609 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000610 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000611 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000612 if not self.should_process:
613 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000614 # When running runhooks, there's no need to consult the SCM.
615 # All known hooks are expected to run unconditionally regardless of working
616 # copy state, so skip the SCM status check.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000617 run_scm = command not in ('runhooks', 'recurse', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000618 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000619 file_list = [] if not options.nohooks else None
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000620 if run_scm and parsed_url:
621 if isinstance(parsed_url, self.FileImpl):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000622 # Special support for single-file checkout.
623 if not command in (None, 'cleanup', 'diff', 'pack', 'status'):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000624 # Sadly, pylint doesn't realize that parsed_url is of FileImpl.
625 # pylint: disable=E1103
626 options.revision = parsed_url.GetRevision()
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000627 self._used_scm = gclient_scm.SVNWrapper(
628 parsed_url.GetPath(), self.root.root_dir, self.name)
629 self._used_scm.RunCommand('updatesingle',
630 options, args + [parsed_url.GetFilename()], file_list)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000631 else:
maruel@chromium.org9e5317a2010-08-13 20:35:11 +0000632 # Create a shallow copy to mutate revision.
633 options = copy.copy(options)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000634 options.revision = revision_overrides.get(self.name)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000635 self.maybeGetParentRevision(
636 command, options, parsed_url, self.parent.name, revision_overrides)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000637 self._used_scm = gclient_scm.CreateSCM(
638 parsed_url, self.root.root_dir, self.name)
639 self._used_scm.RunCommand(command, options, args, file_list)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000640 if file_list:
641 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000642
643 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
644 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000645 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000646 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000647 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000648 continue
649 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000650 [self.root.root_dir.lower(), file_list[i].lower()])
651 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000652 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000653 while file_list[i].startswith(('\\', '/')):
654 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000655
656 # Always parse the DEPS file.
657 self.ParseDepsFile()
658
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000659 self._run_is_done(file_list or [], parsed_url)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000660
661 if self.recursion_limit:
662 # Parse the dependencies of this dependency.
663 for s in self.dependencies:
664 work_queue.enqueue(s)
665
666 if command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000667 if not isinstance(parsed_url, self.FileImpl):
668 # Skip file only checkout.
669 scm = gclient_scm.GetScmName(parsed_url)
670 if not options.scm or scm in options.scm:
671 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
672 # Pass in the SCM type as an env variable
673 env = os.environ.copy()
674 if scm:
675 env['GCLIENT_SCM'] = scm
676 if parsed_url:
677 env['GCLIENT_URL'] = parsed_url
ilevy@chromium.org37116242012-11-28 01:32:48 +0000678 env['GCLIENT_DEP_PATH'] = self.name
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000679 if options.prepend_dir and scm == 'git':
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000680 print_stdout = False
681 def filter_fn(line):
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000682 """Git-specific path marshaling. It is optimized for git-grep."""
683
684 def mod_path(git_pathspec):
685 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
686 modified_path = os.path.join(self.name, match.group(2))
687 branch = match.group(1) or ''
688 return '%s%s' % (branch, modified_path)
689
690 match = re.match('^Binary file ([^\0]+) matches$', line)
691 if match:
692 print 'Binary file %s matches' % mod_path(match.group(1))
693 return
694
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000695 items = line.split('\0')
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000696 if len(items) == 2 and items[1]:
697 print '%s : %s' % (mod_path(items[0]), items[1])
698 elif len(items) >= 2:
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000699 # Multiple null bytes or a single trailing null byte indicate
700 # git is likely displaying filenames only (such as with -l)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000701 print '\n'.join(mod_path(path) for path in items if path)
702 else:
703 print line
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000704 else:
705 print_stdout = True
706 filter_fn = None
707
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000708 if parsed_url is None:
709 print >> sys.stderr, 'Skipped omitted dependency %s' % cwd
710 elif os.path.isdir(cwd):
maruel@chromium.org288054d2012-03-05 00:43:07 +0000711 try:
712 gclient_utils.CheckCallAndFilter(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000713 args, cwd=cwd, env=env, print_stdout=print_stdout,
714 filter_fn=filter_fn,
715 )
maruel@chromium.org288054d2012-03-05 00:43:07 +0000716 except subprocess2.CalledProcessError:
717 if not options.ignore:
718 raise
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000719 else:
720 print >> sys.stderr, 'Skipped missing %s' % cwd
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000721
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000722
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000723 @gclient_utils.lockedmethod
724 def _run_is_done(self, file_list, parsed_url):
725 # Both these are kept for hooks that are run as a separate tree traversal.
726 self._file_list = file_list
727 self._parsed_url = parsed_url
728 self._processed = True
729
szager@google.comb9a78d32012-03-13 18:46:21 +0000730 @staticmethod
731 def GetHookAction(hook_dict, matching_file_list):
732 """Turns a parsed 'hook' dict into an executable command."""
733 logging.debug(hook_dict)
734 logging.debug(matching_file_list)
735 command = hook_dict['action'][:]
736 if command[0] == 'python':
737 # If the hook specified "python" as the first item, the action is a
738 # Python script. Run it by starting a new copy of the same
739 # interpreter.
740 command[0] = sys.executable
741 if '$matching_files' in command:
742 splice_index = command.index('$matching_files')
743 command[splice_index:splice_index + 1] = matching_file_list
744 return command
745
746 def GetHooks(self, options):
747 """Evaluates all hooks, and return them in a flat list.
748
749 RunOnDeps() must have been called before to load the DEPS.
750 """
751 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000752 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000753 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000754 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000755 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000756 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000757 if self.deps_hooks:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000758 # TODO(maruel): If the user is using git or git-svn, then we don't know
759 # what files have changed so we always run all hooks. It'd be nice to fix
760 # that.
761 if (options.force or
762 isinstance(self.parsed_url, self.FileImpl) or
763 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000764 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000765 for hook_dict in self.deps_hooks:
szager@google.comb9a78d32012-03-13 18:46:21 +0000766 result.append(self.GetHookAction(hook_dict, []))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000767 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000768 # Run hooks on the basis of whether the files from the gclient operation
769 # match each hook's pattern.
770 for hook_dict in self.deps_hooks:
771 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000772 matching_file_list = [
773 f for f in self.file_list_and_children if pattern.search(f)
774 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000775 if matching_file_list:
szager@google.comb9a78d32012-03-13 18:46:21 +0000776 result.append(self.GetHookAction(hook_dict, matching_file_list))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000777 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000778 result.extend(s.GetHooks(options))
779 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000780
szager@google.comb9a78d32012-03-13 18:46:21 +0000781 def RunHooksRecursively(self, options):
782 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000783 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000784 for hook in self.GetHooks(options):
785 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000786 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +0000787 gclient_utils.CheckCallAndFilterAndHeader(
788 hook, cwd=self.root.root_dir, always=True)
789 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
790 # Use a discrete exit status code of 2 to indicate that a hook action
791 # failed. Users of this script may wish to treat hook action failures
792 # differently from VC failures.
793 print >> sys.stderr, 'Error: %s' % str(e)
794 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000795 finally:
796 elapsed_time = time.time() - start_time
797 if elapsed_time > 10:
798 print "Hook '%s' took %.2f secs" % (
799 gclient_utils.CommandToStr(hook), elapsed_time)
maruel@chromium.orgeaf61062010-07-07 18:42:39 +0000800
maruel@chromium.org0d812442010-08-10 12:41:08 +0000801 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000802 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000803 dependencies = self.dependencies
804 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000805 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000806 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000807 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000808 for i in d.subtree(include_all):
809 yield i
810
811 def depth_first_tree(self):
812 """Depth-first recursion including the root node."""
813 yield self
814 for i in self.dependencies:
815 for j in i.depth_first_tree():
816 if j.should_process:
817 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +0000818
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000819 @gclient_utils.lockedmethod
820 def add_dependency(self, new_dep):
821 self._dependencies.append(new_dep)
822
823 @gclient_utils.lockedmethod
824 def _mark_as_parsed(self, new_hooks):
825 self._deps_hooks.extend(new_hooks)
826 self._deps_parsed = True
827
maruel@chromium.org68988972011-09-20 14:11:42 +0000828 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000829 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000830 def dependencies(self):
831 return tuple(self._dependencies)
832
833 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000834 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000835 def deps_hooks(self):
836 return tuple(self._deps_hooks)
837
838 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000839 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000840 def parsed_url(self):
841 return self._parsed_url
842
843 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000844 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000845 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000846 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +0000847 return self._deps_parsed
848
849 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000850 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000851 def processed(self):
852 return self._processed
853
854 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000855 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000856 def hooks_ran(self):
857 return self._hooks_ran
858
859 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000860 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000861 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000862 return tuple(self._file_list)
863
864 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000865 def used_scm(self):
866 """SCMWrapper instance for this dependency or None if not processed yet."""
867 return self._used_scm
868
869 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000870 def file_list_and_children(self):
871 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000872 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000873 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +0000874 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000875
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000876 def __str__(self):
877 out = []
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +0000878 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +0000879 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000880 'processed', 'hooks_ran', 'deps_parsed', 'requirements'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +0000881 # First try the native property if it exists.
882 if hasattr(self, '_' + i):
883 value = getattr(self, '_' + i, False)
884 else:
885 value = getattr(self, i, False)
886 if value:
887 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000888
889 for d in self.dependencies:
890 out.extend([' ' + x for x in str(d).splitlines()])
891 out.append('')
892 return '\n'.join(out)
893
894 def __repr__(self):
895 return '%s: %s' % (self.name, self.url)
896
maruel@chromium.orgbffb9042010-07-22 20:59:36 +0000897 def hierarchy(self):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +0000898 """Returns a human-readable hierarchical reference to a Dependency."""
maruel@chromium.orgbffb9042010-07-22 20:59:36 +0000899 out = '%s(%s)' % (self.name, self.url)
900 i = self.parent
901 while i and i.name:
902 out = '%s(%s) -> %s' % (i.name, i.url, out)
903 i = i.parent
904 return out
905
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000906
907class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000908 """Object that represent a gclient checkout. A tree of Dependency(), one per
909 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000910
911 DEPS_OS_CHOICES = {
912 "win32": "win",
913 "win": "win",
914 "cygwin": "win",
915 "darwin": "mac",
916 "mac": "mac",
917 "unix": "unix",
918 "linux": "unix",
919 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +0000920 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +0000921 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000922 }
923
924 DEFAULT_CLIENT_FILE_TEXT = ("""\
925solutions = [
926 { "name" : "%(solution_name)s",
927 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +0000928 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000929 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000930 "custom_deps" : {
931 },
maruel@chromium.org73e21142010-07-05 13:32:01 +0000932 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000933 },
934]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +0000935cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000936""")
937
938 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
939 { "name" : "%(solution_name)s",
940 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +0000941 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000942 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000943 "custom_deps" : {
maruel@chromium.org73e21142010-07-05 13:32:01 +0000944%(solution_deps)s },
945 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000946 },
947""")
948
949 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
950# Snapshot generated with gclient revinfo --snapshot
951solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +0000952%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000953""")
954
955 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +0000956 # Do not change previous behavior. Only solution level and immediate DEPS
957 # are processed.
958 self._recursion_limit = 2
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000959 Dependency.__init__(self, None, None, None, None, True, None, None, None,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000960 'unused', True)
maruel@chromium.org0d425922010-06-21 19:22:24 +0000961 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +0000962 if options.deps_os:
963 enforced_os = options.deps_os.split(',')
964 else:
965 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
966 if 'all' in enforced_os:
967 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000968 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +0000969 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000970 self.config_content = None
971
972 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000973 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000974 config_dict = {}
975 self.config_content = content
976 try:
977 exec(content, config_dict)
978 except SyntaxError, e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +0000979 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000980
peter@chromium.org1efccc82012-04-27 16:34:38 +0000981 # Append any target OS that is not already being enforced to the tuple.
982 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +0000983 if config_dict.get('target_os_only', False):
984 self._enforced_os = tuple(set(target_os))
985 else:
986 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
987
iannucci@chromium.org53456aa2013-07-03 19:38:34 +0000988 gclient_scm.GitWrapper.cache_dir = config_dict.get('cache_dir')
989
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +0000990 if not target_os and config_dict.get('target_os_only', False):
991 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
992 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +0000993
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000994 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000995 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +0000996 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000997 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +0000998 self, s['name'], s['url'],
999 s.get('safesync_url', None),
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001000 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001001 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001002 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001003 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001004 s.get('deps_file', 'DEPS'),
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001005 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001006 except KeyError:
1007 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1008 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001009 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1010 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001011
1012 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001013 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001014 self._options.config_filename),
1015 self.config_content)
1016
1017 @staticmethod
1018 def LoadCurrentConfig(options):
1019 """Searches for and loads a .gclient file relative to the current working
1020 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001021 if options.spec:
1022 client = GClient('.', options)
1023 client.SetConfig(options.spec)
1024 else:
1025 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1026 if not path:
1027 return None
1028 client = GClient(path, options)
1029 client.SetConfig(gclient_utils.FileRead(
1030 os.path.join(path, options.config_filename)))
maruel@chromium.org69392e72011-10-13 22:09:00 +00001031
1032 if (options.revisions and
1033 len(client.dependencies) > 1 and
1034 any('@' not in r for r in options.revisions)):
1035 print >> sys.stderr, (
1036 'You must specify the full solution name like --revision %s@%s\n'
1037 'when you have multiple solutions setup in your .gclient file.\n'
1038 'Other solutions present are: %s.') % (
1039 client.dependencies[0].name,
1040 options.revisions[0],
1041 ', '.join(s.name for s in client.dependencies[1:]))
maruel@chromium.org15804092010-09-02 17:07:37 +00001042 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001043
nsylvain@google.comefc80932011-05-31 21:27:56 +00001044 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001045 safesync_url, managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001046 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1047 'solution_name': solution_name,
1048 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001049 'deps_file': deps_file,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001050 'safesync_url' : safesync_url,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001051 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001052 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001053 })
1054
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001055 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001056 """Creates a .gclient_entries file to record the list of unique checkouts.
1057
1058 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001059 """
1060 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1061 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001062 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001063 for entry in self.root.subtree(False):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001064 # Skip over File() dependencies as we can't version them.
1065 if not isinstance(entry.parsed_url, self.FileImpl):
1066 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1067 pprint.pformat(entry.parsed_url))
1068 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001069 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001070 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001071 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001072
1073 def _ReadEntries(self):
1074 """Read the .gclient_entries file for the given client.
1075
1076 Returns:
1077 A sequence of solution names, which will be empty if there is the
1078 entries file hasn't been created yet.
1079 """
1080 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001081 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001082 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001083 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001084 try:
1085 exec(gclient_utils.FileRead(filename), scope)
1086 except SyntaxError, e:
1087 gclient_utils.SyntaxErrorToError(filename, e)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001088 return scope['entries']
1089
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001090 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001091 """Checks for revision overrides."""
1092 revision_overrides = {}
maruel@chromium.org307d1792010-05-31 20:03:13 +00001093 if self._options.head:
1094 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001095 # Do not check safesync_url if one or more --revision flag is specified.
1096 if not self._options.revisions:
1097 for s in self.dependencies:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001098 if not s.managed:
1099 self._options.revisions.append('%s@unmanaged' % s.name)
1100 elif s.safesync_url:
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001101 self._ApplySafeSyncRev(dep=s)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001102 if not self._options.revisions:
1103 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001104 solutions_names = [s.name for s in self.dependencies]
maruel@chromium.org307d1792010-05-31 20:03:13 +00001105 index = 0
1106 for revision in self._options.revisions:
1107 if not '@' in revision:
1108 # Support for --revision 123
1109 revision = '%s@%s' % (solutions_names[index], revision)
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001110 sol, rev = revision.split('@', 1)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001111 if not sol in solutions_names:
1112 #raise gclient_utils.Error('%s is not a valid solution.' % sol)
1113 print >> sys.stderr, ('Please fix your script, having invalid '
1114 '--revision flags will soon considered an error.')
1115 else:
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001116 revision_overrides[sol] = rev
maruel@chromium.org307d1792010-05-31 20:03:13 +00001117 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001118 return revision_overrides
1119
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001120 def _ApplySafeSyncRev(self, dep):
1121 """Finds a valid revision from the content of the safesync_url and apply it
1122 by appending revisions to the revision list. Throws if revision appears to
1123 be invalid for the given |dep|."""
1124 assert len(dep.safesync_url) > 0
1125 handle = urllib.urlopen(dep.safesync_url)
1126 rev = handle.read().strip()
1127 handle.close()
1128 if not rev:
1129 raise gclient_utils.Error(
1130 'It appears your safesync_url (%s) is not working properly\n'
1131 '(as it returned an empty response). Check your config.' %
1132 dep.safesync_url)
1133 scm = gclient_scm.CreateSCM(dep.url, dep.root.root_dir, dep.name)
iannucci@chromium.org4a4b33b2013-07-04 20:25:46 +00001134 safe_rev = scm.GetUsableRev(rev, self._options)
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001135 if self._options.verbose:
1136 print('Using safesync_url revision: %s.\n' % safe_rev)
1137 self._options.revisions.append('%s@%s' % (dep.name, safe_rev))
1138
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001139 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001140 """Runs a command on each dependency in a client and its dependencies.
1141
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001142 Args:
1143 command: The command to use (e.g., 'status' or 'diff')
1144 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001145 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001146 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001147 raise gclient_utils.Error('No solution specified')
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001148 revision_overrides = {}
1149 # It's unnecessary to check for revision overrides for 'recurse'.
1150 # Save a few seconds by not calling _EnforceRevisions() in that case.
dbeam@chromium.org0f8a9442012-07-10 14:50:20 +00001151 if command not in ('diff', 'recurse', 'runhooks', 'status'):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001152 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001153 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001154 # Disable progress for non-tty stdout.
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001155 if (sys.stdout.isatty() and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001156 if command in ('update', 'revert'):
1157 pm = Progress('Syncing projects', 1)
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +00001158 elif command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001159 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001160 work_queue = gclient_utils.ExecutionQueue(
1161 self._options.jobs, pm, ignore_requirements=ignore_requirements)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001162 for s in self.dependencies:
1163 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001164 work_queue.flush(revision_overrides, command, args, options=self._options)
piman@chromium.org6f363722010-04-27 00:41:09 +00001165
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001166 # Once all the dependencies have been processed, it's now safe to run the
1167 # hooks.
1168 if not self._options.nohooks:
1169 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001170
1171 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001172 # Notify the user if there is an orphaned entry in their working copy.
1173 # Only delete the directory if there are no changes in it, and
1174 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001175 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001176 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1177 for e in entries]
1178
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001179 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001180 if not prev_url:
1181 # entry must have been overridden via .gclient custom_deps
1182 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001183 # Fix path separator on Windows.
1184 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001185 e_dir = os.path.join(self.root_dir, entry_fixed)
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001186
1187 def _IsParentOfAny(parent, path_list):
1188 parent_plus_slash = parent + '/'
1189 return any(
1190 path[:len(parent_plus_slash)] == parent_plus_slash
1191 for path in path_list)
1192
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001193 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001194 if (entry not in entries and
1195 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001196 os.path.exists(e_dir)):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001197 scm = gclient_scm.CreateSCM(prev_url, self.root_dir, entry_fixed)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001198
1199 # Check to see if this directory is now part of a higher-up checkout.
1200 if scm.GetCheckoutRoot() in full_entries:
1201 logging.info('%s is part of a higher level checkout, not '
1202 'removing.', scm.GetCheckoutRoot())
1203 continue
1204
1205 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001206 scm.status(self._options, [], file_list)
1207 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001208 if (not self._options.delete_unversioned_trees or
1209 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001210 # There are modified files in this entry. Keep warning until
1211 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001212 print(('\nWARNING: \'%s\' is no longer part of this client. '
1213 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001214 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001215 else:
1216 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001217 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001218 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001219 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001220 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001221 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001222 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001223
1224 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001225 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001226 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001227 # Load all the settings.
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001228 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, None, False)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001229 for s in self.dependencies:
1230 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001231 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001232
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001233 def GetURLAndRev(dep):
1234 """Returns the revision-qualified SCM url for a Dependency."""
1235 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001236 return None
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001237 if isinstance(dep.parsed_url, self.FileImpl):
1238 original_url = dep.parsed_url.file_location
1239 else:
1240 original_url = dep.parsed_url
nasser@codeaurora.org5d63eb82010-03-24 23:22:09 +00001241 url, _ = gclient_utils.SplitUrlRevision(original_url)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001242 scm = gclient_scm.CreateSCM(original_url, self.root_dir, dep.name)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001243 if not os.path.isdir(scm.checkout_path):
1244 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001245 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001246
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001247 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001248 new_gclient = ''
1249 # First level at .gclient
1250 for d in self.dependencies:
1251 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001252 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001253 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001254 for d in dep.dependencies:
1255 entries[d.name] = GetURLAndRev(d)
1256 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001257 GrabDeps(d)
1258 custom_deps = []
1259 for k in sorted(entries.keys()):
1260 if entries[k]:
1261 # Quotes aren't escaped...
1262 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1263 else:
1264 custom_deps.append(' \"%s\": None,\n' % k)
1265 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1266 'solution_name': d.name,
1267 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001268 'deps_file': d.deps_file,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001269 'safesync_url' : d.safesync_url or '',
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001270 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001271 'solution_deps': ''.join(custom_deps),
1272 }
1273 # Print the snapshot configuration file
1274 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001275 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001276 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001277 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001278 if self._options.actual:
1279 entries[d.name] = GetURLAndRev(d)
1280 else:
1281 entries[d.name] = d.parsed_url
1282 keys = sorted(entries.keys())
1283 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001284 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001285 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001286
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001287 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001288 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001289 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001290
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001291 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001292 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001293 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001294 return self._root_dir
1295
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001296 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001297 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001298 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001299 return self._enforced_os
1300
maruel@chromium.org68988972011-09-20 14:11:42 +00001301 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001302 def recursion_limit(self):
1303 """How recursive can each dependencies in DEPS file can load DEPS file."""
1304 return self._recursion_limit
1305
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001306 @property
1307 def target_os(self):
1308 return self._enforced_os
1309
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001310
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001311#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001312
1313
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001314def CMDcleanup(parser, args):
1315 """Cleans up all working copies.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001316
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001317Mostly svn-specific. Simply runs 'svn cleanup' for each module.
maruel@chromium.org79692d62010-05-14 18:57:13 +00001318"""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001319 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1320 help='override deps for the specified (comma-separated) '
1321 'platform(s); \'all\' will process all deps_os '
1322 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001323 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001324 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001325 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001326 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001327 if options.verbose:
1328 # Print out the .gclient file. This is longer than if we just printed the
1329 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001330 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001331 return client.RunOnDeps('cleanup', args)
1332
1333
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001334@attr('usage', '[command] [args ...]')
1335def CMDrecurse(parser, args):
1336 """Operates on all the entries.
1337
1338 Runs a shell command on all entries.
ilevy@chromium.org37116242012-11-28 01:32:48 +00001339 Sets GCLIENT_DEP_PATH enviroment variable as the dep's relative location to
1340 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001341 """
1342 # Stop parsing at the first non-arg so that these go through to the command
1343 parser.disable_interspersed_args()
1344 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001345 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001346 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001347 help='Ignore non-zero return codes from subcommands.')
1348 parser.add_option('--prepend-dir', action='store_true',
1349 help='Prepend relative dir for use with git <cmd> --null.')
1350 parser.add_option('--no-progress', action='store_true',
1351 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001352 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001353 if not args:
1354 print >> sys.stderr, 'Need to supply a command!'
1355 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001356 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1357 if not root_and_entries:
1358 print >> sys.stderr, (
1359 'You need to run gclient sync at least once to use \'recurse\'.\n'
1360 'This is because .gclient_entries needs to exist and be up to date.')
1361 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001362
1363 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001364 scm_set = set()
1365 for scm in options.scm:
1366 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001367 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001368
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001369 options.nohooks = True
1370 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001371 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1372 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001373
1374
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001375@attr('usage', '[args ...]')
1376def CMDfetch(parser, args):
1377 """Fetches upstream commits for all modules.
1378
1379Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1380"""
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001381 (options, args) = parser.parse_args(args)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001382 return CMDrecurse(Parser(), [
1383 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1384
1385
1386def CMDgrep(parser, args):
1387 """Greps through git repos managed by gclient.
1388
1389Runs 'git grep [args...]' for each module.
1390"""
1391
1392 # We can't use optparse because it will try to parse arguments sent
1393 # to git grep and throw an error. :-(
1394 if not args or re.match('(-h|--help)$', args[0]):
1395 print >> sys.stderr, (
1396 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1397 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1398 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1399 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
1400 ' end of your query.'
1401 )
1402 return 1
1403
1404 jobs_arg = ['--jobs=1']
1405 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1406 jobs_arg, args = args[:1], args[1:]
1407 elif re.match(r'(-j|--jobs)$', args[0]):
1408 jobs_arg, args = args[:2], args[2:]
1409
1410 return CMDrecurse(
1411 parser,
1412 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1413 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001414
1415
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001416@attr('usage', '[url] [safesync url]')
1417def CMDconfig(parser, args):
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001418 """Create a .gclient file in the current directory.
1419
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001420This specifies the configuration for further commands. After update/sync,
maruel@chromium.org79692d62010-05-14 18:57:13 +00001421top-level DEPS files in each module are read to determine dependent
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001422modules to operate on as well. If optional [url] parameter is
maruel@chromium.org79692d62010-05-14 18:57:13 +00001423provided, then configuration is read from a specified Subversion server
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001424URL.
maruel@chromium.org79692d62010-05-14 18:57:13 +00001425"""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001426
1427 # We do a little dance with the --gclientfile option. 'gclient config' is the
1428 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1429 # arguments. So, we temporarily stash any --gclientfile parameter into
1430 # options.output_config_file until after the (gclientfile xor spec) error
1431 # check.
1432 parser.remove_option('--gclientfile')
1433 parser.add_option('--gclientfile', dest='output_config_file',
1434 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001435 parser.add_option('--name',
1436 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001437 parser.add_option('--deps-file', default='DEPS',
1438 help='overrides the default name for the DEPS file for the'
1439 'main solutions and all sub-dependencies')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001440 parser.add_option('--unmanaged', action='store_true', default=False,
1441 help='overrides the default behavior to make it possible '
1442 'to have the main solution untouched by gclient '
1443 '(gclient will check out unmanaged dependencies but '
1444 'will never sync them)')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001445 parser.add_option('--git-deps', action='store_true',
1446 help='sets the deps file to ".DEPS.git" instead of "DEPS"')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001447 parser.add_option('--cache-dir',
1448 help='(git only) Cache all git repos into this dir and do '
1449 'shared clones from the cache, instead of cloning '
1450 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001451 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001452 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001453 if options.output_config_file:
1454 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001455 if ((options.spec and args) or len(args) > 2 or
1456 (not options.spec and not args)):
1457 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1458
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001459 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001460 if options.spec:
1461 client.SetConfig(options.spec)
1462 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001463 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001464 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001465 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001466 if name.endswith('.git'):
1467 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001468 else:
1469 # specify an alternate relpath for the given URL.
1470 name = options.name
nsylvain@google.comefc80932011-05-31 21:27:56 +00001471 deps_file = options.deps_file
1472 if options.git_deps:
1473 deps_file = '.DEPS.git'
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001474 safesync_url = ''
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001475 if len(args) > 1:
1476 safesync_url = args[1]
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001477 client.SetDefaultConfig(name, deps_file, base_url, safesync_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001478 managed=not options.unmanaged,
1479 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001480 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001481 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001482
1483
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001484@attr('epilog', """Example:
1485 gclient pack > patch.txt
1486 generate simple patch for configured client and dependences
1487""")
1488def CMDpack(parser, args):
maruel@chromium.org79692d62010-05-14 18:57:13 +00001489 """Generate a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001490
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001491Internally, runs 'svn diff'/'git diff' on each checked out module and
maruel@chromium.org79692d62010-05-14 18:57:13 +00001492dependencies, and performs minimal postprocessing of the output. The
1493resulting patch is printed to stdout and can be applied to a freshly
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001494checked out tree via 'patch -p0 < patchfile'.
maruel@chromium.org79692d62010-05-14 18:57:13 +00001495"""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001496 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1497 help='override deps for the specified (comma-separated) '
1498 'platform(s); \'all\' will process all deps_os '
1499 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001500 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001501 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001502 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001503 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001504 client = GClient.LoadCurrentConfig(options)
1505 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001506 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001507 if options.verbose:
1508 # Print out the .gclient file. This is longer than if we just printed the
1509 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001510 print(client.config_content)
kbr@google.comab318592009-09-04 00:54:55 +00001511 return client.RunOnDeps('pack', args)
1512
1513
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001514def CMDstatus(parser, args):
1515 """Show modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001516 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1517 help='override deps for the specified (comma-separated) '
1518 'platform(s); \'all\' will process all deps_os '
1519 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001520 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001521 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001522 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001523 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001524 if options.verbose:
1525 # Print out the .gclient file. This is longer than if we just printed the
1526 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001527 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001528 return client.RunOnDeps('status', args)
1529
1530
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001531@attr('epilog', """Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001532 gclient sync
1533 update files from SCM according to current configuration,
1534 *for modules which have changed since last update or sync*
1535 gclient sync --force
1536 update files from SCM according to current configuration, for
1537 all modules (useful for recovering files deleted from local copy)
1538 gclient sync --revision src@31000
1539 update src directory to r31000
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001540""")
1541def CMDsync(parser, args):
1542 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001543 parser.add_option('-f', '--force', action='store_true',
1544 help='force update even for unchanged modules')
1545 parser.add_option('-n', '--nohooks', action='store_true',
1546 help='don\'t run hooks after the update is complete')
1547 parser.add_option('-r', '--revision', action='append',
1548 dest='revisions', metavar='REV', default=[],
1549 help='Enforces revision/hash for the solutions with the '
1550 'format src@rev. The src@ part is optional and can be '
1551 'skipped. -r can be used multiple times when .gclient '
1552 'has multiple solutions configured and will work even '
joi@chromium.org792ea882010-11-10 02:37:27 +00001553 'if the src@ part is skipped. Note that specifying '
1554 '--revision means your safesync_url gets ignored.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00001555 parser.add_option('--with_branch_heads', action='store_true',
1556 help='Clone git "branch_heads" refspecs in addition to '
1557 'the default refspecs. This adds about 1/2GB to a '
1558 'full checkout. (git only)')
floitsch@google.comeaab7842011-04-28 09:07:58 +00001559 parser.add_option('-t', '--transitive', action='store_true',
1560 help='When a revision is specified (in the DEPS file or '
1561 'with the command-line flag), transitively update '
1562 'the dependencies to the date of the given revision. '
1563 'Only supported for SVN repositories.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001564 parser.add_option('-H', '--head', action='store_true',
1565 help='skips any safesync_urls specified in '
1566 'configured solutions and sync to head instead')
1567 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001568 help='Deletes from the working copy any dependencies that '
1569 'have been removed since the last sync, as long as '
1570 'there are no local modifications. When used with '
1571 '--force, such dependencies are removed even if they '
1572 'have local modifications. When used with --reset, '
1573 'all untracked directories are removed from the '
1574 'working copy, exclusing those which are explicitly '
1575 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001576 parser.add_option('-R', '--reset', action='store_true',
1577 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00001578 parser.add_option('-M', '--merge', action='store_true',
1579 help='merge upstream changes instead of trying to '
1580 'fast-forward or rebase')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001581 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1582 help='override deps for the specified (comma-separated) '
1583 'platform(s); \'all\' will process all deps_os '
1584 'references')
1585 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
1586 help='Skip svn up whenever possible by requesting '
1587 'actual HEAD revision from the repository')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001588 parser.add_option('--upstream', action='store_true',
1589 help='Make repo state match upstream branch.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001590 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001591 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001592
1593 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001594 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001595
maruel@chromium.org307d1792010-05-31 20:03:13 +00001596 if options.revisions and options.head:
1597 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001598 print('Warning: you cannot use both --head and --revision')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001599
1600 if options.verbose:
1601 # Print out the .gclient file. This is longer than if we just printed the
1602 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001603 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001604 return client.RunOnDeps('update', args)
1605
1606
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001607def CMDupdate(parser, args):
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001608 """Alias for the sync command. Deprecated."""
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001609 return CMDsync(parser, args)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001610
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001611def CMDdiff(parser, args):
1612 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001613 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1614 help='override deps for the specified (comma-separated) '
1615 'platform(s); \'all\' will process all deps_os '
1616 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001617 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001618 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001619 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001620 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001621 if options.verbose:
1622 # Print out the .gclient file. This is longer than if we just printed the
1623 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001624 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001625 return client.RunOnDeps('diff', args)
1626
1627
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001628def CMDrevert(parser, args):
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001629 """Revert all modifications in every dependencies.
1630
1631 That's the nuclear option to get back to a 'clean' state. It removes anything
1632 that shows up in svn status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001633 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1634 help='override deps for the specified (comma-separated) '
1635 'platform(s); \'all\' will process all deps_os '
1636 'references')
1637 parser.add_option('-n', '--nohooks', action='store_true',
1638 help='don\'t run hooks after the revert is complete')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001639 parser.add_option('--upstream', action='store_true',
1640 help='Make repo state match upstream branch.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001641 (options, args) = parser.parse_args(args)
1642 # --force is implied.
1643 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001644 options.reset = False
1645 options.delete_unversioned_trees = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001646 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001647 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001648 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001649 return client.RunOnDeps('revert', args)
1650
1651
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001652def CMDrunhooks(parser, args):
1653 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001654 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1655 help='override deps for the specified (comma-separated) '
1656 'platform(s); \'all\' will process all deps_os '
1657 'references')
1658 parser.add_option('-f', '--force', action='store_true', default=True,
1659 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001660 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001661 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001662 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001663 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001664 if options.verbose:
1665 # Print out the .gclient file. This is longer than if we just printed the
1666 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001667 print(client.config_content)
maruel@chromium.org5df6a462009-08-28 18:52:26 +00001668 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001669 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001670 return client.RunOnDeps('runhooks', args)
1671
1672
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001673def CMDrevinfo(parser, args):
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001674 """Output revision info mapping for the client and its dependencies.
1675
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001676 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001677 can be used to reproduce the same tree in the future. It is only useful for
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001678 'unpinned dependencies', i.e. DEPS/deps references without a svn revision
1679 number or a git hash. A git branch name isn't 'pinned' since the actual
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001680 commit can change.
1681 """
1682 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1683 help='override deps for the specified (comma-separated) '
1684 'platform(s); \'all\' will process all deps_os '
1685 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001686 parser.add_option('-a', '--actual', action='store_true',
1687 help='gets the actual checked out revisions instead of the '
1688 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001689 parser.add_option('-s', '--snapshot', action='store_true',
1690 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001691 'version of all repositories to reproduce the tree, '
1692 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001693 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001694 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001695 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001696 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001697 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001698 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001699
1700
szager@google.comb9a78d32012-03-13 18:46:21 +00001701def CMDhookinfo(parser, args):
1702 """Output the hooks that would be run by `gclient runhooks`"""
1703 (options, args) = parser.parse_args(args)
1704 options.force = True
1705 client = GClient.LoadCurrentConfig(options)
1706 if not client:
1707 raise gclient_utils.Error('client not configured; see \'gclient config\'')
1708 client.RunOnDeps(None, [])
1709 print '; '.join(' '.join(hook) for hook in client.GetHooks(options))
1710 return 0
1711
1712
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001713def Command(name):
1714 return getattr(sys.modules[__name__], 'CMD' + name, None)
1715
1716
1717def CMDhelp(parser, args):
1718 """Prints list of commands or help for a specific command."""
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001719 (_, args) = parser.parse_args(args)
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001720 if len(args) == 1:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001721 return Main(args + ['--help'])
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001722 parser.print_help()
1723 return 0
1724
1725
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001726def GenUsage(parser, command):
1727 """Modify an OptParse object with the function's documentation."""
1728 obj = Command(command)
1729 if command == 'help':
1730 command = '<command>'
1731 # OptParser.description prefer nicely non-formatted strings.
1732 parser.description = re.sub('[\r\n ]{2,}', ' ', obj.__doc__)
1733 usage = getattr(obj, 'usage', '')
1734 parser.set_usage('%%prog %s [options] %s' % (command, usage))
1735 parser.epilog = getattr(obj, 'epilog', None)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001736
1737
maruel@chromium.org0895b752011-08-26 20:40:33 +00001738def Parser():
1739 """Returns the default parser."""
1740 parser = optparse.OptionParser(version='%prog ' + __version__)
maruel@chromium.org9aa1ce52012-07-16 13:57:18 +00001741 # some arm boards have issues with parallel sync.
1742 if platform.machine().startswith('arm'):
bradnelson@google.com4949dab2012-04-19 16:41:07 +00001743 jobs = 1
1744 else:
ilevy@chromium.org13691502012-10-16 04:26:37 +00001745 jobs = max(8, gclient_utils.NumLocalCpus())
cmp@chromium.org3b37d342013-06-19 19:14:25 +00001746 # cmp: 2013/06/19
1747 # Temporary workaround to lower bot-load on SVN server.
1748 if os.environ.get('CHROME_HEADLESS') == '1':
ilevy@chromium.org413ffbc2013-06-20 02:35:38 +00001749 jobs = 4
szager@chromium.orge2e03202012-07-31 18:05:16 +00001750 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
maruel@chromium.org41071612011-10-19 19:58:08 +00001751 parser.add_option('-j', '--jobs', default=jobs, type='int',
maruel@chromium.org0895b752011-08-26 20:40:33 +00001752 help='Specify how many SCM commands can run in parallel; '
ilevy@chromium.org13691502012-10-16 04:26:37 +00001753 'defaults to number of cpu cores (%default)')
maruel@chromium.org0895b752011-08-26 20:40:33 +00001754 parser.add_option('-v', '--verbose', action='count', default=0,
1755 help='Produces additional output for diagnostics. Can be '
1756 'used up to three times for more logging info.')
1757 parser.add_option('--gclientfile', dest='config_filename',
szager@chromium.orge2e03202012-07-31 18:05:16 +00001758 default=None,
1759 help='Specify an alternate %s file' % gclientfile_default)
1760 parser.add_option('--spec',
1761 default=None,
1762 help='create a gclient file containing the provided '
1763 'string. Due to Cygwin/Python brokenness, it '
1764 'probably can\'t contain any newlines.')
szager@chromium.org41da24b2013-05-23 19:37:04 +00001765 parser.add_option('--no-nag-max', default=False, action='store_true',
1766 help='If a subprocess runs for too long without generating'
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001767 ' terminal output, generate warnings, but do not kill'
1768 ' the process.')
maruel@chromium.org0895b752011-08-26 20:40:33 +00001769 # Integrate standard options processing.
1770 old_parser = parser.parse_args
1771 def Parse(args):
1772 (options, args) = old_parser(args)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001773 level = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG][
1774 min(options.verbose, 3)]
maruel@chromium.org0895b752011-08-26 20:40:33 +00001775 logging.basicConfig(level=level,
1776 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001777 if options.config_filename and options.spec:
1778 parser.error('Cannot specifiy both --gclientfile and --spec')
1779 if not options.config_filename:
1780 options.config_filename = gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00001781 options.entries_filename = options.config_filename + '_entries'
1782 if options.jobs < 1:
1783 parser.error('--jobs must be 1 or higher')
1784
1785 # These hacks need to die.
1786 if not hasattr(options, 'revisions'):
1787 # GClient.RunOnDeps expects it even if not applicable.
1788 options.revisions = []
1789 if not hasattr(options, 'head'):
1790 options.head = None
1791 if not hasattr(options, 'nohooks'):
1792 options.nohooks = True
1793 if not hasattr(options, 'deps_os'):
1794 options.deps_os = None
1795 if not hasattr(options, 'manually_grab_svn_rev'):
1796 options.manually_grab_svn_rev = None
1797 if not hasattr(options, 'force'):
1798 options.force = None
szager@chromium.org41da24b2013-05-23 19:37:04 +00001799 if options.no_nag_max:
1800 gclient_scm.SCMWrapper.nag_max = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00001801 return (options, args)
1802 parser.parse_args = Parse
1803 # We don't want wordwrapping in epilog (usually examples)
1804 parser.format_epilog = lambda _: parser.epilog or ''
1805 return parser
1806
1807
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001808def Main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001809 """Doesn't parse the arguments here, just find the right subcommand to
1810 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00001811 if sys.hexversion < 0x02060000:
maruel@chromium.orgc3a15a22010-11-20 03:12:27 +00001812 print >> sys.stderr, (
maruel@chromium.org82798cb2012-02-23 18:16:12 +00001813 '\nYour python version %s is unsupported, please upgrade.\n' %
1814 sys.version.split(' ', 1)[0])
1815 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00001816 if not sys.executable:
1817 print >> sys.stderr, (
1818 '\nPython cannot find the location of it\'s own executable.\n')
1819 return 2
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +00001820 colorama.init()
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001821 try:
maruel@chromium.orge0de9cb2010-09-17 15:07:14 +00001822 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
1823 # operations. Python as a strong tendency to buffer sys.stdout.
1824 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
maruel@chromium.org4ed34182010-09-17 15:57:47 +00001825 # Make stdout annotated with the thread ids.
1826 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001827 # Do it late so all commands are listed.
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +00001828 # Unused variable 'usage'
1829 # pylint: disable=W0612
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +00001830 def to_str(fn):
1831 return (
1832 ' %s%-10s%s' % (Fore.GREEN, fn[3:], Fore.RESET) +
1833 ' %s' % Command(fn[3:]).__doc__.split('\n')[0].strip())
1834 cmds = (
1835 to_str(fn) for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')
1836 )
1837 CMDhelp.usage = '\n\nCommands are:\n' + '\n'.join(cmds)
maruel@chromium.org0895b752011-08-26 20:40:33 +00001838 parser = Parser()
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001839 if argv:
1840 command = Command(argv[0])
1841 if command:
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001842 # 'fix' the usage and the description now that we know the subcommand.
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001843 GenUsage(parser, argv[0])
1844 return command(parser, argv[1:])
1845 # Not a known command. Default to help.
1846 GenUsage(parser, 'help')
1847 return CMDhelp(parser, argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00001848 except KeyboardInterrupt:
1849 gclient_utils.GClientChildren.KillAllRemainingChildren()
1850 raise
maruel@chromium.org31cb48a2011-04-04 18:01:36 +00001851 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001852 print >> sys.stderr, 'Error: %s' % str(e)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001853 return 1
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001854
1855
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001856if '__main__' == __name__:
maruel@chromium.org35625c72011-03-23 17:34:02 +00001857 fix_encoding.fix_encoding()
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001858 sys.exit(Main(sys.argv[1:]))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001859
1860# vim: ts=2:sw=2:tw=80:et: