blob: 5c5c5ca5f57f4ae3cb692e8fa2c7f51167fedd43 [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.
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +000063#
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
szager@chromium.org848fd492014-04-09 19:06:44 +0000100import git_cache
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000101from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000102import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000103import subprocess2
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +0000104from third_party import colorama
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000105
106
maruel@chromium.org116704f2010-06-11 17:34:38 +0000107class GClientKeywords(object):
108 class FromImpl(object):
109 """Used to implement the From() syntax."""
110
111 def __init__(self, module_name, sub_target_name=None):
112 """module_name is the dep module we want to include from. It can also be
113 the name of a subdirectory to include from.
114
115 sub_target_name is an optional parameter if the module name in the other
116 DEPS file is different. E.g., you might want to map src/net to net."""
117 self.module_name = module_name
118 self.sub_target_name = sub_target_name
119
120 def __str__(self):
121 return 'From(%s, %s)' % (repr(self.module_name),
122 repr(self.sub_target_name))
123
maruel@chromium.org116704f2010-06-11 17:34:38 +0000124 class FileImpl(object):
125 """Used to implement the File('') syntax which lets you sync a single file
maruel@chromium.orge3216c62010-07-08 03:31:43 +0000126 from a SVN repo."""
maruel@chromium.org116704f2010-06-11 17:34:38 +0000127
128 def __init__(self, file_location):
129 self.file_location = file_location
130
131 def __str__(self):
132 return 'File("%s")' % self.file_location
133
134 def GetPath(self):
135 return os.path.split(self.file_location)[0]
136
137 def GetFilename(self):
138 rev_tokens = self.file_location.split('@')
139 return os.path.split(rev_tokens[0])[1]
140
141 def GetRevision(self):
142 rev_tokens = self.file_location.split('@')
143 if len(rev_tokens) > 1:
144 return rev_tokens[1]
145 return None
146
147 class VarImpl(object):
148 def __init__(self, custom_vars, local_scope):
149 self._custom_vars = custom_vars
150 self._local_scope = local_scope
151
152 def Lookup(self, var_name):
153 """Implements the Var syntax."""
154 if var_name in self._custom_vars:
155 return self._custom_vars[var_name]
156 elif var_name in self._local_scope.get("vars", {}):
157 return self._local_scope["vars"][var_name]
158 raise gclient_utils.Error("Var is not defined: %s" % var_name)
159
160
maruel@chromium.org064186c2011-09-27 23:53:33 +0000161class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000162 """Immutable configuration settings."""
163 def __init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000164 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000165 custom_hooks, deps_file, should_process):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000166 GClientKeywords.__init__(self)
167
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000168 # These are not mutable:
169 self._parent = parent
170 self._safesync_url = safesync_url
171 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000172 self._url = url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000173 # 'managed' determines whether or not this dependency is synced/updated by
174 # gclient after gclient checks it out initially. The difference between
175 # 'managed' and 'should_process' is that the user specifies 'managed' via
176 # the --unmanaged command-line flag or a .gclient config, where
177 # 'should_process' is dynamically set by gclient if it goes over its
178 # recursion limit and controls gclient's behavior so it does not misbehave.
179 self._managed = managed
180 self._should_process = should_process
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000181 # This is a mutable value that overrides the normal recursion limit for this
182 # dependency. It is read from the actual DEPS file so cannot be set on
183 # class instantiation.
184 self.recursion_override = None
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000185 # This is a mutable value which has the list of 'target_os' OSes listed in
186 # the current deps file.
187 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000188
189 # These are only set in .gclient and not in DEPS files.
190 self._custom_vars = custom_vars or {}
191 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000192 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000193
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000194 # TODO(iannucci): Remove this when all masters are correctly substituting
195 # the new blink url.
196 if (self._custom_vars.get('webkit_trunk', '') ==
197 'svn://svn-mirror.golo.chromium.org/webkit-readonly/trunk'):
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000198 new_url = 'svn://svn-mirror.golo.chromium.org/blink/trunk'
199 print 'Overwriting Var("webkit_trunk") with %s' % new_url
200 self._custom_vars['webkit_trunk'] = new_url
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000201
maruel@chromium.org064186c2011-09-27 23:53:33 +0000202 # Post process the url to remove trailing slashes.
203 if isinstance(self._url, basestring):
204 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
205 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000206 self._url = self._url.replace('/@', '@')
207 elif not isinstance(self._url,
208 (self.FromImpl, self.FileImpl, None.__class__)):
209 raise gclient_utils.Error(
210 ('dependency url must be either a string, None, '
211 'File() or From() instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000212 # Make any deps_file path platform-appropriate.
213 for sep in ['/', '\\']:
214 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000215
216 @property
217 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000218 return self._deps_file
219
220 @property
221 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000222 return self._managed
223
224 @property
225 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000226 return self._parent
227
228 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000229 def root(self):
230 """Returns the root node, a GClient object."""
231 if not self.parent:
232 # This line is to signal pylint that it could be a GClient instance.
233 return self or GClient(None, None)
234 return self.parent.root
235
236 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000237 def safesync_url(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000238 return self._safesync_url
239
240 @property
241 def should_process(self):
242 """True if this dependency should be processed, i.e. checked out."""
243 return self._should_process
244
245 @property
246 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000247 return self._custom_vars.copy()
248
249 @property
250 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000251 return self._custom_deps.copy()
252
maruel@chromium.org064186c2011-09-27 23:53:33 +0000253 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000254 def custom_hooks(self):
255 return self._custom_hooks[:]
256
257 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000258 def url(self):
259 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000260
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000261 @property
262 def recursion_limit(self):
263 """Returns > 0 if this dependency is not too recursed to be processed."""
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000264 if self.recursion_override is not None:
265 return self.recursion_override
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000266 return max(self.parent.recursion_limit - 1, 0)
267
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000268 @property
269 def target_os(self):
270 if self.local_target_os is not None:
271 return tuple(set(self.local_target_os).union(self.parent.target_os))
272 else:
273 return self.parent.target_os
274
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000275 def get_custom_deps(self, name, url):
276 """Returns a custom deps if applicable."""
277 if self.parent:
278 url = self.parent.get_custom_deps(name, url)
279 # None is a valid return value to disable a dependency.
280 return self.custom_deps.get(name, url)
281
maruel@chromium.org064186c2011-09-27 23:53:33 +0000282
283class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000284 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000285
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000286 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000287 custom_vars, custom_hooks, deps_file, should_process):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000288 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000289 DependencySettings.__init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000290 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000291 custom_hooks, deps_file, should_process)
maruel@chromium.org68988972011-09-20 14:11:42 +0000292
293 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000294 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000295
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000296 self._pre_deps_hooks = []
297
maruel@chromium.org68988972011-09-20 14:11:42 +0000298 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000299 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000300 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000301 # A cache of the files affected by the current operation, necessary for
302 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000303 self._file_list = []
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000304 # If it is not set to True, the dependency wasn't processed for its child
305 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000306 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000307 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000308 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000309 # This dependency had its pre-DEPS hooks run
310 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000311 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000312 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000313 # This is the scm used to checkout self.url. It may be used by dependencies
314 # to get the datetime of the revision we checked out.
315 self._used_scm = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000316 # The actual revision we ended up getting, or None if that information is
317 # unavailable
318 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000319
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000320 if not self.name and self.parent:
321 raise gclient_utils.Error('Dependency without name')
322
maruel@chromium.org470b5432011-10-11 18:18:19 +0000323 @property
324 def requirements(self):
325 """Calculate the list of requirements."""
326 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000327 # self.parent is implicitly a requirement. This will be recursive by
328 # definition.
329 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000330 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000331
332 # For a tree with at least 2 levels*, the leaf node needs to depend
333 # on the level higher up in an orderly way.
334 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
335 # thus unsorted, while the .gclient format is a list thus sorted.
336 #
337 # * _recursion_limit is hard coded 2 and there is no hope to change this
338 # value.
339 #
340 # Interestingly enough, the following condition only works in the case we
341 # want: self is a 2nd level node. 3nd level node wouldn't need this since
342 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000343 if self.parent and self.parent.parent and not self.parent.parent.parent:
344 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000345
346 if isinstance(self.url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000347 requirements.add(self.url.module_name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000348
maruel@chromium.org470b5432011-10-11 18:18:19 +0000349 if self.name:
350 requirements |= set(
351 obj.name for obj in self.root.subtree(False)
352 if (obj is not self
353 and obj.name and
354 self.name.startswith(posixpath.join(obj.name, ''))))
355 requirements = tuple(sorted(requirements))
356 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
357 return requirements
358
359 def verify_validity(self):
360 """Verifies that this Dependency is fine to add as a child of another one.
361
362 Returns True if this entry should be added, False if it is a duplicate of
363 another entry.
364 """
365 logging.info('Dependency(%s).verify_validity()' % self.name)
366 if self.name in [s.name for s in self.parent.dependencies]:
367 raise gclient_utils.Error(
368 'The same name "%s" appears multiple times in the deps section' %
369 self.name)
370 if not self.should_process:
371 # Return early, no need to set requirements.
372 return True
373
374 # This require a full tree traversal with locks.
375 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
376 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000377 self_url = self.LateOverride(self.url)
378 sibling_url = sibling.LateOverride(sibling.url)
379 # Allow to have only one to be None or ''.
380 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000381 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000382 ('Dependency %s specified more than once:\n'
383 ' %s [%s]\n'
384 'vs\n'
385 ' %s [%s]') % (
386 self.name,
387 sibling.hierarchy(),
388 sibling_url,
389 self.hierarchy(),
390 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000391 # In theory we could keep it as a shadow of the other one. In
392 # practice, simply ignore it.
393 logging.warn('Won\'t process duplicate dependency %s' % sibling)
394 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000395 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000396
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000397 def LateOverride(self, url):
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000398 """Resolves the parsed url from url.
399
400 Manages From() keyword accordingly. Do not touch self.parsed_url nor
401 self.url because it may called with other urls due to From()."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000402 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000403 parsed_url = self.get_custom_deps(self.name, url)
404 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000405 logging.info(
406 'Dependency(%s).LateOverride(%s) -> %s' %
407 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000408 return parsed_url
409
410 if isinstance(url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000411 # Requires tree traversal.
maruel@chromium.org68988972011-09-20 14:11:42 +0000412 ref = [
413 dep for dep in self.root.subtree(True) if url.module_name == dep.name
414 ]
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000415 if not ref:
416 raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
417 url.module_name, ref))
418 # It may happen that len(ref) > 1 but it's no big deal.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000419 ref = ref[0]
maruel@chromium.org98d05fa2010-07-22 21:58:01 +0000420 sub_target = url.sub_target_name or self.name
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000421 found_deps = [d for d in ref.dependencies if d.name == sub_target]
422 if len(found_deps) != 1:
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000423 raise gclient_utils.Error(
maruel@chromium.org98023df2011-09-07 18:44:47 +0000424 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
425 sub_target, ref.name, self.name, self.parent.name,
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000426 str(self.root)))
maruel@chromium.org98023df2011-09-07 18:44:47 +0000427
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000428 # Call LateOverride() again.
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000429 found_dep = found_deps[0]
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000430 parsed_url = found_dep.LateOverride(found_dep.url)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000431 logging.info(
maruel@chromium.org470b5432011-10-11 18:18:19 +0000432 'Dependency(%s).LateOverride(%s) -> %s (From)' %
433 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000434 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000435
436 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000437 parsed_url = urlparse.urlparse(url)
438 if not parsed_url[0]:
439 # A relative url. Fetch the real base.
440 path = parsed_url[2]
441 if not path.startswith('/'):
442 raise gclient_utils.Error(
443 'relative DEPS entry \'%s\' must begin with a slash' % url)
444 # Create a scm just to query the full url.
445 parent_url = self.parent.parsed_url
446 if isinstance(parent_url, self.FileImpl):
447 parent_url = parent_url.file_location
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000448 scm = gclient_scm.CreateSCM(
449 parent_url, self.root.root_dir, None, self.outbuf)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000450 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000451 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000452 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000453 logging.info(
454 'Dependency(%s).LateOverride(%s) -> %s' %
455 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000456 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000457
458 if isinstance(url, self.FileImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000459 logging.info(
460 'Dependency(%s).LateOverride(%s) -> %s (File)' %
461 (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000462 return url
463
464 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000465 logging.info(
466 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000467 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000468
469 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000470
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000471 @staticmethod
472 def MergeWithOsDeps(deps, deps_os, target_os_list):
473 """Returns a new "deps" structure that is the deps sent in updated
474 with information from deps_os (the deps_os section of the DEPS
475 file) that matches the list of target os."""
476 os_overrides = {}
477 for the_target_os in target_os_list:
478 the_target_os_deps = deps_os.get(the_target_os, {})
479 for os_dep_key, os_dep_value in the_target_os_deps.iteritems():
480 overrides = os_overrides.setdefault(os_dep_key, [])
481 overrides.append((the_target_os, os_dep_value))
482
483 # If any os didn't specify a value (we have fewer value entries
484 # than in the os list), then it wants to use the default value.
485 for os_dep_key, os_dep_value in os_overrides.iteritems():
486 if len(os_dep_value) != len(target_os_list):
487 # Record the default value too so that we don't accidently
488 # set it to None or miss a conflicting DEPS.
489 if os_dep_key in deps:
490 os_dep_value.append(('default', deps[os_dep_key]))
491
492 target_os_deps = {}
493 for os_dep_key, os_dep_value in os_overrides.iteritems():
494 # os_dep_value is a list of (os, value) pairs.
495 possible_values = set(x[1] for x in os_dep_value if x[1] is not None)
496 if not possible_values:
497 target_os_deps[os_dep_key] = None
498 else:
499 if len(possible_values) > 1:
500 # It would be possible to abort here but it would be
501 # unfortunate if we end up preventing any kind of checkout.
502 logging.error('Conflicting dependencies for %s: %s. (target_os=%s)',
503 os_dep_key, os_dep_value, target_os_list)
504 # Sorting to get the same result every time in case of conflicts.
505 target_os_deps[os_dep_key] = sorted(possible_values)[0]
506
507 new_deps = deps.copy()
508 new_deps.update(target_os_deps)
509 return new_deps
510
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000511 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000512 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000513 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000514 assert not self.dependencies
515 # One thing is unintuitive, vars = {} must happen before Var() use.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000516 local_scope = {}
517 var = self.VarImpl(self.custom_vars, local_scope)
518 global_scope = {
519 'File': self.FileImpl,
520 'From': self.FromImpl,
521 'Var': var.Lookup,
522 'deps_os': {},
523 }
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000524 filepath = os.path.join(self.root.root_dir, self.name, self.deps_file)
maruel@chromium.org46304292010-10-28 11:42:00 +0000525 if not os.path.isfile(filepath):
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000526 logging.info(
527 'ParseDepsFile(%s): No %s file found at %s' % (
528 self.name, self.deps_file, filepath))
maruel@chromium.org46304292010-10-28 11:42:00 +0000529 else:
530 deps_content = gclient_utils.FileRead(filepath)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000531 logging.debug('ParseDepsFile(%s) read:\n%s' % (self.name, deps_content))
maruel@chromium.org46304292010-10-28 11:42:00 +0000532 # Eval the content.
533 try:
534 exec(deps_content, global_scope, local_scope)
535 except SyntaxError, e:
536 gclient_utils.SyntaxErrorToError(filepath, e)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000537 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000538 if 'recursion' in local_scope:
539 self.recursion_override = local_scope.get('recursion')
540 logging.warning(
541 'Setting %s recursion to %d.', self.name, self.recursion_limit)
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000542 # If present, save 'target_os' in the local_target_os property.
543 if 'target_os' in local_scope:
544 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000545 # load os specific dependencies if defined. these dependencies may
546 # override or extend the values defined by the 'deps' member.
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000547 target_os_list = self.target_os
548 if 'deps_os' in local_scope and target_os_list:
549 deps = self.MergeWithOsDeps(deps, local_scope['deps_os'], target_os_list)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000550
maruel@chromium.org271375b2010-06-23 19:17:38 +0000551 # If a line is in custom_deps, but not in the solution, we want to append
552 # this line to the solution.
553 for d in self.custom_deps:
554 if d not in deps:
555 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000556
557 # If use_relative_paths is set in the DEPS file, regenerate
558 # the dictionary using paths relative to the directory containing
559 # the DEPS file.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000560 use_relative_paths = local_scope.get('use_relative_paths', False)
561 if use_relative_paths:
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000562 rel_deps = {}
563 for d, url in deps.items():
564 # normpath is required to allow DEPS to use .. in their
565 # dependency local path.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000566 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url
567 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000568
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000569 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000570 deps_to_add = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000571 for name, url in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000572 should_process = self.recursion_limit and self.should_process
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000573 deps_to_add.append(Dependency(
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000574 self, name, url, None, None, None, None, None,
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000575 self.deps_file, should_process))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000576 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000577
578 # override named sets of hooks by the custom hooks
579 hooks_to_run = []
580 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
581 for hook in local_scope.get('hooks', []):
582 if hook.get('name', '') not in hook_names_to_suppress:
583 hooks_to_run.append(hook)
584
585 # add the replacements and any additions
586 for hook in self.custom_hooks:
587 if 'action' in hook:
588 hooks_to_run.append(hook)
589
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000590 self._pre_deps_hooks = [self.GetHookAction(hook, []) for hook in
591 local_scope.get('pre_deps_hooks', [])]
592
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000593 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000594 logging.info('ParseDepsFile(%s) done' % self.name)
595
596 def add_dependencies_and_close(self, deps_to_add, hooks):
597 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000598 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000599 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000600 self.add_dependency(dep)
601 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000602
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000603 def maybeGetParentRevision(
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000604 self, command, options, parsed_url, parent_name, revision_overrides):
605 """Uses revision/timestamp of parent if no explicit revision was specified.
606
607 If we are performing an update and --transitive is set, use
608 - the parent's revision if 'self.url' is in the same repository
609 - the parent's timestamp otherwise
610 to update 'self.url'. The used revision/timestamp will be set in
611 'options.revision'.
612 If we have an explicit revision do nothing.
613 """
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000614 if command == 'update' and options.transitive and not options.revision:
615 _, revision = gclient_utils.SplitUrlRevision(parsed_url)
616 if not revision:
617 options.revision = revision_overrides.get(parent_name)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000618 if (options.revision and
619 not gclient_utils.IsDateRevision(options.revision)):
620 assert self.parent and self.parent.used_scm
621 # If this dependency is in the same repository as parent it's url will
622 # start with a slash. If so we take the parent revision instead of
623 # it's timestamp.
624 # (The timestamps of commits in google code are broken -- which can
625 # result in dependencies to be checked out at the wrong revision)
626 if self.url.startswith('/'):
627 if options.verbose:
628 print('Using parent\'s revision %s since we are in the same '
629 'repository.' % options.revision)
630 else:
631 parent_revision_date = self.parent.used_scm.GetRevisionDate(
632 options.revision)
633 options.revision = gclient_utils.MakeDateRevision(
634 parent_revision_date)
635 if options.verbose:
636 print('Using parent\'s revision date %s since we are in a '
637 'different repository.' % options.revision)
638 revision_overrides[self.name] = options.revision
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000639
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000640 # Arguments number differs from overridden method
641 # pylint: disable=W0221
maruel@chromium.org3742c842010-09-09 19:27:14 +0000642 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000643 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000644 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000645 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000646 if not self.should_process:
647 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000648 # When running runhooks, there's no need to consult the SCM.
649 # All known hooks are expected to run unconditionally regardless of working
650 # copy state, so skip the SCM status check.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000651 run_scm = command not in ('runhooks', 'recurse', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000652 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000653 file_list = [] if not options.nohooks else None
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000654 if run_scm and parsed_url:
655 if isinstance(parsed_url, self.FileImpl):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000656 # Special support for single-file checkout.
657 if not command in (None, 'cleanup', 'diff', 'pack', 'status'):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000658 # Sadly, pylint doesn't realize that parsed_url is of FileImpl.
659 # pylint: disable=E1103
660 options.revision = parsed_url.GetRevision()
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000661 self._used_scm = gclient_scm.SVNWrapper(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000662 parsed_url.GetPath(), self.root.root_dir, self.name,
663 out_cb=work_queue.out_cb)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000664 self._used_scm.RunCommand('updatesingle',
665 options, args + [parsed_url.GetFilename()], file_list)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000666 else:
maruel@chromium.org9e5317a2010-08-13 20:35:11 +0000667 # Create a shallow copy to mutate revision.
668 options = copy.copy(options)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000669 options.revision = revision_overrides.get(self.name)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000670 self.maybeGetParentRevision(
671 command, options, parsed_url, self.parent.name, revision_overrides)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000672 self._used_scm = gclient_scm.CreateSCM(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000673 parsed_url, self.root.root_dir, self.name, self.outbuf,
674 out_cb=work_queue.out_cb)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000675 self._got_revision = self._used_scm.RunCommand(command, options, args,
676 file_list)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000677 if file_list:
678 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000679
680 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
681 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000682 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000683 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000684 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000685 continue
686 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000687 [self.root.root_dir.lower(), file_list[i].lower()])
688 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000689 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000690 while file_list[i].startswith(('\\', '/')):
691 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000692
693 # Always parse the DEPS file.
694 self.ParseDepsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000695 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000696 if command in ('update', 'revert') and not options.noprehooks:
697 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000698
699 if self.recursion_limit:
700 # Parse the dependencies of this dependency.
701 for s in self.dependencies:
702 work_queue.enqueue(s)
703
704 if command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000705 if not isinstance(parsed_url, self.FileImpl):
706 # Skip file only checkout.
707 scm = gclient_scm.GetScmName(parsed_url)
708 if not options.scm or scm in options.scm:
709 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000710 # Pass in the SCM type as an env variable. Make sure we don't put
711 # unicode strings in the environment.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000712 env = os.environ.copy()
713 if scm:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000714 env['GCLIENT_SCM'] = str(scm)
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000715 if parsed_url:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000716 env['GCLIENT_URL'] = str(parsed_url)
717 env['GCLIENT_DEP_PATH'] = str(self.name)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000718 if options.prepend_dir and scm == 'git':
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000719 print_stdout = False
720 def filter_fn(line):
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000721 """Git-specific path marshaling. It is optimized for git-grep."""
722
723 def mod_path(git_pathspec):
724 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
725 modified_path = os.path.join(self.name, match.group(2))
726 branch = match.group(1) or ''
727 return '%s%s' % (branch, modified_path)
728
729 match = re.match('^Binary file ([^\0]+) matches$', line)
730 if match:
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000731 print 'Binary file %s matches\n' % mod_path(match.group(1))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000732 return
733
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000734 items = line.split('\0')
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000735 if len(items) == 2 and items[1]:
736 print '%s : %s' % (mod_path(items[0]), items[1])
737 elif len(items) >= 2:
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000738 # Multiple null bytes or a single trailing null byte indicate
739 # git is likely displaying filenames only (such as with -l)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000740 print '\n'.join(mod_path(path) for path in items if path)
741 else:
742 print line
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000743 else:
744 print_stdout = True
745 filter_fn = None
746
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000747 if parsed_url is None:
748 print >> sys.stderr, 'Skipped omitted dependency %s' % cwd
749 elif os.path.isdir(cwd):
maruel@chromium.org288054d2012-03-05 00:43:07 +0000750 try:
751 gclient_utils.CheckCallAndFilter(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000752 args, cwd=cwd, env=env, print_stdout=print_stdout,
753 filter_fn=filter_fn,
754 )
maruel@chromium.org288054d2012-03-05 00:43:07 +0000755 except subprocess2.CalledProcessError:
756 if not options.ignore:
757 raise
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000758 else:
759 print >> sys.stderr, 'Skipped missing %s' % cwd
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000760
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000761
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000762 @gclient_utils.lockedmethod
763 def _run_is_done(self, file_list, parsed_url):
764 # Both these are kept for hooks that are run as a separate tree traversal.
765 self._file_list = file_list
766 self._parsed_url = parsed_url
767 self._processed = True
768
szager@google.comb9a78d32012-03-13 18:46:21 +0000769 @staticmethod
770 def GetHookAction(hook_dict, matching_file_list):
771 """Turns a parsed 'hook' dict into an executable command."""
772 logging.debug(hook_dict)
773 logging.debug(matching_file_list)
774 command = hook_dict['action'][:]
775 if command[0] == 'python':
776 # If the hook specified "python" as the first item, the action is a
777 # Python script. Run it by starting a new copy of the same
778 # interpreter.
779 command[0] = sys.executable
780 if '$matching_files' in command:
781 splice_index = command.index('$matching_files')
782 command[splice_index:splice_index + 1] = matching_file_list
783 return command
784
785 def GetHooks(self, options):
786 """Evaluates all hooks, and return them in a flat list.
787
788 RunOnDeps() must have been called before to load the DEPS.
789 """
790 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000791 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000792 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000793 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000794 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000795 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000796 if self.deps_hooks:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000797 # TODO(maruel): If the user is using git or git-svn, then we don't know
798 # what files have changed so we always run all hooks. It'd be nice to fix
799 # that.
800 if (options.force or
801 isinstance(self.parsed_url, self.FileImpl) or
802 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000803 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000804 for hook_dict in self.deps_hooks:
szager@google.comb9a78d32012-03-13 18:46:21 +0000805 result.append(self.GetHookAction(hook_dict, []))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000806 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000807 # Run hooks on the basis of whether the files from the gclient operation
808 # match each hook's pattern.
809 for hook_dict in self.deps_hooks:
810 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000811 matching_file_list = [
812 f for f in self.file_list_and_children if pattern.search(f)
813 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000814 if matching_file_list:
szager@google.comb9a78d32012-03-13 18:46:21 +0000815 result.append(self.GetHookAction(hook_dict, matching_file_list))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000816 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000817 result.extend(s.GetHooks(options))
818 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000819
szager@google.comb9a78d32012-03-13 18:46:21 +0000820 def RunHooksRecursively(self, options):
821 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000822 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000823 for hook in self.GetHooks(options):
824 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000825 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +0000826 gclient_utils.CheckCallAndFilterAndHeader(
827 hook, cwd=self.root.root_dir, always=True)
828 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
829 # Use a discrete exit status code of 2 to indicate that a hook action
830 # failed. Users of this script may wish to treat hook action failures
831 # differently from VC failures.
832 print >> sys.stderr, 'Error: %s' % str(e)
833 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000834 finally:
835 elapsed_time = time.time() - start_time
836 if elapsed_time > 10:
837 print "Hook '%s' took %.2f secs" % (
838 gclient_utils.CommandToStr(hook), elapsed_time)
maruel@chromium.orgeaf61062010-07-07 18:42:39 +0000839
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000840 def RunPreDepsHooks(self):
841 assert self.processed
842 assert self.deps_parsed
843 assert not self.pre_deps_hooks_ran
844 assert not self.hooks_ran
845 for s in self.dependencies:
846 assert not s.processed
847 self._pre_deps_hooks_ran = True
848 for hook in self.pre_deps_hooks:
849 try:
850 start_time = time.time()
851 gclient_utils.CheckCallAndFilterAndHeader(
852 hook, cwd=self.root.root_dir, always=True)
853 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
854 # Use a discrete exit status code of 2 to indicate that a hook action
855 # failed. Users of this script may wish to treat hook action failures
856 # differently from VC failures.
857 print >> sys.stderr, 'Error: %s' % str(e)
858 sys.exit(2)
859 finally:
860 elapsed_time = time.time() - start_time
861 if elapsed_time > 10:
862 print "Hook '%s' took %.2f secs" % (
863 gclient_utils.CommandToStr(hook), elapsed_time)
864
865
maruel@chromium.org0d812442010-08-10 12:41:08 +0000866 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000867 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000868 dependencies = self.dependencies
869 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000870 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000871 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000872 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000873 for i in d.subtree(include_all):
874 yield i
875
876 def depth_first_tree(self):
877 """Depth-first recursion including the root node."""
878 yield self
879 for i in self.dependencies:
880 for j in i.depth_first_tree():
881 if j.should_process:
882 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +0000883
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000884 @gclient_utils.lockedmethod
885 def add_dependency(self, new_dep):
886 self._dependencies.append(new_dep)
887
888 @gclient_utils.lockedmethod
889 def _mark_as_parsed(self, new_hooks):
890 self._deps_hooks.extend(new_hooks)
891 self._deps_parsed = True
892
maruel@chromium.org68988972011-09-20 14:11:42 +0000893 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000894 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000895 def dependencies(self):
896 return tuple(self._dependencies)
897
898 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000899 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000900 def deps_hooks(self):
901 return tuple(self._deps_hooks)
902
903 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000904 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000905 def pre_deps_hooks(self):
906 return tuple(self._pre_deps_hooks)
907
908 @property
909 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000910 def parsed_url(self):
911 return self._parsed_url
912
913 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000914 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000915 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000916 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +0000917 return self._deps_parsed
918
919 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000920 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000921 def processed(self):
922 return self._processed
923
924 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000925 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000926 def pre_deps_hooks_ran(self):
927 return self._pre_deps_hooks_ran
928
929 @property
930 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000931 def hooks_ran(self):
932 return self._hooks_ran
933
934 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000935 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000936 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000937 return tuple(self._file_list)
938
939 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000940 def used_scm(self):
941 """SCMWrapper instance for this dependency or None if not processed yet."""
942 return self._used_scm
943
944 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000945 @gclient_utils.lockedmethod
946 def got_revision(self):
947 return self._got_revision
948
949 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000950 def file_list_and_children(self):
951 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000952 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000953 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +0000954 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000955
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000956 def __str__(self):
957 out = []
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +0000958 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +0000959 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000960 'processed', 'hooks_ran', 'deps_parsed', 'requirements'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +0000961 # First try the native property if it exists.
962 if hasattr(self, '_' + i):
963 value = getattr(self, '_' + i, False)
964 else:
965 value = getattr(self, i, False)
966 if value:
967 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000968
969 for d in self.dependencies:
970 out.extend([' ' + x for x in str(d).splitlines()])
971 out.append('')
972 return '\n'.join(out)
973
974 def __repr__(self):
975 return '%s: %s' % (self.name, self.url)
976
maruel@chromium.orgbffb9042010-07-22 20:59:36 +0000977 def hierarchy(self):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +0000978 """Returns a human-readable hierarchical reference to a Dependency."""
maruel@chromium.orgbffb9042010-07-22 20:59:36 +0000979 out = '%s(%s)' % (self.name, self.url)
980 i = self.parent
981 while i and i.name:
982 out = '%s(%s) -> %s' % (i.name, i.url, out)
983 i = i.parent
984 return out
985
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000986
987class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000988 """Object that represent a gclient checkout. A tree of Dependency(), one per
989 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000990
991 DEPS_OS_CHOICES = {
992 "win32": "win",
993 "win": "win",
994 "cygwin": "win",
995 "darwin": "mac",
996 "mac": "mac",
997 "unix": "unix",
998 "linux": "unix",
999 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +00001000 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +00001001 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001002 }
1003
1004 DEFAULT_CLIENT_FILE_TEXT = ("""\
1005solutions = [
1006 { "name" : "%(solution_name)s",
1007 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001008 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001009 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001010 "custom_deps" : {
1011 },
maruel@chromium.org73e21142010-07-05 13:32:01 +00001012 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001013 },
1014]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001015cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001016""")
1017
1018 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
1019 { "name" : "%(solution_name)s",
1020 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001021 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001022 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001023 "custom_deps" : {
maruel@chromium.org73e21142010-07-05 13:32:01 +00001024%(solution_deps)s },
1025 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001026 },
1027""")
1028
1029 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1030# Snapshot generated with gclient revinfo --snapshot
1031solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001032%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001033""")
1034
1035 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001036 # Do not change previous behavior. Only solution level and immediate DEPS
1037 # are processed.
1038 self._recursion_limit = 2
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001039 Dependency.__init__(self, None, None, None, None, True, None, None, None,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001040 'unused', True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001041 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001042 if options.deps_os:
1043 enforced_os = options.deps_os.split(',')
1044 else:
1045 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1046 if 'all' in enforced_os:
1047 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001048 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001049 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001050 self.config_content = None
1051
borenet@google.com88d10082014-03-21 17:24:48 +00001052 def _CheckConfig(self):
1053 """Verify that the config matches the state of the existing checked-out
1054 solutions."""
1055 for dep in self.dependencies:
1056 if dep.managed and dep.url:
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001057 scm = gclient_scm.CreateSCM(
1058 dep.url, self.root_dir, dep.name, self.outbuf)
borenet@google.com4e9be262014-04-08 19:40:30 +00001059 actual_url = scm.GetActualRemoteURL(self._options)
1060 if actual_url and not scm.DoesRemoteURLMatch(self._options):
borenet@google.com0a427372014-04-02 19:12:13 +00001061 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001062Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001063is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001064
borenet@google.com97882362014-04-07 20:06:02 +00001065The .gclient file contains:
1066%(expected_url)s (%(expected_scm)s)
1067
1068The local checkout in %(checkout_path)s reports:
1069%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001070
1071You should ensure that the URL listed in .gclient is correct and either change
1072it or fix the checkout. If you're managing your own git checkout in
1073%(checkout_path)s but the URL in .gclient is for an svn repository, you probably
1074want to set 'managed': False in .gclient.
borenet@google.com88d10082014-03-21 17:24:48 +00001075''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1076 'expected_url': dep.url,
1077 'expected_scm': gclient_scm.GetScmName(dep.url),
1078 'actual_url': actual_url,
1079 'actual_scm': gclient_scm.GetScmName(actual_url)})
1080
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001081 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001082 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001083 config_dict = {}
1084 self.config_content = content
1085 try:
1086 exec(content, config_dict)
1087 except SyntaxError, e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001088 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001089
peter@chromium.org1efccc82012-04-27 16:34:38 +00001090 # Append any target OS that is not already being enforced to the tuple.
1091 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001092 if config_dict.get('target_os_only', False):
1093 self._enforced_os = tuple(set(target_os))
1094 else:
1095 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1096
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001097 gclient_scm.GitWrapper.cache_dir = config_dict.get('cache_dir')
szager@chromium.org848fd492014-04-09 19:06:44 +00001098 git_cache.Mirror.SetCachePath(config_dict.get('cache_dir'))
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001099
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001100 if not target_os and config_dict.get('target_os_only', False):
1101 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1102 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001103
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001104 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001105 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001106 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001107 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +00001108 self, s['name'], s['url'],
1109 s.get('safesync_url', None),
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001110 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001111 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001112 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001113 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001114 s.get('deps_file', 'DEPS'),
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001115 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001116 except KeyError:
1117 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1118 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001119 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1120 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001121
1122 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001123 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001124 self._options.config_filename),
1125 self.config_content)
1126
1127 @staticmethod
1128 def LoadCurrentConfig(options):
1129 """Searches for and loads a .gclient file relative to the current working
1130 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001131 if options.spec:
1132 client = GClient('.', options)
1133 client.SetConfig(options.spec)
1134 else:
1135 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1136 if not path:
1137 return None
1138 client = GClient(path, options)
1139 client.SetConfig(gclient_utils.FileRead(
1140 os.path.join(path, options.config_filename)))
maruel@chromium.org69392e72011-10-13 22:09:00 +00001141
1142 if (options.revisions and
1143 len(client.dependencies) > 1 and
1144 any('@' not in r for r in options.revisions)):
1145 print >> sys.stderr, (
1146 'You must specify the full solution name like --revision %s@%s\n'
1147 'when you have multiple solutions setup in your .gclient file.\n'
1148 'Other solutions present are: %s.') % (
1149 client.dependencies[0].name,
1150 options.revisions[0],
1151 ', '.join(s.name for s in client.dependencies[1:]))
maruel@chromium.org15804092010-09-02 17:07:37 +00001152 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001153
nsylvain@google.comefc80932011-05-31 21:27:56 +00001154 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001155 safesync_url, managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001156 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1157 'solution_name': solution_name,
1158 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001159 'deps_file': deps_file,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001160 'safesync_url' : safesync_url,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001161 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001162 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001163 })
1164
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001165 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001166 """Creates a .gclient_entries file to record the list of unique checkouts.
1167
1168 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001169 """
1170 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1171 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001172 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001173 for entry in self.root.subtree(False):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001174 # Skip over File() dependencies as we can't version them.
1175 if not isinstance(entry.parsed_url, self.FileImpl):
1176 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1177 pprint.pformat(entry.parsed_url))
1178 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001179 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001180 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001181 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001182
1183 def _ReadEntries(self):
1184 """Read the .gclient_entries file for the given client.
1185
1186 Returns:
1187 A sequence of solution names, which will be empty if there is the
1188 entries file hasn't been created yet.
1189 """
1190 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001191 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001192 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001193 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001194 try:
1195 exec(gclient_utils.FileRead(filename), scope)
1196 except SyntaxError, e:
1197 gclient_utils.SyntaxErrorToError(filename, e)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001198 return scope['entries']
1199
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001200 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001201 """Checks for revision overrides."""
1202 revision_overrides = {}
maruel@chromium.org307d1792010-05-31 20:03:13 +00001203 if self._options.head:
1204 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001205 # Do not check safesync_url if one or more --revision flag is specified.
1206 if not self._options.revisions:
1207 for s in self.dependencies:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001208 if not s.managed:
1209 self._options.revisions.append('%s@unmanaged' % s.name)
1210 elif s.safesync_url:
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001211 self._ApplySafeSyncRev(dep=s)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001212 if not self._options.revisions:
1213 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001214 solutions_names = [s.name for s in self.dependencies]
maruel@chromium.org307d1792010-05-31 20:03:13 +00001215 index = 0
1216 for revision in self._options.revisions:
1217 if not '@' in revision:
1218 # Support for --revision 123
1219 revision = '%s@%s' % (solutions_names[index], revision)
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001220 sol, rev = revision.split('@', 1)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001221 if not sol in solutions_names:
1222 #raise gclient_utils.Error('%s is not a valid solution.' % sol)
1223 print >> sys.stderr, ('Please fix your script, having invalid '
1224 '--revision flags will soon considered an error.')
1225 else:
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001226 revision_overrides[sol] = rev
maruel@chromium.org307d1792010-05-31 20:03:13 +00001227 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001228 return revision_overrides
1229
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001230 def _ApplySafeSyncRev(self, dep):
1231 """Finds a valid revision from the content of the safesync_url and apply it
1232 by appending revisions to the revision list. Throws if revision appears to
1233 be invalid for the given |dep|."""
1234 assert len(dep.safesync_url) > 0
1235 handle = urllib.urlopen(dep.safesync_url)
1236 rev = handle.read().strip()
1237 handle.close()
1238 if not rev:
1239 raise gclient_utils.Error(
1240 'It appears your safesync_url (%s) is not working properly\n'
1241 '(as it returned an empty response). Check your config.' %
1242 dep.safesync_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001243 scm = gclient_scm.CreateSCM(
1244 dep.url, dep.root.root_dir, dep.name, self.outbuf)
iannucci@chromium.org4a4b33b2013-07-04 20:25:46 +00001245 safe_rev = scm.GetUsableRev(rev, self._options)
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001246 if self._options.verbose:
1247 print('Using safesync_url revision: %s.\n' % safe_rev)
1248 self._options.revisions.append('%s@%s' % (dep.name, safe_rev))
1249
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001250 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001251 """Runs a command on each dependency in a client and its dependencies.
1252
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001253 Args:
1254 command: The command to use (e.g., 'status' or 'diff')
1255 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001256 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001257 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001258 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001259
1260 self._CheckConfig()
1261
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001262 revision_overrides = {}
1263 # It's unnecessary to check for revision overrides for 'recurse'.
1264 # Save a few seconds by not calling _EnforceRevisions() in that case.
dbeam@chromium.org0f8a9442012-07-10 14:50:20 +00001265 if command not in ('diff', 'recurse', 'runhooks', 'status'):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001266 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001267 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001268 # Disable progress for non-tty stdout.
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001269 if (sys.stdout.isatty() and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001270 if command in ('update', 'revert'):
1271 pm = Progress('Syncing projects', 1)
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +00001272 elif command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001273 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001274 work_queue = gclient_utils.ExecutionQueue(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001275 self._options.jobs, pm, ignore_requirements=ignore_requirements,
1276 verbose=self._options.verbose)
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(revision_overrides, command, args, options=self._options)
piman@chromium.org6f363722010-04-27 00:41:09 +00001280
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001281 # Once all the dependencies have been processed, it's now safe to run the
1282 # hooks.
1283 if not self._options.nohooks:
1284 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001285
1286 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001287 # Notify the user if there is an orphaned entry in their working copy.
1288 # Only delete the directory if there are no changes in it, and
1289 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001290 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001291 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1292 for e in entries]
1293
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001294 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001295 if not prev_url:
1296 # entry must have been overridden via .gclient custom_deps
1297 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001298 # Fix path separator on Windows.
1299 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001300 e_dir = os.path.join(self.root_dir, entry_fixed)
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001301
1302 def _IsParentOfAny(parent, path_list):
1303 parent_plus_slash = parent + '/'
1304 return any(
1305 path[:len(parent_plus_slash)] == parent_plus_slash
1306 for path in path_list)
1307
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001308 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001309 if (entry not in entries and
1310 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001311 os.path.exists(e_dir)):
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001312 scm = gclient_scm.CreateSCM(
1313 prev_url, self.root_dir, entry_fixed, self.outbuf)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001314
1315 # Check to see if this directory is now part of a higher-up checkout.
1316 if scm.GetCheckoutRoot() in full_entries:
1317 logging.info('%s is part of a higher level checkout, not '
1318 'removing.', scm.GetCheckoutRoot())
1319 continue
1320
1321 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001322 scm.status(self._options, [], file_list)
1323 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001324 if (not self._options.delete_unversioned_trees or
1325 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001326 # There are modified files in this entry. Keep warning until
1327 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001328 print(('\nWARNING: \'%s\' is no longer part of this client. '
1329 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001330 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001331 else:
1332 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001333 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001334 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001335 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001336 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001337 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001338 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001339
1340 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001341 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001342 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001343 # Load all the settings.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001344 work_queue = gclient_utils.ExecutionQueue(
1345 self._options.jobs, None, False, verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001346 for s in self.dependencies:
1347 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001348 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001349
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001350 def GetURLAndRev(dep):
1351 """Returns the revision-qualified SCM url for a Dependency."""
1352 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001353 return None
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001354 if isinstance(dep.parsed_url, self.FileImpl):
1355 original_url = dep.parsed_url.file_location
1356 else:
1357 original_url = dep.parsed_url
nasser@codeaurora.org5d63eb82010-03-24 23:22:09 +00001358 url, _ = gclient_utils.SplitUrlRevision(original_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001359 scm = gclient_scm.CreateSCM(
1360 original_url, self.root_dir, dep.name, self.outbuf)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001361 if not os.path.isdir(scm.checkout_path):
1362 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001363 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001364
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001365 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001366 new_gclient = ''
1367 # First level at .gclient
1368 for d in self.dependencies:
1369 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001370 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001371 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001372 for d in dep.dependencies:
1373 entries[d.name] = GetURLAndRev(d)
1374 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001375 GrabDeps(d)
1376 custom_deps = []
1377 for k in sorted(entries.keys()):
1378 if entries[k]:
1379 # Quotes aren't escaped...
1380 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1381 else:
1382 custom_deps.append(' \"%s\": None,\n' % k)
1383 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1384 'solution_name': d.name,
1385 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001386 'deps_file': d.deps_file,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001387 'safesync_url' : d.safesync_url or '',
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001388 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001389 'solution_deps': ''.join(custom_deps),
1390 }
1391 # Print the snapshot configuration file
1392 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001393 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001394 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001395 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001396 if self._options.actual:
1397 entries[d.name] = GetURLAndRev(d)
1398 else:
1399 entries[d.name] = d.parsed_url
1400 keys = sorted(entries.keys())
1401 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001402 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001403 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001404
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001405 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001406 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001407 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001408
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001409 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001410 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001411 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001412 return self._root_dir
1413
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001414 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001415 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001416 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001417 return self._enforced_os
1418
maruel@chromium.org68988972011-09-20 14:11:42 +00001419 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001420 def recursion_limit(self):
1421 """How recursive can each dependencies in DEPS file can load DEPS file."""
1422 return self._recursion_limit
1423
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001424 @property
1425 def target_os(self):
1426 return self._enforced_os
1427
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001428
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001429#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001430
1431
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001432def CMDcleanup(parser, args):
1433 """Cleans up all working copies.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001434
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001435 Mostly svn-specific. Simply runs 'svn cleanup' for each module.
1436 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001437 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1438 help='override deps for the specified (comma-separated) '
1439 'platform(s); \'all\' will process all deps_os '
1440 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001441 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001442 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001443 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001444 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001445 if options.verbose:
1446 # Print out the .gclient file. This is longer than if we just printed the
1447 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001448 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001449 return client.RunOnDeps('cleanup', args)
1450
1451
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001452@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001453def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001454 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001455
1456 Runs a shell command on all entries.
ilevy@chromium.org37116242012-11-28 01:32:48 +00001457 Sets GCLIENT_DEP_PATH enviroment variable as the dep's relative location to
1458 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001459 """
1460 # Stop parsing at the first non-arg so that these go through to the command
1461 parser.disable_interspersed_args()
1462 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001463 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001464 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001465 help='Ignore non-zero return codes from subcommands.')
1466 parser.add_option('--prepend-dir', action='store_true',
1467 help='Prepend relative dir for use with git <cmd> --null.')
1468 parser.add_option('--no-progress', action='store_true',
1469 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001470 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001471 if not args:
1472 print >> sys.stderr, 'Need to supply a command!'
1473 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001474 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1475 if not root_and_entries:
1476 print >> sys.stderr, (
1477 'You need to run gclient sync at least once to use \'recurse\'.\n'
1478 'This is because .gclient_entries needs to exist and be up to date.')
1479 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001480
1481 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001482 scm_set = set()
1483 for scm in options.scm:
1484 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001485 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001486
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001487 options.nohooks = True
1488 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001489 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1490 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001491
1492
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001493@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001494def CMDfetch(parser, args):
1495 """Fetches upstream commits for all modules.
1496
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001497 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1498 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001499 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001500 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001501 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1502
1503
1504def CMDgrep(parser, args):
1505 """Greps through git repos managed by gclient.
1506
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001507 Runs 'git grep [args...]' for each module.
1508 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001509 # We can't use optparse because it will try to parse arguments sent
1510 # to git grep and throw an error. :-(
1511 if not args or re.match('(-h|--help)$', args[0]):
1512 print >> sys.stderr, (
1513 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1514 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1515 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1516 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
1517 ' end of your query.'
1518 )
1519 return 1
1520
1521 jobs_arg = ['--jobs=1']
1522 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1523 jobs_arg, args = args[:1], args[1:]
1524 elif re.match(r'(-j|--jobs)$', args[0]):
1525 jobs_arg, args = args[:2], args[2:]
1526
1527 return CMDrecurse(
1528 parser,
1529 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1530 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001531
1532
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001533@subcommand.usage('[url] [safesync url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001534def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001535 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001536
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001537 This specifies the configuration for further commands. After update/sync,
1538 top-level DEPS files in each module are read to determine dependent
1539 modules to operate on as well. If optional [url] parameter is
1540 provided, then configuration is read from a specified Subversion server
1541 URL.
1542 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00001543 # We do a little dance with the --gclientfile option. 'gclient config' is the
1544 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1545 # arguments. So, we temporarily stash any --gclientfile parameter into
1546 # options.output_config_file until after the (gclientfile xor spec) error
1547 # check.
1548 parser.remove_option('--gclientfile')
1549 parser.add_option('--gclientfile', dest='output_config_file',
1550 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001551 parser.add_option('--name',
1552 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001553 parser.add_option('--deps-file', default='DEPS',
1554 help='overrides the default name for the DEPS file for the'
1555 'main solutions and all sub-dependencies')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001556 parser.add_option('--unmanaged', action='store_true', default=False,
1557 help='overrides the default behavior to make it possible '
1558 'to have the main solution untouched by gclient '
1559 '(gclient will check out unmanaged dependencies but '
1560 'will never sync them)')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001561 parser.add_option('--git-deps', action='store_true',
1562 help='sets the deps file to ".DEPS.git" instead of "DEPS"')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001563 parser.add_option('--cache-dir',
1564 help='(git only) Cache all git repos into this dir and do '
1565 'shared clones from the cache, instead of cloning '
1566 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001567 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001568 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001569 if options.output_config_file:
1570 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001571 if ((options.spec and args) or len(args) > 2 or
1572 (not options.spec and not args)):
1573 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1574
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001575 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001576 if options.spec:
1577 client.SetConfig(options.spec)
1578 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001579 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001580 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001581 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001582 if name.endswith('.git'):
1583 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001584 else:
1585 # specify an alternate relpath for the given URL.
1586 name = options.name
nsylvain@google.comefc80932011-05-31 21:27:56 +00001587 deps_file = options.deps_file
1588 if options.git_deps:
1589 deps_file = '.DEPS.git'
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001590 safesync_url = ''
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001591 if len(args) > 1:
1592 safesync_url = args[1]
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001593 client.SetDefaultConfig(name, deps_file, base_url, safesync_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001594 managed=not options.unmanaged,
1595 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001596 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001597 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001598
1599
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001600@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001601 gclient pack > patch.txt
1602 generate simple patch for configured client and dependences
1603""")
1604def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001605 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001606
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001607 Internally, runs 'svn diff'/'git diff' on each checked out module and
1608 dependencies, and performs minimal postprocessing of the output. The
1609 resulting patch is printed to stdout and can be applied to a freshly
1610 checked out tree via 'patch -p0 < patchfile'.
1611 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001612 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1613 help='override deps for the specified (comma-separated) '
1614 'platform(s); \'all\' will process all deps_os '
1615 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001616 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001617 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001618 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001619 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001620 client = GClient.LoadCurrentConfig(options)
1621 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001622 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001623 if options.verbose:
1624 # Print out the .gclient file. This is longer than if we just printed the
1625 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001626 print(client.config_content)
kbr@google.comab318592009-09-04 00:54:55 +00001627 return client.RunOnDeps('pack', args)
1628
1629
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001630def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001631 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001632 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1633 help='override deps for the specified (comma-separated) '
1634 'platform(s); \'all\' will process all deps_os '
1635 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001636 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001637 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001638 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001639 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001640 if options.verbose:
1641 # Print out the .gclient file. This is longer than if we just printed the
1642 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001643 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001644 return client.RunOnDeps('status', args)
1645
1646
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001647@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001648 gclient sync
1649 update files from SCM according to current configuration,
1650 *for modules which have changed since last update or sync*
1651 gclient sync --force
1652 update files from SCM according to current configuration, for
1653 all modules (useful for recovering files deleted from local copy)
1654 gclient sync --revision src@31000
1655 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001656
1657JSON output format:
1658If the --output-json option is specified, the following document structure will
1659be emitted to the provided file. 'null' entries may occur for subprojects which
1660are present in the gclient solution, but were not processed (due to custom_deps,
1661os_deps, etc.)
1662
1663{
1664 "solutions" : {
1665 "<name>": { # <name> is the posix-normalized path to the solution.
1666 "revision": [<svn rev int>|<git id hex string>|null],
1667 "scm": ["svn"|"git"|null],
1668 }
1669 }
1670}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001671""")
1672def CMDsync(parser, args):
1673 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001674 parser.add_option('-f', '--force', action='store_true',
1675 help='force update even for unchanged modules')
1676 parser.add_option('-n', '--nohooks', action='store_true',
1677 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001678 parser.add_option('-p', '--noprehooks', action='store_true',
1679 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001680 parser.add_option('-r', '--revision', action='append',
1681 dest='revisions', metavar='REV', default=[],
1682 help='Enforces revision/hash for the solutions with the '
1683 'format src@rev. The src@ part is optional and can be '
1684 'skipped. -r can be used multiple times when .gclient '
1685 'has multiple solutions configured and will work even '
joi@chromium.org792ea882010-11-10 02:37:27 +00001686 'if the src@ part is skipped. Note that specifying '
1687 '--revision means your safesync_url gets ignored.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00001688 parser.add_option('--with_branch_heads', action='store_true',
1689 help='Clone git "branch_heads" refspecs in addition to '
1690 'the default refspecs. This adds about 1/2GB to a '
1691 'full checkout. (git only)')
floitsch@google.comeaab7842011-04-28 09:07:58 +00001692 parser.add_option('-t', '--transitive', action='store_true',
1693 help='When a revision is specified (in the DEPS file or '
1694 'with the command-line flag), transitively update '
1695 'the dependencies to the date of the given revision. '
1696 'Only supported for SVN repositories.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001697 parser.add_option('-H', '--head', action='store_true',
1698 help='skips any safesync_urls specified in '
1699 'configured solutions and sync to head instead')
1700 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001701 help='Deletes from the working copy any dependencies that '
1702 'have been removed since the last sync, as long as '
1703 'there are no local modifications. When used with '
1704 '--force, such dependencies are removed even if they '
1705 'have local modifications. When used with --reset, '
1706 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00001707 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001708 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001709 parser.add_option('-R', '--reset', action='store_true',
1710 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00001711 parser.add_option('-M', '--merge', action='store_true',
1712 help='merge upstream changes instead of trying to '
1713 'fast-forward or rebase')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001714 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1715 help='override deps for the specified (comma-separated) '
1716 'platform(s); \'all\' will process all deps_os '
1717 'references')
1718 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
1719 help='Skip svn up whenever possible by requesting '
1720 'actual HEAD revision from the repository')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001721 parser.add_option('--upstream', action='store_true',
1722 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001723 parser.add_option('--output-json',
1724 help='Output a json document to this path containing '
1725 'summary information about the sync.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001726 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001727 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001728
1729 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001730 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001731
maruel@chromium.org307d1792010-05-31 20:03:13 +00001732 if options.revisions and options.head:
1733 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001734 print('Warning: you cannot use both --head and --revision')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001735
1736 if options.verbose:
1737 # Print out the .gclient file. This is longer than if we just printed the
1738 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001739 print(client.config_content)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001740 ret = client.RunOnDeps('update', args)
1741 if options.output_json:
1742 slns = {}
1743 for d in client.subtree(True):
1744 normed = d.name.replace('\\', '/').rstrip('/') + '/'
1745 slns[normed] = {
1746 'revision': d.got_revision,
1747 'scm': d.used_scm.name if d.used_scm else None,
1748 }
1749 with open(options.output_json, 'wb') as f:
1750 json.dump({'solutions': slns}, f)
1751 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001752
1753
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001754CMDupdate = CMDsync
1755
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001756
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001757def CMDdiff(parser, args):
1758 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001759 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1760 help='override deps for the specified (comma-separated) '
1761 'platform(s); \'all\' will process all deps_os '
1762 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001763 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001764 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001765 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001766 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001767 if options.verbose:
1768 # Print out the .gclient file. This is longer than if we just printed the
1769 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001770 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001771 return client.RunOnDeps('diff', args)
1772
1773
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001774def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001775 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001776
1777 That's the nuclear option to get back to a 'clean' state. It removes anything
1778 that shows up in svn status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001779 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1780 help='override deps for the specified (comma-separated) '
1781 'platform(s); \'all\' will process all deps_os '
1782 'references')
1783 parser.add_option('-n', '--nohooks', action='store_true',
1784 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001785 parser.add_option('-p', '--noprehooks', action='store_true',
1786 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001787 parser.add_option('--upstream', action='store_true',
1788 help='Make repo state match upstream branch.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001789 (options, args) = parser.parse_args(args)
1790 # --force is implied.
1791 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001792 options.reset = False
1793 options.delete_unversioned_trees = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001794 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001795 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001796 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001797 return client.RunOnDeps('revert', args)
1798
1799
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001800def CMDrunhooks(parser, args):
1801 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001802 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1803 help='override deps for the specified (comma-separated) '
1804 'platform(s); \'all\' will process all deps_os '
1805 'references')
1806 parser.add_option('-f', '--force', action='store_true', default=True,
1807 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001808 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001809 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001810 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001811 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001812 if options.verbose:
1813 # Print out the .gclient file. This is longer than if we just printed the
1814 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001815 print(client.config_content)
maruel@chromium.org5df6a462009-08-28 18:52:26 +00001816 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001817 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001818 return client.RunOnDeps('runhooks', args)
1819
1820
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001821def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001822 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001823
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001824 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001825 can be used to reproduce the same tree in the future. It is only useful for
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001826 'unpinned dependencies', i.e. DEPS/deps references without a svn revision
1827 number or a git hash. A git branch name isn't 'pinned' since the actual
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001828 commit can change.
1829 """
1830 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1831 help='override deps for the specified (comma-separated) '
1832 'platform(s); \'all\' will process all deps_os '
1833 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001834 parser.add_option('-a', '--actual', action='store_true',
1835 help='gets the actual checked out revisions instead of the '
1836 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001837 parser.add_option('-s', '--snapshot', action='store_true',
1838 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001839 'version of all repositories to reproduce the tree, '
1840 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001841 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001842 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001843 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001844 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001845 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001846 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001847
1848
szager@google.comb9a78d32012-03-13 18:46:21 +00001849def CMDhookinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001850 """Outputs the hooks that would be run by `gclient runhooks`."""
szager@google.comb9a78d32012-03-13 18:46:21 +00001851 (options, args) = parser.parse_args(args)
1852 options.force = True
1853 client = GClient.LoadCurrentConfig(options)
1854 if not client:
1855 raise gclient_utils.Error('client not configured; see \'gclient config\'')
1856 client.RunOnDeps(None, [])
1857 print '; '.join(' '.join(hook) for hook in client.GetHooks(options))
1858 return 0
1859
1860
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001861class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00001862 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001863
1864 def __init__(self, **kwargs):
1865 optparse.OptionParser.__init__(
1866 self, version='%prog ' + __version__, **kwargs)
1867
1868 # Some arm boards have issues with parallel sync.
1869 if platform.machine().startswith('arm'):
1870 jobs = 1
1871 else:
1872 jobs = max(8, gclient_utils.NumLocalCpus())
1873 # cmp: 2013/06/19
1874 # Temporary workaround to lower bot-load on SVN server.
hinoka@google.com267f33e2014-02-28 22:02:32 +00001875 # Bypassed if a bot_update flag is detected.
1876 if (os.environ.get('CHROME_HEADLESS') == '1' and
1877 not os.path.exists('update.flag')):
xusydoc@chromium.org05028412013-07-29 13:40:10 +00001878 jobs = 1
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001879
1880 self.add_option(
1881 '-j', '--jobs', default=jobs, type='int',
1882 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00001883 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001884 self.add_option(
1885 '-v', '--verbose', action='count', default=0,
1886 help='Produces additional output for diagnostics. Can be used up to '
1887 'three times for more logging info.')
1888 self.add_option(
1889 '--gclientfile', dest='config_filename',
1890 help='Specify an alternate %s file' % self.gclientfile_default)
1891 self.add_option(
1892 '--spec',
1893 help='create a gclient file containing the provided string. Due to '
1894 'Cygwin/Python brokenness, it can\'t contain any newlines.')
1895 self.add_option(
1896 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00001897 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001898
1899 def parse_args(self, args=None, values=None):
1900 """Integrates standard options processing."""
1901 options, args = optparse.OptionParser.parse_args(self, args, values)
1902 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
1903 logging.basicConfig(
1904 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00001905 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001906 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001907 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00001908 if (options.config_filename and
1909 options.config_filename != os.path.basename(options.config_filename)):
1910 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001911 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001912 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00001913 options.entries_filename = options.config_filename + '_entries'
1914 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001915 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00001916
1917 # These hacks need to die.
1918 if not hasattr(options, 'revisions'):
1919 # GClient.RunOnDeps expects it even if not applicable.
1920 options.revisions = []
1921 if not hasattr(options, 'head'):
1922 options.head = None
1923 if not hasattr(options, 'nohooks'):
1924 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001925 if not hasattr(options, 'noprehooks'):
1926 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00001927 if not hasattr(options, 'deps_os'):
1928 options.deps_os = None
1929 if not hasattr(options, 'manually_grab_svn_rev'):
1930 options.manually_grab_svn_rev = None
1931 if not hasattr(options, 'force'):
1932 options.force = None
1933 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001934
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001935
1936def disable_buffering():
1937 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
1938 # operations. Python as a strong tendency to buffer sys.stdout.
1939 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
1940 # Make stdout annotated with the thread ids.
1941 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00001942
1943
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001944def Main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001945 """Doesn't parse the arguments here, just find the right subcommand to
1946 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00001947 if sys.hexversion < 0x02060000:
maruel@chromium.orgc3a15a22010-11-20 03:12:27 +00001948 print >> sys.stderr, (
maruel@chromium.org82798cb2012-02-23 18:16:12 +00001949 '\nYour python version %s is unsupported, please upgrade.\n' %
1950 sys.version.split(' ', 1)[0])
1951 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00001952 if not sys.executable:
1953 print >> sys.stderr, (
1954 '\nPython cannot find the location of it\'s own executable.\n')
1955 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001956 fix_encoding.fix_encoding()
1957 disable_buffering()
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +00001958 colorama.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001959 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001960 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001961 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00001962 except KeyboardInterrupt:
1963 gclient_utils.GClientChildren.KillAllRemainingChildren()
1964 raise
maruel@chromium.org31cb48a2011-04-04 18:01:36 +00001965 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001966 print >> sys.stderr, 'Error: %s' % str(e)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001967 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00001968 finally:
1969 gclient_utils.PrintWarnings()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001970
1971
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001972if '__main__' == __name__:
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001973 sys.exit(Main(sys.argv[1:]))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001974
1975# vim: ts=2:sw=2:tw=80:et: