blob: c3e295c42e1dca0fff893a61352cfa7deee4fe36 [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.org39c0b222013-08-17 16:57:01 +00006"""Meta checkout manager supporting both Subversion and GIT."""
7# Files
8# .gclient : Current client configuration, written by 'config' command.
9# Format is a Python script defining 'solutions', a list whose
10# entries each are maps binding the strings "name" and "url"
11# to strings specifying the name and location of the client
12# module, as well as "custom_deps" to a map similar to the
13# deps section of the DEPS file below, as well as
14# "custom_hooks" to a list similar to the hooks sections of
15# the DEPS file below.
16# .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
20# requisite submodule name to a URL where it can be found (via
21# one SCM)
22#
23# Hooks
24# .gclient and DEPS files may optionally contain a list named "hooks" to
25# allow custom actions to be performed based on files that have changed in the
26# working copy as a result of a "sync"/"update" or "revert" operation. This
27# can be prevented by using --nohooks (hooks run by default). Hooks can also
28# be forced to run with the "runhooks" operation. If "sync" is run with
29# --force, all known but not suppressed hooks will run regardless of the state
30# of the working copy.
31#
32# Each item in a "hooks" list is a dict, containing these two keys:
33# "pattern" The associated value is a string containing a regular
34# expression. When a file whose pathname matches the expression
35# is checked out, updated, or reverted, the hook's "action" will
36# run.
37# "action" A list describing a command to run along with its arguments, if
38# any. An action command will run at most one time per gclient
39# invocation, regardless of how many files matched the pattern.
40# The action is executed in the same directory as the .gclient
41# file. If the first item in the list is the string "python",
42# the current Python interpreter (sys.executable) will be used
43# to run the command. If the list contains string
44# "$matching_files" it will be removed from the list and the list
45# will be extended by the list of matching files.
46# "name" An optional string specifying the group to which a hook belongs
47# for overriding and organizing.
48#
49# Example:
50# hooks = [
51# { "pattern": "\\.(gif|jpe?g|pr0n|png)$",
52# "action": ["python", "image_indexer.py", "--all"]},
53# { "pattern": ".",
54# "name": "gyp",
55# "action": ["python", "src/build/gyp_chromium"]},
56# ]
57#
borenet@google.com2d1ee9e2013-10-15 08:13:16 +000058# Pre-DEPS Hooks
59# DEPS files may optionally contain a list named "pre_deps_hooks". These are
60# the same as normal hooks, except that they run before the DEPS are
61# processed. Pre-DEPS run with "sync" and "revert" unless the --noprehooks
62# flag is used.
63#
maruel@chromium.org39c0b222013-08-17 16:57:01 +000064# Specifying a target OS
65# An optional key named "target_os" may be added to a gclient file to specify
66# one or more additional operating systems that should be considered when
67# processing the deps_os dict of a DEPS file.
68#
69# Example:
70# target_os = [ "android" ]
71#
72# If the "target_os_only" key is also present and true, then *only* the
73# operating systems listed in "target_os" will be used.
74#
75# Example:
76# target_os = [ "ios" ]
77# target_os_only = True
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000078
maruel@chromium.org39c0b222013-08-17 16:57:01 +000079__version__ = '0.7'
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000080
maruel@chromium.org9e5317a2010-08-13 20:35:11 +000081import copy
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +000082import json
maruel@chromium.org754960e2009-09-21 12:31:05 +000083import logging
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000084import optparse
85import os
bradnelson@google.com4949dab2012-04-19 16:41:07 +000086import platform
maruel@chromium.org621939b2010-08-10 20:12:00 +000087import posixpath
msb@chromium.org2e38de72009-09-28 17:04:47 +000088import pprint
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000089import re
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000090import sys
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +000091import time
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000092import urllib
bradnelson@google.com4949dab2012-04-19 16:41:07 +000093import urlparse
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000094
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +000095import breakpad # pylint: disable=W0611
maruel@chromium.orgada4c652009-12-03 15:32:01 +000096
maruel@chromium.org35625c72011-03-23 17:34:02 +000097import fix_encoding
maruel@chromium.org5f3eee32009-09-17 00:34:30 +000098import gclient_scm
99import gclient_utils
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000100from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000101import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000102import subprocess2
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +0000103from third_party import colorama
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000104
105
maruel@chromium.org116704f2010-06-11 17:34:38 +0000106class GClientKeywords(object):
107 class FromImpl(object):
108 """Used to implement the From() syntax."""
109
110 def __init__(self, module_name, sub_target_name=None):
111 """module_name is the dep module we want to include from. It can also be
112 the name of a subdirectory to include from.
113
114 sub_target_name is an optional parameter if the module name in the other
115 DEPS file is different. E.g., you might want to map src/net to net."""
116 self.module_name = module_name
117 self.sub_target_name = sub_target_name
118
119 def __str__(self):
120 return 'From(%s, %s)' % (repr(self.module_name),
121 repr(self.sub_target_name))
122
maruel@chromium.org116704f2010-06-11 17:34:38 +0000123 class FileImpl(object):
124 """Used to implement the File('') syntax which lets you sync a single file
maruel@chromium.orge3216c62010-07-08 03:31:43 +0000125 from a SVN repo."""
maruel@chromium.org116704f2010-06-11 17:34:38 +0000126
127 def __init__(self, file_location):
128 self.file_location = file_location
129
130 def __str__(self):
131 return 'File("%s")' % self.file_location
132
133 def GetPath(self):
134 return os.path.split(self.file_location)[0]
135
136 def GetFilename(self):
137 rev_tokens = self.file_location.split('@')
138 return os.path.split(rev_tokens[0])[1]
139
140 def GetRevision(self):
141 rev_tokens = self.file_location.split('@')
142 if len(rev_tokens) > 1:
143 return rev_tokens[1]
144 return None
145
146 class VarImpl(object):
147 def __init__(self, custom_vars, local_scope):
148 self._custom_vars = custom_vars
149 self._local_scope = local_scope
150
151 def Lookup(self, var_name):
152 """Implements the Var syntax."""
153 if var_name in self._custom_vars:
154 return self._custom_vars[var_name]
155 elif var_name in self._local_scope.get("vars", {}):
156 return self._local_scope["vars"][var_name]
157 raise gclient_utils.Error("Var is not defined: %s" % var_name)
158
159
maruel@chromium.org064186c2011-09-27 23:53:33 +0000160class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000161 """Immutable configuration settings."""
162 def __init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000163 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000164 custom_hooks, deps_file, should_process):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000165 GClientKeywords.__init__(self)
166
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000167 # These are not mutable:
168 self._parent = parent
169 self._safesync_url = safesync_url
170 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000171 self._url = url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000172 # 'managed' determines whether or not this dependency is synced/updated by
173 # gclient after gclient checks it out initially. The difference between
174 # 'managed' and 'should_process' is that the user specifies 'managed' via
175 # the --unmanaged command-line flag or a .gclient config, where
176 # 'should_process' is dynamically set by gclient if it goes over its
177 # recursion limit and controls gclient's behavior so it does not misbehave.
178 self._managed = managed
179 self._should_process = should_process
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000180 # This is a mutable value that overrides the normal recursion limit for this
181 # dependency. It is read from the actual DEPS file so cannot be set on
182 # class instantiation.
183 self.recursion_override = None
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000184 # This is a mutable value which has the list of 'target_os' OSes listed in
185 # the current deps file.
186 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000187
188 # These are only set in .gclient and not in DEPS files.
189 self._custom_vars = custom_vars or {}
190 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000191 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000192
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000193 # TODO(iannucci): Remove this when all masters are correctly substituting
194 # the new blink url.
195 if (self._custom_vars.get('webkit_trunk', '') ==
196 'svn://svn-mirror.golo.chromium.org/webkit-readonly/trunk'):
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000197 new_url = 'svn://svn-mirror.golo.chromium.org/blink/trunk'
198 print 'Overwriting Var("webkit_trunk") with %s' % new_url
199 self._custom_vars['webkit_trunk'] = new_url
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000200
maruel@chromium.org064186c2011-09-27 23:53:33 +0000201 # Post process the url to remove trailing slashes.
202 if isinstance(self._url, basestring):
203 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
204 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000205 self._url = self._url.replace('/@', '@')
206 elif not isinstance(self._url,
207 (self.FromImpl, self.FileImpl, None.__class__)):
208 raise gclient_utils.Error(
209 ('dependency url must be either a string, None, '
210 'File() or From() instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000211 # Make any deps_file path platform-appropriate.
212 for sep in ['/', '\\']:
213 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000214
215 @property
216 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000217 return self._deps_file
218
219 @property
220 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000221 return self._managed
222
223 @property
224 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000225 return self._parent
226
227 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000228 def root(self):
229 """Returns the root node, a GClient object."""
230 if not self.parent:
231 # This line is to signal pylint that it could be a GClient instance.
232 return self or GClient(None, None)
233 return self.parent.root
234
235 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000236 def safesync_url(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000237 return self._safesync_url
238
239 @property
240 def should_process(self):
241 """True if this dependency should be processed, i.e. checked out."""
242 return self._should_process
243
244 @property
245 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000246 return self._custom_vars.copy()
247
248 @property
249 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000250 return self._custom_deps.copy()
251
maruel@chromium.org064186c2011-09-27 23:53:33 +0000252 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000253 def custom_hooks(self):
254 return self._custom_hooks[:]
255
256 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000257 def url(self):
258 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000259
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000260 @property
261 def recursion_limit(self):
262 """Returns > 0 if this dependency is not too recursed to be processed."""
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000263 if self.recursion_override is not None:
264 return self.recursion_override
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000265 return max(self.parent.recursion_limit - 1, 0)
266
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000267 @property
268 def target_os(self):
269 if self.local_target_os is not None:
270 return tuple(set(self.local_target_os).union(self.parent.target_os))
271 else:
272 return self.parent.target_os
273
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000274 def get_custom_deps(self, name, url):
275 """Returns a custom deps if applicable."""
276 if self.parent:
277 url = self.parent.get_custom_deps(name, url)
278 # None is a valid return value to disable a dependency.
279 return self.custom_deps.get(name, url)
280
maruel@chromium.org064186c2011-09-27 23:53:33 +0000281
282class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000283 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000284
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000285 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000286 custom_vars, custom_hooks, deps_file, should_process):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000287 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000288 DependencySettings.__init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000289 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000290 custom_hooks, deps_file, should_process)
maruel@chromium.org68988972011-09-20 14:11:42 +0000291
292 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000293 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000294
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000295 self._pre_deps_hooks = []
296
maruel@chromium.org68988972011-09-20 14:11:42 +0000297 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000298 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000299 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000300 # A cache of the files affected by the current operation, necessary for
301 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000302 self._file_list = []
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000303 # If it is not set to True, the dependency wasn't processed for its child
304 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000305 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000306 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000307 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000308 # This dependency had its pre-DEPS hooks run
309 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000310 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000311 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000312 # This is the scm used to checkout self.url. It may be used by dependencies
313 # to get the datetime of the revision we checked out.
314 self._used_scm = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000315 # The actual revision we ended up getting, or None if that information is
316 # unavailable
317 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000318
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000319 if not self.name and self.parent:
320 raise gclient_utils.Error('Dependency without name')
321
maruel@chromium.org470b5432011-10-11 18:18:19 +0000322 @property
323 def requirements(self):
324 """Calculate the list of requirements."""
325 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000326 # self.parent is implicitly a requirement. This will be recursive by
327 # definition.
328 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000329 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000330
331 # For a tree with at least 2 levels*, the leaf node needs to depend
332 # on the level higher up in an orderly way.
333 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
334 # thus unsorted, while the .gclient format is a list thus sorted.
335 #
336 # * _recursion_limit is hard coded 2 and there is no hope to change this
337 # value.
338 #
339 # Interestingly enough, the following condition only works in the case we
340 # want: self is a 2nd level node. 3nd level node wouldn't need this since
341 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000342 if self.parent and self.parent.parent and not self.parent.parent.parent:
343 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000344
345 if isinstance(self.url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000346 requirements.add(self.url.module_name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000347
maruel@chromium.org470b5432011-10-11 18:18:19 +0000348 if self.name:
349 requirements |= set(
350 obj.name for obj in self.root.subtree(False)
351 if (obj is not self
352 and obj.name and
353 self.name.startswith(posixpath.join(obj.name, ''))))
354 requirements = tuple(sorted(requirements))
355 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
356 return requirements
357
358 def verify_validity(self):
359 """Verifies that this Dependency is fine to add as a child of another one.
360
361 Returns True if this entry should be added, False if it is a duplicate of
362 another entry.
363 """
364 logging.info('Dependency(%s).verify_validity()' % self.name)
365 if self.name in [s.name for s in self.parent.dependencies]:
366 raise gclient_utils.Error(
367 'The same name "%s" appears multiple times in the deps section' %
368 self.name)
369 if not self.should_process:
370 # Return early, no need to set requirements.
371 return True
372
373 # This require a full tree traversal with locks.
374 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
375 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000376 self_url = self.LateOverride(self.url)
377 sibling_url = sibling.LateOverride(sibling.url)
378 # Allow to have only one to be None or ''.
379 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000380 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000381 ('Dependency %s specified more than once:\n'
382 ' %s [%s]\n'
383 'vs\n'
384 ' %s [%s]') % (
385 self.name,
386 sibling.hierarchy(),
387 sibling_url,
388 self.hierarchy(),
389 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000390 # In theory we could keep it as a shadow of the other one. In
391 # practice, simply ignore it.
392 logging.warn('Won\'t process duplicate dependency %s' % sibling)
393 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000394 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000395
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000396 def LateOverride(self, url):
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000397 """Resolves the parsed url from url.
398
399 Manages From() keyword accordingly. Do not touch self.parsed_url nor
400 self.url because it may called with other urls due to From()."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000401 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000402 parsed_url = self.get_custom_deps(self.name, url)
403 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000404 logging.info(
405 'Dependency(%s).LateOverride(%s) -> %s' %
406 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000407 return parsed_url
408
409 if isinstance(url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000410 # Requires tree traversal.
maruel@chromium.org68988972011-09-20 14:11:42 +0000411 ref = [
412 dep for dep in self.root.subtree(True) if url.module_name == dep.name
413 ]
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000414 if not ref:
415 raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
416 url.module_name, ref))
417 # It may happen that len(ref) > 1 but it's no big deal.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000418 ref = ref[0]
maruel@chromium.org98d05fa2010-07-22 21:58:01 +0000419 sub_target = url.sub_target_name or self.name
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000420 found_deps = [d for d in ref.dependencies if d.name == sub_target]
421 if len(found_deps) != 1:
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000422 raise gclient_utils.Error(
maruel@chromium.org98023df2011-09-07 18:44:47 +0000423 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
424 sub_target, ref.name, self.name, self.parent.name,
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000425 str(self.root)))
maruel@chromium.org98023df2011-09-07 18:44:47 +0000426
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000427 # Call LateOverride() again.
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000428 found_dep = found_deps[0]
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000429 parsed_url = found_dep.LateOverride(found_dep.url)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000430 logging.info(
maruel@chromium.org470b5432011-10-11 18:18:19 +0000431 'Dependency(%s).LateOverride(%s) -> %s (From)' %
432 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000433 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000434
435 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000436 parsed_url = urlparse.urlparse(url)
437 if not parsed_url[0]:
438 # A relative url. Fetch the real base.
439 path = parsed_url[2]
440 if not path.startswith('/'):
441 raise gclient_utils.Error(
442 'relative DEPS entry \'%s\' must begin with a slash' % url)
443 # Create a scm just to query the full url.
444 parent_url = self.parent.parsed_url
445 if isinstance(parent_url, self.FileImpl):
446 parent_url = parent_url.file_location
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000447 scm = gclient_scm.CreateSCM(parent_url, self.root.root_dir, None)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000448 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000449 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000450 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000451 logging.info(
452 'Dependency(%s).LateOverride(%s) -> %s' %
453 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000454 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000455
456 if isinstance(url, self.FileImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000457 logging.info(
458 'Dependency(%s).LateOverride(%s) -> %s (File)' %
459 (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000460 return url
461
462 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000463 logging.info(
464 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000465 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000466
467 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000468
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000469 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000470 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000471 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000472 assert not self.dependencies
473 # One thing is unintuitive, vars = {} must happen before Var() use.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000474 local_scope = {}
475 var = self.VarImpl(self.custom_vars, local_scope)
476 global_scope = {
477 'File': self.FileImpl,
478 'From': self.FromImpl,
479 'Var': var.Lookup,
480 'deps_os': {},
481 }
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000482 filepath = os.path.join(self.root.root_dir, self.name, self.deps_file)
maruel@chromium.org46304292010-10-28 11:42:00 +0000483 if not os.path.isfile(filepath):
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000484 logging.info(
485 'ParseDepsFile(%s): No %s file found at %s' % (
486 self.name, self.deps_file, filepath))
maruel@chromium.org46304292010-10-28 11:42:00 +0000487 else:
488 deps_content = gclient_utils.FileRead(filepath)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000489 logging.debug('ParseDepsFile(%s) read:\n%s' % (self.name, deps_content))
maruel@chromium.org46304292010-10-28 11:42:00 +0000490 # Eval the content.
491 try:
492 exec(deps_content, global_scope, local_scope)
493 except SyntaxError, e:
494 gclient_utils.SyntaxErrorToError(filepath, e)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000495 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000496 if 'recursion' in local_scope:
497 self.recursion_override = local_scope.get('recursion')
498 logging.warning(
499 'Setting %s recursion to %d.', self.name, self.recursion_limit)
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000500 # If present, save 'target_os' in the local_target_os property.
501 if 'target_os' in local_scope:
502 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000503 # load os specific dependencies if defined. these dependencies may
504 # override or extend the values defined by the 'deps' member.
sivachandra@chromium.orga0ad8ad2012-11-06 19:41:28 +0000505 target_os_deps = {}
maruel@chromium.org271375b2010-06-23 19:17:38 +0000506 if 'deps_os' in local_scope:
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000507 for deps_os_key in self.target_os:
maruel@chromium.org271375b2010-06-23 19:17:38 +0000508 os_deps = local_scope['deps_os'].get(deps_os_key, {})
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000509 if len(self.target_os) > 1:
maruel@chromium.org271375b2010-06-23 19:17:38 +0000510 # Ignore any conflict when including deps for more than one
maruel@chromium.org46304292010-10-28 11:42:00 +0000511 # platform, so we collect the broadest set of dependencies
512 # available. We may end up with the wrong revision of something for
513 # our platform, but this is the best we can do.
sivachandra@chromium.orga0ad8ad2012-11-06 19:41:28 +0000514 target_os_deps.update(
515 [x for x in os_deps.items() if not x[0] in target_os_deps])
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000516 else:
sivachandra@chromium.orga0ad8ad2012-11-06 19:41:28 +0000517 target_os_deps.update(os_deps)
518
519 # deps_os overrides paths from deps
520 deps.update(target_os_deps)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000521
maruel@chromium.org271375b2010-06-23 19:17:38 +0000522 # If a line is in custom_deps, but not in the solution, we want to append
523 # this line to the solution.
524 for d in self.custom_deps:
525 if d not in deps:
526 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000527
528 # If use_relative_paths is set in the DEPS file, regenerate
529 # the dictionary using paths relative to the directory containing
530 # the DEPS file.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000531 use_relative_paths = local_scope.get('use_relative_paths', False)
532 if use_relative_paths:
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000533 rel_deps = {}
534 for d, url in deps.items():
535 # normpath is required to allow DEPS to use .. in their
536 # dependency local path.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000537 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url
538 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000539
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000540 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000541 deps_to_add = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000542 for name, url in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000543 should_process = self.recursion_limit and self.should_process
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000544 deps_to_add.append(Dependency(
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000545 self, name, url, None, None, None, None, None,
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000546 self.deps_file, should_process))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000547 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000548
549 # override named sets of hooks by the custom hooks
550 hooks_to_run = []
551 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
552 for hook in local_scope.get('hooks', []):
553 if hook.get('name', '') not in hook_names_to_suppress:
554 hooks_to_run.append(hook)
555
556 # add the replacements and any additions
557 for hook in self.custom_hooks:
558 if 'action' in hook:
559 hooks_to_run.append(hook)
560
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000561 self._pre_deps_hooks = [self.GetHookAction(hook, []) for hook in
562 local_scope.get('pre_deps_hooks', [])]
563
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000564 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000565 logging.info('ParseDepsFile(%s) done' % self.name)
566
567 def add_dependencies_and_close(self, deps_to_add, hooks):
568 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000569 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000570 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000571 self.add_dependency(dep)
572 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000573
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000574 def maybeGetParentRevision(
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000575 self, command, options, parsed_url, parent_name, revision_overrides):
576 """Uses revision/timestamp of parent if no explicit revision was specified.
577
578 If we are performing an update and --transitive is set, use
579 - the parent's revision if 'self.url' is in the same repository
580 - the parent's timestamp otherwise
581 to update 'self.url'. The used revision/timestamp will be set in
582 'options.revision'.
583 If we have an explicit revision do nothing.
584 """
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000585 if command == 'update' and options.transitive and not options.revision:
586 _, revision = gclient_utils.SplitUrlRevision(parsed_url)
587 if not revision:
588 options.revision = revision_overrides.get(parent_name)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000589 if (options.revision and
590 not gclient_utils.IsDateRevision(options.revision)):
591 assert self.parent and self.parent.used_scm
592 # If this dependency is in the same repository as parent it's url will
593 # start with a slash. If so we take the parent revision instead of
594 # it's timestamp.
595 # (The timestamps of commits in google code are broken -- which can
596 # result in dependencies to be checked out at the wrong revision)
597 if self.url.startswith('/'):
598 if options.verbose:
599 print('Using parent\'s revision %s since we are in the same '
600 'repository.' % options.revision)
601 else:
602 parent_revision_date = self.parent.used_scm.GetRevisionDate(
603 options.revision)
604 options.revision = gclient_utils.MakeDateRevision(
605 parent_revision_date)
606 if options.verbose:
607 print('Using parent\'s revision date %s since we are in a '
608 'different repository.' % options.revision)
609 revision_overrides[self.name] = options.revision
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000610
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000611 # Arguments number differs from overridden method
612 # pylint: disable=W0221
maruel@chromium.org3742c842010-09-09 19:27:14 +0000613 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000614 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000615 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000616 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000617 if not self.should_process:
618 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000619 # When running runhooks, there's no need to consult the SCM.
620 # All known hooks are expected to run unconditionally regardless of working
621 # copy state, so skip the SCM status check.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000622 run_scm = command not in ('runhooks', 'recurse', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000623 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000624 file_list = [] if not options.nohooks else None
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000625 if run_scm and parsed_url:
626 if isinstance(parsed_url, self.FileImpl):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000627 # Special support for single-file checkout.
628 if not command in (None, 'cleanup', 'diff', 'pack', 'status'):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000629 # Sadly, pylint doesn't realize that parsed_url is of FileImpl.
630 # pylint: disable=E1103
631 options.revision = parsed_url.GetRevision()
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000632 self._used_scm = gclient_scm.SVNWrapper(
633 parsed_url.GetPath(), self.root.root_dir, self.name)
634 self._used_scm.RunCommand('updatesingle',
635 options, args + [parsed_url.GetFilename()], file_list)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000636 else:
maruel@chromium.org9e5317a2010-08-13 20:35:11 +0000637 # Create a shallow copy to mutate revision.
638 options = copy.copy(options)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000639 options.revision = revision_overrides.get(self.name)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000640 self.maybeGetParentRevision(
641 command, options, parsed_url, self.parent.name, revision_overrides)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000642 self._used_scm = gclient_scm.CreateSCM(
643 parsed_url, self.root.root_dir, self.name)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000644 self._got_revision = self._used_scm.RunCommand(command, options, args,
645 file_list)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000646 if file_list:
647 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000648
649 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
650 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000651 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000652 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000653 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000654 continue
655 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000656 [self.root.root_dir.lower(), file_list[i].lower()])
657 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000658 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000659 while file_list[i].startswith(('\\', '/')):
660 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000661
662 # Always parse the DEPS file.
663 self.ParseDepsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000664 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000665 if command in ('update', 'revert') and not options.noprehooks:
666 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000667
668 if self.recursion_limit:
669 # Parse the dependencies of this dependency.
670 for s in self.dependencies:
671 work_queue.enqueue(s)
672
673 if command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000674 if not isinstance(parsed_url, self.FileImpl):
675 # Skip file only checkout.
676 scm = gclient_scm.GetScmName(parsed_url)
677 if not options.scm or scm in options.scm:
678 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
679 # Pass in the SCM type as an env variable
680 env = os.environ.copy()
681 if scm:
682 env['GCLIENT_SCM'] = scm
683 if parsed_url:
684 env['GCLIENT_URL'] = parsed_url
ilevy@chromium.org37116242012-11-28 01:32:48 +0000685 env['GCLIENT_DEP_PATH'] = self.name
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000686 if options.prepend_dir and scm == 'git':
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000687 print_stdout = False
688 def filter_fn(line):
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000689 """Git-specific path marshaling. It is optimized for git-grep."""
690
691 def mod_path(git_pathspec):
692 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
693 modified_path = os.path.join(self.name, match.group(2))
694 branch = match.group(1) or ''
695 return '%s%s' % (branch, modified_path)
696
697 match = re.match('^Binary file ([^\0]+) matches$', line)
698 if match:
699 print 'Binary file %s matches' % mod_path(match.group(1))
700 return
701
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000702 items = line.split('\0')
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000703 if len(items) == 2 and items[1]:
704 print '%s : %s' % (mod_path(items[0]), items[1])
705 elif len(items) >= 2:
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000706 # Multiple null bytes or a single trailing null byte indicate
707 # git is likely displaying filenames only (such as with -l)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000708 print '\n'.join(mod_path(path) for path in items if path)
709 else:
710 print line
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000711 else:
712 print_stdout = True
713 filter_fn = None
714
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000715 if parsed_url is None:
716 print >> sys.stderr, 'Skipped omitted dependency %s' % cwd
717 elif os.path.isdir(cwd):
maruel@chromium.org288054d2012-03-05 00:43:07 +0000718 try:
719 gclient_utils.CheckCallAndFilter(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000720 args, cwd=cwd, env=env, print_stdout=print_stdout,
721 filter_fn=filter_fn,
722 )
maruel@chromium.org288054d2012-03-05 00:43:07 +0000723 except subprocess2.CalledProcessError:
724 if not options.ignore:
725 raise
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000726 else:
727 print >> sys.stderr, 'Skipped missing %s' % cwd
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000728
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000729
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000730 @gclient_utils.lockedmethod
731 def _run_is_done(self, file_list, parsed_url):
732 # Both these are kept for hooks that are run as a separate tree traversal.
733 self._file_list = file_list
734 self._parsed_url = parsed_url
735 self._processed = True
736
szager@google.comb9a78d32012-03-13 18:46:21 +0000737 @staticmethod
738 def GetHookAction(hook_dict, matching_file_list):
739 """Turns a parsed 'hook' dict into an executable command."""
740 logging.debug(hook_dict)
741 logging.debug(matching_file_list)
742 command = hook_dict['action'][:]
743 if command[0] == 'python':
744 # If the hook specified "python" as the first item, the action is a
745 # Python script. Run it by starting a new copy of the same
746 # interpreter.
747 command[0] = sys.executable
748 if '$matching_files' in command:
749 splice_index = command.index('$matching_files')
750 command[splice_index:splice_index + 1] = matching_file_list
751 return command
752
753 def GetHooks(self, options):
754 """Evaluates all hooks, and return them in a flat list.
755
756 RunOnDeps() must have been called before to load the DEPS.
757 """
758 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000759 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000760 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000761 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000762 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000763 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000764 if self.deps_hooks:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000765 # TODO(maruel): If the user is using git or git-svn, then we don't know
766 # what files have changed so we always run all hooks. It'd be nice to fix
767 # that.
768 if (options.force or
769 isinstance(self.parsed_url, self.FileImpl) or
770 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000771 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000772 for hook_dict in self.deps_hooks:
szager@google.comb9a78d32012-03-13 18:46:21 +0000773 result.append(self.GetHookAction(hook_dict, []))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000774 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000775 # Run hooks on the basis of whether the files from the gclient operation
776 # match each hook's pattern.
777 for hook_dict in self.deps_hooks:
778 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000779 matching_file_list = [
780 f for f in self.file_list_and_children if pattern.search(f)
781 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000782 if matching_file_list:
szager@google.comb9a78d32012-03-13 18:46:21 +0000783 result.append(self.GetHookAction(hook_dict, matching_file_list))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000784 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000785 result.extend(s.GetHooks(options))
786 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000787
szager@google.comb9a78d32012-03-13 18:46:21 +0000788 def RunHooksRecursively(self, options):
789 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000790 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000791 for hook in self.GetHooks(options):
792 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000793 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +0000794 gclient_utils.CheckCallAndFilterAndHeader(
795 hook, cwd=self.root.root_dir, always=True)
796 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
797 # Use a discrete exit status code of 2 to indicate that a hook action
798 # failed. Users of this script may wish to treat hook action failures
799 # differently from VC failures.
800 print >> sys.stderr, 'Error: %s' % str(e)
801 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000802 finally:
803 elapsed_time = time.time() - start_time
804 if elapsed_time > 10:
805 print "Hook '%s' took %.2f secs" % (
806 gclient_utils.CommandToStr(hook), elapsed_time)
maruel@chromium.orgeaf61062010-07-07 18:42:39 +0000807
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000808 def RunPreDepsHooks(self):
809 assert self.processed
810 assert self.deps_parsed
811 assert not self.pre_deps_hooks_ran
812 assert not self.hooks_ran
813 for s in self.dependencies:
814 assert not s.processed
815 self._pre_deps_hooks_ran = True
816 for hook in self.pre_deps_hooks:
817 try:
818 start_time = time.time()
819 gclient_utils.CheckCallAndFilterAndHeader(
820 hook, cwd=self.root.root_dir, always=True)
821 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
822 # Use a discrete exit status code of 2 to indicate that a hook action
823 # failed. Users of this script may wish to treat hook action failures
824 # differently from VC failures.
825 print >> sys.stderr, 'Error: %s' % str(e)
826 sys.exit(2)
827 finally:
828 elapsed_time = time.time() - start_time
829 if elapsed_time > 10:
830 print "Hook '%s' took %.2f secs" % (
831 gclient_utils.CommandToStr(hook), elapsed_time)
832
833
maruel@chromium.org0d812442010-08-10 12:41:08 +0000834 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000835 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000836 dependencies = self.dependencies
837 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000838 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000839 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000840 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000841 for i in d.subtree(include_all):
842 yield i
843
844 def depth_first_tree(self):
845 """Depth-first recursion including the root node."""
846 yield self
847 for i in self.dependencies:
848 for j in i.depth_first_tree():
849 if j.should_process:
850 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +0000851
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000852 @gclient_utils.lockedmethod
853 def add_dependency(self, new_dep):
854 self._dependencies.append(new_dep)
855
856 @gclient_utils.lockedmethod
857 def _mark_as_parsed(self, new_hooks):
858 self._deps_hooks.extend(new_hooks)
859 self._deps_parsed = True
860
maruel@chromium.org68988972011-09-20 14:11:42 +0000861 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000862 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000863 def dependencies(self):
864 return tuple(self._dependencies)
865
866 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000867 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000868 def deps_hooks(self):
869 return tuple(self._deps_hooks)
870
871 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000872 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000873 def pre_deps_hooks(self):
874 return tuple(self._pre_deps_hooks)
875
876 @property
877 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000878 def parsed_url(self):
879 return self._parsed_url
880
881 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000882 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000883 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000884 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +0000885 return self._deps_parsed
886
887 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000888 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000889 def processed(self):
890 return self._processed
891
892 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000893 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000894 def pre_deps_hooks_ran(self):
895 return self._pre_deps_hooks_ran
896
897 @property
898 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000899 def hooks_ran(self):
900 return self._hooks_ran
901
902 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000903 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000904 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000905 return tuple(self._file_list)
906
907 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000908 def used_scm(self):
909 """SCMWrapper instance for this dependency or None if not processed yet."""
910 return self._used_scm
911
912 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000913 @gclient_utils.lockedmethod
914 def got_revision(self):
915 return self._got_revision
916
917 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000918 def file_list_and_children(self):
919 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000920 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000921 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +0000922 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000923
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000924 def __str__(self):
925 out = []
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +0000926 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +0000927 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000928 'processed', 'hooks_ran', 'deps_parsed', 'requirements'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +0000929 # First try the native property if it exists.
930 if hasattr(self, '_' + i):
931 value = getattr(self, '_' + i, False)
932 else:
933 value = getattr(self, i, False)
934 if value:
935 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000936
937 for d in self.dependencies:
938 out.extend([' ' + x for x in str(d).splitlines()])
939 out.append('')
940 return '\n'.join(out)
941
942 def __repr__(self):
943 return '%s: %s' % (self.name, self.url)
944
maruel@chromium.orgbffb9042010-07-22 20:59:36 +0000945 def hierarchy(self):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +0000946 """Returns a human-readable hierarchical reference to a Dependency."""
maruel@chromium.orgbffb9042010-07-22 20:59:36 +0000947 out = '%s(%s)' % (self.name, self.url)
948 i = self.parent
949 while i and i.name:
950 out = '%s(%s) -> %s' % (i.name, i.url, out)
951 i = i.parent
952 return out
953
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000954
955class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000956 """Object that represent a gclient checkout. A tree of Dependency(), one per
957 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000958
959 DEPS_OS_CHOICES = {
960 "win32": "win",
961 "win": "win",
962 "cygwin": "win",
963 "darwin": "mac",
964 "mac": "mac",
965 "unix": "unix",
966 "linux": "unix",
967 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +0000968 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +0000969 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000970 }
971
972 DEFAULT_CLIENT_FILE_TEXT = ("""\
973solutions = [
974 { "name" : "%(solution_name)s",
975 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +0000976 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000977 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000978 "custom_deps" : {
979 },
maruel@chromium.org73e21142010-07-05 13:32:01 +0000980 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000981 },
982]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +0000983cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000984""")
985
986 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
987 { "name" : "%(solution_name)s",
988 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +0000989 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000990 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000991 "custom_deps" : {
maruel@chromium.org73e21142010-07-05 13:32:01 +0000992%(solution_deps)s },
993 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000994 },
995""")
996
997 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
998# Snapshot generated with gclient revinfo --snapshot
999solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001000%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001001""")
1002
1003 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001004 # Do not change previous behavior. Only solution level and immediate DEPS
1005 # are processed.
1006 self._recursion_limit = 2
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001007 Dependency.__init__(self, None, None, None, None, True, None, None, None,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001008 'unused', True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001009 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001010 if options.deps_os:
1011 enforced_os = options.deps_os.split(',')
1012 else:
1013 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1014 if 'all' in enforced_os:
1015 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001016 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001017 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001018 self.config_content = None
1019
1020 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001021 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001022 config_dict = {}
1023 self.config_content = content
1024 try:
1025 exec(content, config_dict)
1026 except SyntaxError, e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001027 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001028
peter@chromium.org1efccc82012-04-27 16:34:38 +00001029 # Append any target OS that is not already being enforced to the tuple.
1030 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001031 if config_dict.get('target_os_only', False):
1032 self._enforced_os = tuple(set(target_os))
1033 else:
1034 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1035
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001036 gclient_scm.GitWrapper.cache_dir = config_dict.get('cache_dir')
1037
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001038 if not target_os and config_dict.get('target_os_only', False):
1039 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1040 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001041
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001042 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001043 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001044 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001045 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +00001046 self, s['name'], s['url'],
1047 s.get('safesync_url', None),
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001048 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001049 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001050 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001051 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001052 s.get('deps_file', 'DEPS'),
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001053 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001054 except KeyError:
1055 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1056 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001057 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1058 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001059
1060 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001061 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001062 self._options.config_filename),
1063 self.config_content)
1064
1065 @staticmethod
1066 def LoadCurrentConfig(options):
1067 """Searches for and loads a .gclient file relative to the current working
1068 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001069 if options.spec:
1070 client = GClient('.', options)
1071 client.SetConfig(options.spec)
1072 else:
1073 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1074 if not path:
1075 return None
1076 client = GClient(path, options)
1077 client.SetConfig(gclient_utils.FileRead(
1078 os.path.join(path, options.config_filename)))
maruel@chromium.org69392e72011-10-13 22:09:00 +00001079
1080 if (options.revisions and
1081 len(client.dependencies) > 1 and
1082 any('@' not in r for r in options.revisions)):
1083 print >> sys.stderr, (
1084 'You must specify the full solution name like --revision %s@%s\n'
1085 'when you have multiple solutions setup in your .gclient file.\n'
1086 'Other solutions present are: %s.') % (
1087 client.dependencies[0].name,
1088 options.revisions[0],
1089 ', '.join(s.name for s in client.dependencies[1:]))
maruel@chromium.org15804092010-09-02 17:07:37 +00001090 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001091
nsylvain@google.comefc80932011-05-31 21:27:56 +00001092 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001093 safesync_url, managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001094 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1095 'solution_name': solution_name,
1096 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001097 'deps_file': deps_file,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001098 'safesync_url' : safesync_url,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001099 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001100 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001101 })
1102
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001103 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001104 """Creates a .gclient_entries file to record the list of unique checkouts.
1105
1106 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001107 """
1108 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1109 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001110 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001111 for entry in self.root.subtree(False):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001112 # Skip over File() dependencies as we can't version them.
1113 if not isinstance(entry.parsed_url, self.FileImpl):
1114 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1115 pprint.pformat(entry.parsed_url))
1116 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001117 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001118 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001119 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001120
1121 def _ReadEntries(self):
1122 """Read the .gclient_entries file for the given client.
1123
1124 Returns:
1125 A sequence of solution names, which will be empty if there is the
1126 entries file hasn't been created yet.
1127 """
1128 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001129 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001130 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001131 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001132 try:
1133 exec(gclient_utils.FileRead(filename), scope)
1134 except SyntaxError, e:
1135 gclient_utils.SyntaxErrorToError(filename, e)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001136 return scope['entries']
1137
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001138 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001139 """Checks for revision overrides."""
1140 revision_overrides = {}
maruel@chromium.org307d1792010-05-31 20:03:13 +00001141 if self._options.head:
1142 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001143 # Do not check safesync_url if one or more --revision flag is specified.
1144 if not self._options.revisions:
1145 for s in self.dependencies:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001146 if not s.managed:
1147 self._options.revisions.append('%s@unmanaged' % s.name)
1148 elif s.safesync_url:
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001149 self._ApplySafeSyncRev(dep=s)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001150 if not self._options.revisions:
1151 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001152 solutions_names = [s.name for s in self.dependencies]
maruel@chromium.org307d1792010-05-31 20:03:13 +00001153 index = 0
1154 for revision in self._options.revisions:
1155 if not '@' in revision:
1156 # Support for --revision 123
1157 revision = '%s@%s' % (solutions_names[index], revision)
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001158 sol, rev = revision.split('@', 1)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001159 if not sol in solutions_names:
1160 #raise gclient_utils.Error('%s is not a valid solution.' % sol)
1161 print >> sys.stderr, ('Please fix your script, having invalid '
1162 '--revision flags will soon considered an error.')
1163 else:
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001164 revision_overrides[sol] = rev
maruel@chromium.org307d1792010-05-31 20:03:13 +00001165 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001166 return revision_overrides
1167
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001168 def _ApplySafeSyncRev(self, dep):
1169 """Finds a valid revision from the content of the safesync_url and apply it
1170 by appending revisions to the revision list. Throws if revision appears to
1171 be invalid for the given |dep|."""
1172 assert len(dep.safesync_url) > 0
1173 handle = urllib.urlopen(dep.safesync_url)
1174 rev = handle.read().strip()
1175 handle.close()
1176 if not rev:
1177 raise gclient_utils.Error(
1178 'It appears your safesync_url (%s) is not working properly\n'
1179 '(as it returned an empty response). Check your config.' %
1180 dep.safesync_url)
1181 scm = gclient_scm.CreateSCM(dep.url, dep.root.root_dir, dep.name)
iannucci@chromium.org4a4b33b2013-07-04 20:25:46 +00001182 safe_rev = scm.GetUsableRev(rev, self._options)
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001183 if self._options.verbose:
1184 print('Using safesync_url revision: %s.\n' % safe_rev)
1185 self._options.revisions.append('%s@%s' % (dep.name, safe_rev))
1186
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001187 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001188 """Runs a command on each dependency in a client and its dependencies.
1189
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001190 Args:
1191 command: The command to use (e.g., 'status' or 'diff')
1192 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001193 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001194 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001195 raise gclient_utils.Error('No solution specified')
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001196 revision_overrides = {}
1197 # It's unnecessary to check for revision overrides for 'recurse'.
1198 # Save a few seconds by not calling _EnforceRevisions() in that case.
dbeam@chromium.org0f8a9442012-07-10 14:50:20 +00001199 if command not in ('diff', 'recurse', 'runhooks', 'status'):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001200 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001201 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001202 # Disable progress for non-tty stdout.
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001203 if (sys.stdout.isatty() and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001204 if command in ('update', 'revert'):
1205 pm = Progress('Syncing projects', 1)
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +00001206 elif command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001207 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001208 work_queue = gclient_utils.ExecutionQueue(
1209 self._options.jobs, pm, ignore_requirements=ignore_requirements)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001210 for s in self.dependencies:
1211 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001212 work_queue.flush(revision_overrides, command, args, options=self._options)
piman@chromium.org6f363722010-04-27 00:41:09 +00001213
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001214 # Once all the dependencies have been processed, it's now safe to run the
1215 # hooks.
1216 if not self._options.nohooks:
1217 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001218
1219 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001220 # Notify the user if there is an orphaned entry in their working copy.
1221 # Only delete the directory if there are no changes in it, and
1222 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001223 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001224 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1225 for e in entries]
1226
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001227 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001228 if not prev_url:
1229 # entry must have been overridden via .gclient custom_deps
1230 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001231 # Fix path separator on Windows.
1232 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001233 e_dir = os.path.join(self.root_dir, entry_fixed)
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001234
1235 def _IsParentOfAny(parent, path_list):
1236 parent_plus_slash = parent + '/'
1237 return any(
1238 path[:len(parent_plus_slash)] == parent_plus_slash
1239 for path in path_list)
1240
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001241 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001242 if (entry not in entries and
1243 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001244 os.path.exists(e_dir)):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001245 scm = gclient_scm.CreateSCM(prev_url, self.root_dir, entry_fixed)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001246
1247 # Check to see if this directory is now part of a higher-up checkout.
1248 if scm.GetCheckoutRoot() in full_entries:
1249 logging.info('%s is part of a higher level checkout, not '
1250 'removing.', scm.GetCheckoutRoot())
1251 continue
1252
1253 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001254 scm.status(self._options, [], file_list)
1255 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001256 if (not self._options.delete_unversioned_trees or
1257 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001258 # There are modified files in this entry. Keep warning until
1259 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001260 print(('\nWARNING: \'%s\' is no longer part of this client. '
1261 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001262 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001263 else:
1264 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001265 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001266 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001267 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001268 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001269 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001270 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001271
1272 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001273 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001274 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001275 # Load all the settings.
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001276 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, None, False)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001277 for s in self.dependencies:
1278 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001279 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001280
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001281 def GetURLAndRev(dep):
1282 """Returns the revision-qualified SCM url for a Dependency."""
1283 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001284 return None
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001285 if isinstance(dep.parsed_url, self.FileImpl):
1286 original_url = dep.parsed_url.file_location
1287 else:
1288 original_url = dep.parsed_url
nasser@codeaurora.org5d63eb82010-03-24 23:22:09 +00001289 url, _ = gclient_utils.SplitUrlRevision(original_url)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001290 scm = gclient_scm.CreateSCM(original_url, self.root_dir, dep.name)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001291 if not os.path.isdir(scm.checkout_path):
1292 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001293 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001294
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001295 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001296 new_gclient = ''
1297 # First level at .gclient
1298 for d in self.dependencies:
1299 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001300 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001301 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001302 for d in dep.dependencies:
1303 entries[d.name] = GetURLAndRev(d)
1304 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001305 GrabDeps(d)
1306 custom_deps = []
1307 for k in sorted(entries.keys()):
1308 if entries[k]:
1309 # Quotes aren't escaped...
1310 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1311 else:
1312 custom_deps.append(' \"%s\": None,\n' % k)
1313 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1314 'solution_name': d.name,
1315 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001316 'deps_file': d.deps_file,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001317 'safesync_url' : d.safesync_url or '',
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001318 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001319 'solution_deps': ''.join(custom_deps),
1320 }
1321 # Print the snapshot configuration file
1322 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001323 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001324 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001325 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001326 if self._options.actual:
1327 entries[d.name] = GetURLAndRev(d)
1328 else:
1329 entries[d.name] = d.parsed_url
1330 keys = sorted(entries.keys())
1331 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001332 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001333 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001334
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001335 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001336 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001337 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001338
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001339 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001340 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001341 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001342 return self._root_dir
1343
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001344 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001345 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001346 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001347 return self._enforced_os
1348
maruel@chromium.org68988972011-09-20 14:11:42 +00001349 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001350 def recursion_limit(self):
1351 """How recursive can each dependencies in DEPS file can load DEPS file."""
1352 return self._recursion_limit
1353
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001354 @property
1355 def target_os(self):
1356 return self._enforced_os
1357
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001358
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001359#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001360
1361
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001362def CMDcleanup(parser, args):
1363 """Cleans up all working copies.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001364
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001365 Mostly svn-specific. Simply runs 'svn cleanup' for each module.
1366 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001367 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1368 help='override deps for the specified (comma-separated) '
1369 'platform(s); \'all\' will process all deps_os '
1370 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001371 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001372 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001373 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001374 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001375 if options.verbose:
1376 # Print out the .gclient file. This is longer than if we just printed the
1377 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001378 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001379 return client.RunOnDeps('cleanup', args)
1380
1381
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001382@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001383def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001384 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001385
1386 Runs a shell command on all entries.
ilevy@chromium.org37116242012-11-28 01:32:48 +00001387 Sets GCLIENT_DEP_PATH enviroment variable as the dep's relative location to
1388 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001389 """
1390 # Stop parsing at the first non-arg so that these go through to the command
1391 parser.disable_interspersed_args()
1392 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001393 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001394 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001395 help='Ignore non-zero return codes from subcommands.')
1396 parser.add_option('--prepend-dir', action='store_true',
1397 help='Prepend relative dir for use with git <cmd> --null.')
1398 parser.add_option('--no-progress', action='store_true',
1399 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001400 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001401 if not args:
1402 print >> sys.stderr, 'Need to supply a command!'
1403 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001404 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1405 if not root_and_entries:
1406 print >> sys.stderr, (
1407 'You need to run gclient sync at least once to use \'recurse\'.\n'
1408 'This is because .gclient_entries needs to exist and be up to date.')
1409 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001410
1411 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001412 scm_set = set()
1413 for scm in options.scm:
1414 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001415 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001416
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001417 options.nohooks = True
1418 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001419 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1420 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001421
1422
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001423@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001424def CMDfetch(parser, args):
1425 """Fetches upstream commits for all modules.
1426
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001427 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1428 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001429 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001430 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001431 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1432
1433
1434def CMDgrep(parser, args):
1435 """Greps through git repos managed by gclient.
1436
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001437 Runs 'git grep [args...]' for each module.
1438 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001439 # We can't use optparse because it will try to parse arguments sent
1440 # to git grep and throw an error. :-(
1441 if not args or re.match('(-h|--help)$', args[0]):
1442 print >> sys.stderr, (
1443 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1444 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1445 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1446 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
1447 ' end of your query.'
1448 )
1449 return 1
1450
1451 jobs_arg = ['--jobs=1']
1452 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1453 jobs_arg, args = args[:1], args[1:]
1454 elif re.match(r'(-j|--jobs)$', args[0]):
1455 jobs_arg, args = args[:2], args[2:]
1456
1457 return CMDrecurse(
1458 parser,
1459 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1460 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001461
1462
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001463@subcommand.usage('[url] [safesync url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001464def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001465 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001466
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001467 This specifies the configuration for further commands. After update/sync,
1468 top-level DEPS files in each module are read to determine dependent
1469 modules to operate on as well. If optional [url] parameter is
1470 provided, then configuration is read from a specified Subversion server
1471 URL.
1472 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00001473 # We do a little dance with the --gclientfile option. 'gclient config' is the
1474 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1475 # arguments. So, we temporarily stash any --gclientfile parameter into
1476 # options.output_config_file until after the (gclientfile xor spec) error
1477 # check.
1478 parser.remove_option('--gclientfile')
1479 parser.add_option('--gclientfile', dest='output_config_file',
1480 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001481 parser.add_option('--name',
1482 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001483 parser.add_option('--deps-file', default='DEPS',
1484 help='overrides the default name for the DEPS file for the'
1485 'main solutions and all sub-dependencies')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001486 parser.add_option('--unmanaged', action='store_true', default=False,
1487 help='overrides the default behavior to make it possible '
1488 'to have the main solution untouched by gclient '
1489 '(gclient will check out unmanaged dependencies but '
1490 'will never sync them)')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001491 parser.add_option('--git-deps', action='store_true',
1492 help='sets the deps file to ".DEPS.git" instead of "DEPS"')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001493 parser.add_option('--cache-dir',
1494 help='(git only) Cache all git repos into this dir and do '
1495 'shared clones from the cache, instead of cloning '
1496 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001497 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001498 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001499 if options.output_config_file:
1500 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001501 if ((options.spec and args) or len(args) > 2 or
1502 (not options.spec and not args)):
1503 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1504
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001505 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001506 if options.spec:
1507 client.SetConfig(options.spec)
1508 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001509 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001510 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001511 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001512 if name.endswith('.git'):
1513 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001514 else:
1515 # specify an alternate relpath for the given URL.
1516 name = options.name
nsylvain@google.comefc80932011-05-31 21:27:56 +00001517 deps_file = options.deps_file
1518 if options.git_deps:
1519 deps_file = '.DEPS.git'
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001520 safesync_url = ''
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001521 if len(args) > 1:
1522 safesync_url = args[1]
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001523 client.SetDefaultConfig(name, deps_file, base_url, safesync_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001524 managed=not options.unmanaged,
1525 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001526 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001527 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001528
1529
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001530@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001531 gclient pack > patch.txt
1532 generate simple patch for configured client and dependences
1533""")
1534def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001535 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001536
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001537 Internally, runs 'svn diff'/'git diff' on each checked out module and
1538 dependencies, and performs minimal postprocessing of the output. The
1539 resulting patch is printed to stdout and can be applied to a freshly
1540 checked out tree via 'patch -p0 < patchfile'.
1541 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001542 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1543 help='override deps for the specified (comma-separated) '
1544 'platform(s); \'all\' will process all deps_os '
1545 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001546 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001547 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001548 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001549 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001550 client = GClient.LoadCurrentConfig(options)
1551 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001552 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001553 if options.verbose:
1554 # Print out the .gclient file. This is longer than if we just printed the
1555 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001556 print(client.config_content)
kbr@google.comab318592009-09-04 00:54:55 +00001557 return client.RunOnDeps('pack', args)
1558
1559
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001560def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001561 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001562 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1563 help='override deps for the specified (comma-separated) '
1564 'platform(s); \'all\' will process all deps_os '
1565 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001566 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001567 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001568 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001569 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001570 if options.verbose:
1571 # Print out the .gclient file. This is longer than if we just printed the
1572 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001573 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001574 return client.RunOnDeps('status', args)
1575
1576
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001577@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001578 gclient sync
1579 update files from SCM according to current configuration,
1580 *for modules which have changed since last update or sync*
1581 gclient sync --force
1582 update files from SCM according to current configuration, for
1583 all modules (useful for recovering files deleted from local copy)
1584 gclient sync --revision src@31000
1585 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001586
1587JSON output format:
1588If the --output-json option is specified, the following document structure will
1589be emitted to the provided file. 'null' entries may occur for subprojects which
1590are present in the gclient solution, but were not processed (due to custom_deps,
1591os_deps, etc.)
1592
1593{
1594 "solutions" : {
1595 "<name>": { # <name> is the posix-normalized path to the solution.
1596 "revision": [<svn rev int>|<git id hex string>|null],
1597 "scm": ["svn"|"git"|null],
1598 }
1599 }
1600}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001601""")
1602def CMDsync(parser, args):
1603 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001604 parser.add_option('-f', '--force', action='store_true',
1605 help='force update even for unchanged modules')
1606 parser.add_option('-n', '--nohooks', action='store_true',
1607 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001608 parser.add_option('-p', '--noprehooks', action='store_true',
1609 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001610 parser.add_option('-r', '--revision', action='append',
1611 dest='revisions', metavar='REV', default=[],
1612 help='Enforces revision/hash for the solutions with the '
1613 'format src@rev. The src@ part is optional and can be '
1614 'skipped. -r can be used multiple times when .gclient '
1615 'has multiple solutions configured and will work even '
joi@chromium.org792ea882010-11-10 02:37:27 +00001616 'if the src@ part is skipped. Note that specifying '
1617 '--revision means your safesync_url gets ignored.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00001618 parser.add_option('--with_branch_heads', action='store_true',
1619 help='Clone git "branch_heads" refspecs in addition to '
1620 'the default refspecs. This adds about 1/2GB to a '
1621 'full checkout. (git only)')
floitsch@google.comeaab7842011-04-28 09:07:58 +00001622 parser.add_option('-t', '--transitive', action='store_true',
1623 help='When a revision is specified (in the DEPS file or '
1624 'with the command-line flag), transitively update '
1625 'the dependencies to the date of the given revision. '
1626 'Only supported for SVN repositories.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001627 parser.add_option('-H', '--head', action='store_true',
1628 help='skips any safesync_urls specified in '
1629 'configured solutions and sync to head instead')
1630 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001631 help='Deletes from the working copy any dependencies that '
1632 'have been removed since the last sync, as long as '
1633 'there are no local modifications. When used with '
1634 '--force, such dependencies are removed even if they '
1635 'have local modifications. When used with --reset, '
1636 'all untracked directories are removed from the '
1637 'working copy, exclusing those which are explicitly '
1638 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001639 parser.add_option('-R', '--reset', action='store_true',
1640 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00001641 parser.add_option('-M', '--merge', action='store_true',
1642 help='merge upstream changes instead of trying to '
1643 'fast-forward or rebase')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001644 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1645 help='override deps for the specified (comma-separated) '
1646 'platform(s); \'all\' will process all deps_os '
1647 'references')
1648 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
1649 help='Skip svn up whenever possible by requesting '
1650 'actual HEAD revision from the repository')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001651 parser.add_option('--upstream', action='store_true',
1652 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001653 parser.add_option('--output-json',
1654 help='Output a json document to this path containing '
1655 'summary information about the sync.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001656 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001657 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001658
1659 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001660 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001661
maruel@chromium.org307d1792010-05-31 20:03:13 +00001662 if options.revisions and options.head:
1663 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001664 print('Warning: you cannot use both --head and --revision')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001665
1666 if options.verbose:
1667 # Print out the .gclient file. This is longer than if we just printed the
1668 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001669 print(client.config_content)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001670 ret = client.RunOnDeps('update', args)
1671 if options.output_json:
1672 slns = {}
1673 for d in client.subtree(True):
1674 normed = d.name.replace('\\', '/').rstrip('/') + '/'
1675 slns[normed] = {
1676 'revision': d.got_revision,
1677 'scm': d.used_scm.name if d.used_scm else None,
1678 }
1679 with open(options.output_json, 'wb') as f:
1680 json.dump({'solutions': slns}, f)
1681 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001682
1683
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001684CMDupdate = CMDsync
1685
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001686
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001687def CMDdiff(parser, args):
1688 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001689 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1690 help='override deps for the specified (comma-separated) '
1691 'platform(s); \'all\' will process all deps_os '
1692 'references')
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 if options.verbose:
1698 # Print out the .gclient file. This is longer than if we just printed the
1699 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001700 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001701 return client.RunOnDeps('diff', args)
1702
1703
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001704def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001705 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001706
1707 That's the nuclear option to get back to a 'clean' state. It removes anything
1708 that shows up in svn status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001709 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1710 help='override deps for the specified (comma-separated) '
1711 'platform(s); \'all\' will process all deps_os '
1712 'references')
1713 parser.add_option('-n', '--nohooks', action='store_true',
1714 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001715 parser.add_option('-p', '--noprehooks', action='store_true',
1716 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001717 parser.add_option('--upstream', action='store_true',
1718 help='Make repo state match upstream branch.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001719 (options, args) = parser.parse_args(args)
1720 # --force is implied.
1721 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001722 options.reset = False
1723 options.delete_unversioned_trees = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001724 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001725 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001726 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001727 return client.RunOnDeps('revert', args)
1728
1729
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001730def CMDrunhooks(parser, args):
1731 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001732 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1733 help='override deps for the specified (comma-separated) '
1734 'platform(s); \'all\' will process all deps_os '
1735 'references')
1736 parser.add_option('-f', '--force', action='store_true', default=True,
1737 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001738 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001739 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001740 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001741 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001742 if options.verbose:
1743 # Print out the .gclient file. This is longer than if we just printed the
1744 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001745 print(client.config_content)
maruel@chromium.org5df6a462009-08-28 18:52:26 +00001746 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001747 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001748 return client.RunOnDeps('runhooks', args)
1749
1750
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001751def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001752 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001753
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001754 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001755 can be used to reproduce the same tree in the future. It is only useful for
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001756 'unpinned dependencies', i.e. DEPS/deps references without a svn revision
1757 number or a git hash. A git branch name isn't 'pinned' since the actual
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001758 commit can change.
1759 """
1760 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1761 help='override deps for the specified (comma-separated) '
1762 'platform(s); \'all\' will process all deps_os '
1763 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001764 parser.add_option('-a', '--actual', action='store_true',
1765 help='gets the actual checked out revisions instead of the '
1766 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001767 parser.add_option('-s', '--snapshot', action='store_true',
1768 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001769 'version of all repositories to reproduce the tree, '
1770 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001771 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001772 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001773 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001774 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001775 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001776 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001777
1778
szager@google.comb9a78d32012-03-13 18:46:21 +00001779def CMDhookinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001780 """Outputs the hooks that would be run by `gclient runhooks`."""
szager@google.comb9a78d32012-03-13 18:46:21 +00001781 (options, args) = parser.parse_args(args)
1782 options.force = True
1783 client = GClient.LoadCurrentConfig(options)
1784 if not client:
1785 raise gclient_utils.Error('client not configured; see \'gclient config\'')
1786 client.RunOnDeps(None, [])
1787 print '; '.join(' '.join(hook) for hook in client.GetHooks(options))
1788 return 0
1789
1790
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001791class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00001792 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001793
1794 def __init__(self, **kwargs):
1795 optparse.OptionParser.__init__(
1796 self, version='%prog ' + __version__, **kwargs)
1797
1798 # Some arm boards have issues with parallel sync.
1799 if platform.machine().startswith('arm'):
1800 jobs = 1
1801 else:
1802 jobs = max(8, gclient_utils.NumLocalCpus())
1803 # cmp: 2013/06/19
1804 # Temporary workaround to lower bot-load on SVN server.
1805 if os.environ.get('CHROME_HEADLESS') == '1':
xusydoc@chromium.org05028412013-07-29 13:40:10 +00001806 jobs = 1
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001807
1808 self.add_option(
1809 '-j', '--jobs', default=jobs, type='int',
1810 help='Specify how many SCM commands can run in parallel; defaults to '
1811 'number of cpu cores (%default)')
1812 self.add_option(
1813 '-v', '--verbose', action='count', default=0,
1814 help='Produces additional output for diagnostics. Can be used up to '
1815 'three times for more logging info.')
1816 self.add_option(
1817 '--gclientfile', dest='config_filename',
1818 help='Specify an alternate %s file' % self.gclientfile_default)
1819 self.add_option(
1820 '--spec',
1821 help='create a gclient file containing the provided string. Due to '
1822 'Cygwin/Python brokenness, it can\'t contain any newlines.')
1823 self.add_option(
1824 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00001825 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001826
1827 def parse_args(self, args=None, values=None):
1828 """Integrates standard options processing."""
1829 options, args = optparse.OptionParser.parse_args(self, args, values)
1830 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
1831 logging.basicConfig(
1832 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00001833 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001834 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001835 self.error('Cannot specifiy both --gclientfile and --spec')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001836 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001837 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00001838 options.entries_filename = options.config_filename + '_entries'
1839 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001840 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00001841
1842 # These hacks need to die.
1843 if not hasattr(options, 'revisions'):
1844 # GClient.RunOnDeps expects it even if not applicable.
1845 options.revisions = []
1846 if not hasattr(options, 'head'):
1847 options.head = None
1848 if not hasattr(options, 'nohooks'):
1849 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001850 if not hasattr(options, 'noprehooks'):
1851 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00001852 if not hasattr(options, 'deps_os'):
1853 options.deps_os = None
1854 if not hasattr(options, 'manually_grab_svn_rev'):
1855 options.manually_grab_svn_rev = None
1856 if not hasattr(options, 'force'):
1857 options.force = None
1858 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001859
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001860
1861def disable_buffering():
1862 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
1863 # operations. Python as a strong tendency to buffer sys.stdout.
1864 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
1865 # Make stdout annotated with the thread ids.
1866 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00001867
1868
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001869def Main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001870 """Doesn't parse the arguments here, just find the right subcommand to
1871 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00001872 if sys.hexversion < 0x02060000:
maruel@chromium.orgc3a15a22010-11-20 03:12:27 +00001873 print >> sys.stderr, (
maruel@chromium.org82798cb2012-02-23 18:16:12 +00001874 '\nYour python version %s is unsupported, please upgrade.\n' %
1875 sys.version.split(' ', 1)[0])
1876 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00001877 if not sys.executable:
1878 print >> sys.stderr, (
1879 '\nPython cannot find the location of it\'s own executable.\n')
1880 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001881 fix_encoding.fix_encoding()
1882 disable_buffering()
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +00001883 colorama.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001884 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001885 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001886 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00001887 except KeyboardInterrupt:
1888 gclient_utils.GClientChildren.KillAllRemainingChildren()
1889 raise
maruel@chromium.org31cb48a2011-04-04 18:01:36 +00001890 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001891 print >> sys.stderr, 'Error: %s' % str(e)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001892 return 1
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001893
1894
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001895if '__main__' == __name__:
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001896 sys.exit(Main(sys.argv[1:]))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001897
1898# vim: ts=2:sw=2:tw=80:et: