blob: 1b5d8f4a395c8863d58ce83873162dffb2dea269 [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
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000100from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000101import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000102import subprocess2
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +0000103from third_party import colorama
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000104
105
maruel@chromium.org116704f2010-06-11 17:34:38 +0000106class GClientKeywords(object):
107 class FromImpl(object):
108 """Used to implement the From() syntax."""
109
110 def __init__(self, module_name, sub_target_name=None):
111 """module_name is the dep module we want to include from. It can also be
112 the name of a subdirectory to include from.
113
114 sub_target_name is an optional parameter if the module name in the other
115 DEPS file is different. E.g., you might want to map src/net to net."""
116 self.module_name = module_name
117 self.sub_target_name = sub_target_name
118
119 def __str__(self):
120 return 'From(%s, %s)' % (repr(self.module_name),
121 repr(self.sub_target_name))
122
maruel@chromium.org116704f2010-06-11 17:34:38 +0000123 class FileImpl(object):
124 """Used to implement the File('') syntax which lets you sync a single file
maruel@chromium.orge3216c62010-07-08 03:31:43 +0000125 from a SVN repo."""
maruel@chromium.org116704f2010-06-11 17:34:38 +0000126
127 def __init__(self, file_location):
128 self.file_location = file_location
129
130 def __str__(self):
131 return 'File("%s")' % self.file_location
132
133 def GetPath(self):
134 return os.path.split(self.file_location)[0]
135
136 def GetFilename(self):
137 rev_tokens = self.file_location.split('@')
138 return os.path.split(rev_tokens[0])[1]
139
140 def GetRevision(self):
141 rev_tokens = self.file_location.split('@')
142 if len(rev_tokens) > 1:
143 return rev_tokens[1]
144 return None
145
146 class VarImpl(object):
147 def __init__(self, custom_vars, local_scope):
148 self._custom_vars = custom_vars
149 self._local_scope = local_scope
150
151 def Lookup(self, var_name):
152 """Implements the Var syntax."""
153 if var_name in self._custom_vars:
154 return self._custom_vars[var_name]
155 elif var_name in self._local_scope.get("vars", {}):
156 return self._local_scope["vars"][var_name]
157 raise gclient_utils.Error("Var is not defined: %s" % var_name)
158
159
maruel@chromium.org064186c2011-09-27 23:53:33 +0000160class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000161 """Immutable configuration settings."""
162 def __init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000163 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000164 custom_hooks, deps_file, should_process):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000165 GClientKeywords.__init__(self)
166
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000167 # These are not mutable:
168 self._parent = parent
169 self._safesync_url = safesync_url
170 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000171 self._url = url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000172 # 'managed' determines whether or not this dependency is synced/updated by
173 # gclient after gclient checks it out initially. The difference between
174 # 'managed' and 'should_process' is that the user specifies 'managed' via
175 # the --unmanaged command-line flag or a .gclient config, where
176 # 'should_process' is dynamically set by gclient if it goes over its
177 # recursion limit and controls gclient's behavior so it does not misbehave.
178 self._managed = managed
179 self._should_process = should_process
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000180 # This is a mutable value that overrides the normal recursion limit for this
181 # dependency. It is read from the actual DEPS file so cannot be set on
182 # class instantiation.
183 self.recursion_override = None
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000184 # This is a mutable value which has the list of 'target_os' OSes listed in
185 # the current deps file.
186 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000187
188 # These are only set in .gclient and not in DEPS files.
189 self._custom_vars = custom_vars or {}
190 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000191 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000192
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000193 # TODO(iannucci): Remove this when all masters are correctly substituting
194 # the new blink url.
195 if (self._custom_vars.get('webkit_trunk', '') ==
196 'svn://svn-mirror.golo.chromium.org/webkit-readonly/trunk'):
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000197 new_url = 'svn://svn-mirror.golo.chromium.org/blink/trunk'
198 print 'Overwriting Var("webkit_trunk") with %s' % new_url
199 self._custom_vars['webkit_trunk'] = new_url
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000200
maruel@chromium.org064186c2011-09-27 23:53:33 +0000201 # Post process the url to remove trailing slashes.
202 if isinstance(self._url, basestring):
203 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
204 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000205 self._url = self._url.replace('/@', '@')
206 elif not isinstance(self._url,
207 (self.FromImpl, self.FileImpl, None.__class__)):
208 raise gclient_utils.Error(
209 ('dependency url must be either a string, None, '
210 'File() or From() instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000211 # Make any deps_file path platform-appropriate.
212 for sep in ['/', '\\']:
213 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000214
215 @property
216 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000217 return self._deps_file
218
219 @property
220 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000221 return self._managed
222
223 @property
224 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000225 return self._parent
226
227 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000228 def root(self):
229 """Returns the root node, a GClient object."""
230 if not self.parent:
231 # This line is to signal pylint that it could be a GClient instance.
232 return self or GClient(None, None)
233 return self.parent.root
234
235 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000236 def safesync_url(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000237 return self._safesync_url
238
239 @property
240 def should_process(self):
241 """True if this dependency should be processed, i.e. checked out."""
242 return self._should_process
243
244 @property
245 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000246 return self._custom_vars.copy()
247
248 @property
249 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000250 return self._custom_deps.copy()
251
maruel@chromium.org064186c2011-09-27 23:53:33 +0000252 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000253 def custom_hooks(self):
254 return self._custom_hooks[:]
255
256 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000257 def url(self):
258 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000259
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000260 @property
261 def recursion_limit(self):
262 """Returns > 0 if this dependency is not too recursed to be processed."""
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000263 if self.recursion_override is not None:
264 return self.recursion_override
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000265 return max(self.parent.recursion_limit - 1, 0)
266
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000267 @property
268 def target_os(self):
269 if self.local_target_os is not None:
270 return tuple(set(self.local_target_os).union(self.parent.target_os))
271 else:
272 return self.parent.target_os
273
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000274 def get_custom_deps(self, name, url):
275 """Returns a custom deps if applicable."""
276 if self.parent:
277 url = self.parent.get_custom_deps(name, url)
278 # None is a valid return value to disable a dependency.
279 return self.custom_deps.get(name, url)
280
maruel@chromium.org064186c2011-09-27 23:53:33 +0000281
282class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000283 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000284
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000285 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000286 custom_vars, custom_hooks, deps_file, should_process):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000287 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000288 DependencySettings.__init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000289 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000290 custom_hooks, deps_file, should_process)
maruel@chromium.org68988972011-09-20 14:11:42 +0000291
292 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000293 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000294
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000295 self._pre_deps_hooks = []
296
maruel@chromium.org68988972011-09-20 14:11:42 +0000297 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000298 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000299 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000300 # A cache of the files affected by the current operation, necessary for
301 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000302 self._file_list = []
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000303 # If it is not set to True, the dependency wasn't processed for its child
304 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000305 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000306 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000307 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000308 # This dependency had its pre-DEPS hooks run
309 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000310 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000311 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000312 # This is the scm used to checkout self.url. It may be used by dependencies
313 # to get the datetime of the revision we checked out.
314 self._used_scm = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000315 # The actual revision we ended up getting, or None if that information is
316 # unavailable
317 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000318
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000319 if not self.name and self.parent:
320 raise gclient_utils.Error('Dependency without name')
321
maruel@chromium.org470b5432011-10-11 18:18:19 +0000322 @property
323 def requirements(self):
324 """Calculate the list of requirements."""
325 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000326 # self.parent is implicitly a requirement. This will be recursive by
327 # definition.
328 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000329 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000330
331 # For a tree with at least 2 levels*, the leaf node needs to depend
332 # on the level higher up in an orderly way.
333 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
334 # thus unsorted, while the .gclient format is a list thus sorted.
335 #
336 # * _recursion_limit is hard coded 2 and there is no hope to change this
337 # value.
338 #
339 # Interestingly enough, the following condition only works in the case we
340 # want: self is a 2nd level node. 3nd level node wouldn't need this since
341 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000342 if self.parent and self.parent.parent and not self.parent.parent.parent:
343 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000344
345 if isinstance(self.url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000346 requirements.add(self.url.module_name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000347
maruel@chromium.org470b5432011-10-11 18:18:19 +0000348 if self.name:
349 requirements |= set(
350 obj.name for obj in self.root.subtree(False)
351 if (obj is not self
352 and obj.name and
353 self.name.startswith(posixpath.join(obj.name, ''))))
354 requirements = tuple(sorted(requirements))
355 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
356 return requirements
357
358 def verify_validity(self):
359 """Verifies that this Dependency is fine to add as a child of another one.
360
361 Returns True if this entry should be added, False if it is a duplicate of
362 another entry.
363 """
364 logging.info('Dependency(%s).verify_validity()' % self.name)
365 if self.name in [s.name for s in self.parent.dependencies]:
366 raise gclient_utils.Error(
367 'The same name "%s" appears multiple times in the deps section' %
368 self.name)
369 if not self.should_process:
370 # Return early, no need to set requirements.
371 return True
372
373 # This require a full tree traversal with locks.
374 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
375 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000376 self_url = self.LateOverride(self.url)
377 sibling_url = sibling.LateOverride(sibling.url)
378 # Allow to have only one to be None or ''.
379 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000380 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000381 ('Dependency %s specified more than once:\n'
382 ' %s [%s]\n'
383 'vs\n'
384 ' %s [%s]') % (
385 self.name,
386 sibling.hierarchy(),
387 sibling_url,
388 self.hierarchy(),
389 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000390 # In theory we could keep it as a shadow of the other one. In
391 # practice, simply ignore it.
392 logging.warn('Won\'t process duplicate dependency %s' % sibling)
393 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000394 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000395
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000396 def LateOverride(self, url):
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000397 """Resolves the parsed url from url.
398
399 Manages From() keyword accordingly. Do not touch self.parsed_url nor
400 self.url because it may called with other urls due to From()."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000401 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000402 parsed_url = self.get_custom_deps(self.name, url)
403 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000404 logging.info(
405 'Dependency(%s).LateOverride(%s) -> %s' %
406 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000407 return parsed_url
408
409 if isinstance(url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000410 # Requires tree traversal.
maruel@chromium.org68988972011-09-20 14:11:42 +0000411 ref = [
412 dep for dep in self.root.subtree(True) if url.module_name == dep.name
413 ]
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000414 if not ref:
415 raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
416 url.module_name, ref))
417 # It may happen that len(ref) > 1 but it's no big deal.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000418 ref = ref[0]
maruel@chromium.org98d05fa2010-07-22 21:58:01 +0000419 sub_target = url.sub_target_name or self.name
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000420 found_deps = [d for d in ref.dependencies if d.name == sub_target]
421 if len(found_deps) != 1:
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000422 raise gclient_utils.Error(
maruel@chromium.org98023df2011-09-07 18:44:47 +0000423 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
424 sub_target, ref.name, self.name, self.parent.name,
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000425 str(self.root)))
maruel@chromium.org98023df2011-09-07 18:44:47 +0000426
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000427 # Call LateOverride() again.
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000428 found_dep = found_deps[0]
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000429 parsed_url = found_dep.LateOverride(found_dep.url)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000430 logging.info(
maruel@chromium.org470b5432011-10-11 18:18:19 +0000431 'Dependency(%s).LateOverride(%s) -> %s (From)' %
432 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000433 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000434
435 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000436 parsed_url = urlparse.urlparse(url)
437 if not parsed_url[0]:
438 # A relative url. Fetch the real base.
439 path = parsed_url[2]
440 if not path.startswith('/'):
441 raise gclient_utils.Error(
442 'relative DEPS entry \'%s\' must begin with a slash' % url)
443 # Create a scm just to query the full url.
444 parent_url = self.parent.parsed_url
445 if isinstance(parent_url, self.FileImpl):
446 parent_url = parent_url.file_location
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000447 scm = gclient_scm.CreateSCM(parent_url, self.root.root_dir, None)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000448 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000449 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000450 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000451 logging.info(
452 'Dependency(%s).LateOverride(%s) -> %s' %
453 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000454 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000455
456 if isinstance(url, self.FileImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000457 logging.info(
458 'Dependency(%s).LateOverride(%s) -> %s (File)' %
459 (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000460 return url
461
462 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000463 logging.info(
464 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000465 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000466
467 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000468
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000469 @staticmethod
470 def MergeWithOsDeps(deps, deps_os, target_os_list):
471 """Returns a new "deps" structure that is the deps sent in updated
472 with information from deps_os (the deps_os section of the DEPS
473 file) that matches the list of target os."""
474 os_overrides = {}
475 for the_target_os in target_os_list:
476 the_target_os_deps = deps_os.get(the_target_os, {})
477 for os_dep_key, os_dep_value in the_target_os_deps.iteritems():
478 overrides = os_overrides.setdefault(os_dep_key, [])
479 overrides.append((the_target_os, os_dep_value))
480
481 # If any os didn't specify a value (we have fewer value entries
482 # than in the os list), then it wants to use the default value.
483 for os_dep_key, os_dep_value in os_overrides.iteritems():
484 if len(os_dep_value) != len(target_os_list):
485 # Record the default value too so that we don't accidently
486 # set it to None or miss a conflicting DEPS.
487 if os_dep_key in deps:
488 os_dep_value.append(('default', deps[os_dep_key]))
489
490 target_os_deps = {}
491 for os_dep_key, os_dep_value in os_overrides.iteritems():
492 # os_dep_value is a list of (os, value) pairs.
493 possible_values = set(x[1] for x in os_dep_value if x[1] is not None)
494 if not possible_values:
495 target_os_deps[os_dep_key] = None
496 else:
497 if len(possible_values) > 1:
498 # It would be possible to abort here but it would be
499 # unfortunate if we end up preventing any kind of checkout.
500 logging.error('Conflicting dependencies for %s: %s. (target_os=%s)',
501 os_dep_key, os_dep_value, target_os_list)
502 # Sorting to get the same result every time in case of conflicts.
503 target_os_deps[os_dep_key] = sorted(possible_values)[0]
504
505 new_deps = deps.copy()
506 new_deps.update(target_os_deps)
507 return new_deps
508
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000509 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000510 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000511 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000512 assert not self.dependencies
513 # One thing is unintuitive, vars = {} must happen before Var() use.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000514 local_scope = {}
515 var = self.VarImpl(self.custom_vars, local_scope)
516 global_scope = {
517 'File': self.FileImpl,
518 'From': self.FromImpl,
519 'Var': var.Lookup,
520 'deps_os': {},
521 }
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000522 filepath = os.path.join(self.root.root_dir, self.name, self.deps_file)
maruel@chromium.org46304292010-10-28 11:42:00 +0000523 if not os.path.isfile(filepath):
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000524 logging.info(
525 'ParseDepsFile(%s): No %s file found at %s' % (
526 self.name, self.deps_file, filepath))
maruel@chromium.org46304292010-10-28 11:42:00 +0000527 else:
528 deps_content = gclient_utils.FileRead(filepath)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000529 logging.debug('ParseDepsFile(%s) read:\n%s' % (self.name, deps_content))
maruel@chromium.org46304292010-10-28 11:42:00 +0000530 # Eval the content.
531 try:
532 exec(deps_content, global_scope, local_scope)
533 except SyntaxError, e:
534 gclient_utils.SyntaxErrorToError(filepath, e)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000535 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000536 if 'recursion' in local_scope:
537 self.recursion_override = local_scope.get('recursion')
538 logging.warning(
539 'Setting %s recursion to %d.', self.name, self.recursion_limit)
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000540 # If present, save 'target_os' in the local_target_os property.
541 if 'target_os' in local_scope:
542 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000543 # load os specific dependencies if defined. these dependencies may
544 # override or extend the values defined by the 'deps' member.
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000545 target_os_list = self.target_os
546 if 'deps_os' in local_scope and target_os_list:
547 deps = self.MergeWithOsDeps(deps, local_scope['deps_os'], target_os_list)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000548
maruel@chromium.org271375b2010-06-23 19:17:38 +0000549 # If a line is in custom_deps, but not in the solution, we want to append
550 # this line to the solution.
551 for d in self.custom_deps:
552 if d not in deps:
553 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000554
555 # If use_relative_paths is set in the DEPS file, regenerate
556 # the dictionary using paths relative to the directory containing
557 # the DEPS file.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000558 use_relative_paths = local_scope.get('use_relative_paths', False)
559 if use_relative_paths:
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000560 rel_deps = {}
561 for d, url in deps.items():
562 # normpath is required to allow DEPS to use .. in their
563 # dependency local path.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000564 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url
565 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000566
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000567 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000568 deps_to_add = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000569 for name, url in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000570 should_process = self.recursion_limit and self.should_process
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000571 deps_to_add.append(Dependency(
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000572 self, name, url, None, None, None, None, None,
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000573 self.deps_file, should_process))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000574 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000575
576 # override named sets of hooks by the custom hooks
577 hooks_to_run = []
578 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
579 for hook in local_scope.get('hooks', []):
580 if hook.get('name', '') not in hook_names_to_suppress:
581 hooks_to_run.append(hook)
582
583 # add the replacements and any additions
584 for hook in self.custom_hooks:
585 if 'action' in hook:
586 hooks_to_run.append(hook)
587
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000588 self._pre_deps_hooks = [self.GetHookAction(hook, []) for hook in
589 local_scope.get('pre_deps_hooks', [])]
590
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000591 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000592 logging.info('ParseDepsFile(%s) done' % self.name)
593
594 def add_dependencies_and_close(self, deps_to_add, hooks):
595 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000596 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000597 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000598 self.add_dependency(dep)
599 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000600
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000601 def maybeGetParentRevision(
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000602 self, command, options, parsed_url, parent_name, revision_overrides):
603 """Uses revision/timestamp of parent if no explicit revision was specified.
604
605 If we are performing an update and --transitive is set, use
606 - the parent's revision if 'self.url' is in the same repository
607 - the parent's timestamp otherwise
608 to update 'self.url'. The used revision/timestamp will be set in
609 'options.revision'.
610 If we have an explicit revision do nothing.
611 """
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000612 if command == 'update' and options.transitive and not options.revision:
613 _, revision = gclient_utils.SplitUrlRevision(parsed_url)
614 if not revision:
615 options.revision = revision_overrides.get(parent_name)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000616 if (options.revision and
617 not gclient_utils.IsDateRevision(options.revision)):
618 assert self.parent and self.parent.used_scm
619 # If this dependency is in the same repository as parent it's url will
620 # start with a slash. If so we take the parent revision instead of
621 # it's timestamp.
622 # (The timestamps of commits in google code are broken -- which can
623 # result in dependencies to be checked out at the wrong revision)
624 if self.url.startswith('/'):
625 if options.verbose:
626 print('Using parent\'s revision %s since we are in the same '
627 'repository.' % options.revision)
628 else:
629 parent_revision_date = self.parent.used_scm.GetRevisionDate(
630 options.revision)
631 options.revision = gclient_utils.MakeDateRevision(
632 parent_revision_date)
633 if options.verbose:
634 print('Using parent\'s revision date %s since we are in a '
635 'different repository.' % options.revision)
636 revision_overrides[self.name] = options.revision
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000637
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000638 # Arguments number differs from overridden method
639 # pylint: disable=W0221
maruel@chromium.org3742c842010-09-09 19:27:14 +0000640 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000641 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000642 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000643 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000644 if not self.should_process:
645 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000646 # When running runhooks, there's no need to consult the SCM.
647 # All known hooks are expected to run unconditionally regardless of working
648 # copy state, so skip the SCM status check.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000649 run_scm = command not in ('runhooks', 'recurse', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000650 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000651 file_list = [] if not options.nohooks else None
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000652 if run_scm and parsed_url:
653 if isinstance(parsed_url, self.FileImpl):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000654 # Special support for single-file checkout.
655 if not command in (None, 'cleanup', 'diff', 'pack', 'status'):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000656 # Sadly, pylint doesn't realize that parsed_url is of FileImpl.
657 # pylint: disable=E1103
658 options.revision = parsed_url.GetRevision()
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000659 self._used_scm = gclient_scm.SVNWrapper(
660 parsed_url.GetPath(), self.root.root_dir, self.name)
661 self._used_scm.RunCommand('updatesingle',
662 options, args + [parsed_url.GetFilename()], file_list)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000663 else:
maruel@chromium.org9e5317a2010-08-13 20:35:11 +0000664 # Create a shallow copy to mutate revision.
665 options = copy.copy(options)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000666 options.revision = revision_overrides.get(self.name)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000667 self.maybeGetParentRevision(
668 command, options, parsed_url, self.parent.name, revision_overrides)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000669 self._used_scm = gclient_scm.CreateSCM(
670 parsed_url, self.root.root_dir, self.name)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000671 self._got_revision = self._used_scm.RunCommand(command, options, args,
672 file_list)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000673 if file_list:
674 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000675
676 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
677 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000678 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000679 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000680 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000681 continue
682 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000683 [self.root.root_dir.lower(), file_list[i].lower()])
684 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000685 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000686 while file_list[i].startswith(('\\', '/')):
687 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000688
689 # Always parse the DEPS file.
690 self.ParseDepsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000691 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000692 if command in ('update', 'revert') and not options.noprehooks:
693 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000694
695 if self.recursion_limit:
696 # Parse the dependencies of this dependency.
697 for s in self.dependencies:
698 work_queue.enqueue(s)
699
700 if command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000701 if not isinstance(parsed_url, self.FileImpl):
702 # Skip file only checkout.
703 scm = gclient_scm.GetScmName(parsed_url)
704 if not options.scm or scm in options.scm:
705 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000706 # Pass in the SCM type as an env variable. Make sure we don't put
707 # unicode strings in the environment.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000708 env = os.environ.copy()
709 if scm:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000710 env['GCLIENT_SCM'] = str(scm)
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000711 if parsed_url:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000712 env['GCLIENT_URL'] = str(parsed_url)
713 env['GCLIENT_DEP_PATH'] = str(self.name)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000714 if options.prepend_dir and scm == 'git':
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000715 print_stdout = False
716 def filter_fn(line):
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000717 """Git-specific path marshaling. It is optimized for git-grep."""
718
719 def mod_path(git_pathspec):
720 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
721 modified_path = os.path.join(self.name, match.group(2))
722 branch = match.group(1) or ''
723 return '%s%s' % (branch, modified_path)
724
725 match = re.match('^Binary file ([^\0]+) matches$', line)
726 if match:
727 print 'Binary file %s matches' % mod_path(match.group(1))
728 return
729
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000730 items = line.split('\0')
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000731 if len(items) == 2 and items[1]:
732 print '%s : %s' % (mod_path(items[0]), items[1])
733 elif len(items) >= 2:
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000734 # Multiple null bytes or a single trailing null byte indicate
735 # git is likely displaying filenames only (such as with -l)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000736 print '\n'.join(mod_path(path) for path in items if path)
737 else:
738 print line
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000739 else:
740 print_stdout = True
741 filter_fn = None
742
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000743 if parsed_url is None:
744 print >> sys.stderr, 'Skipped omitted dependency %s' % cwd
745 elif os.path.isdir(cwd):
maruel@chromium.org288054d2012-03-05 00:43:07 +0000746 try:
747 gclient_utils.CheckCallAndFilter(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000748 args, cwd=cwd, env=env, print_stdout=print_stdout,
749 filter_fn=filter_fn,
750 )
maruel@chromium.org288054d2012-03-05 00:43:07 +0000751 except subprocess2.CalledProcessError:
752 if not options.ignore:
753 raise
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000754 else:
755 print >> sys.stderr, 'Skipped missing %s' % cwd
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000756
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000757
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000758 @gclient_utils.lockedmethod
759 def _run_is_done(self, file_list, parsed_url):
760 # Both these are kept for hooks that are run as a separate tree traversal.
761 self._file_list = file_list
762 self._parsed_url = parsed_url
763 self._processed = True
764
szager@google.comb9a78d32012-03-13 18:46:21 +0000765 @staticmethod
766 def GetHookAction(hook_dict, matching_file_list):
767 """Turns a parsed 'hook' dict into an executable command."""
768 logging.debug(hook_dict)
769 logging.debug(matching_file_list)
770 command = hook_dict['action'][:]
771 if command[0] == 'python':
772 # If the hook specified "python" as the first item, the action is a
773 # Python script. Run it by starting a new copy of the same
774 # interpreter.
775 command[0] = sys.executable
776 if '$matching_files' in command:
777 splice_index = command.index('$matching_files')
778 command[splice_index:splice_index + 1] = matching_file_list
779 return command
780
781 def GetHooks(self, options):
782 """Evaluates all hooks, and return them in a flat list.
783
784 RunOnDeps() must have been called before to load the DEPS.
785 """
786 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000787 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000788 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000789 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000790 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000791 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000792 if self.deps_hooks:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000793 # TODO(maruel): If the user is using git or git-svn, then we don't know
794 # what files have changed so we always run all hooks. It'd be nice to fix
795 # that.
796 if (options.force or
797 isinstance(self.parsed_url, self.FileImpl) or
798 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000799 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000800 for hook_dict in self.deps_hooks:
szager@google.comb9a78d32012-03-13 18:46:21 +0000801 result.append(self.GetHookAction(hook_dict, []))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000802 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000803 # Run hooks on the basis of whether the files from the gclient operation
804 # match each hook's pattern.
805 for hook_dict in self.deps_hooks:
806 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000807 matching_file_list = [
808 f for f in self.file_list_and_children if pattern.search(f)
809 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000810 if matching_file_list:
szager@google.comb9a78d32012-03-13 18:46:21 +0000811 result.append(self.GetHookAction(hook_dict, matching_file_list))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000812 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000813 result.extend(s.GetHooks(options))
814 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000815
szager@google.comb9a78d32012-03-13 18:46:21 +0000816 def RunHooksRecursively(self, options):
817 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000818 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000819 for hook in self.GetHooks(options):
820 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000821 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +0000822 gclient_utils.CheckCallAndFilterAndHeader(
823 hook, cwd=self.root.root_dir, always=True)
824 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
825 # Use a discrete exit status code of 2 to indicate that a hook action
826 # failed. Users of this script may wish to treat hook action failures
827 # differently from VC failures.
828 print >> sys.stderr, 'Error: %s' % str(e)
829 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000830 finally:
831 elapsed_time = time.time() - start_time
832 if elapsed_time > 10:
833 print "Hook '%s' took %.2f secs" % (
834 gclient_utils.CommandToStr(hook), elapsed_time)
maruel@chromium.orgeaf61062010-07-07 18:42:39 +0000835
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000836 def RunPreDepsHooks(self):
837 assert self.processed
838 assert self.deps_parsed
839 assert not self.pre_deps_hooks_ran
840 assert not self.hooks_ran
841 for s in self.dependencies:
842 assert not s.processed
843 self._pre_deps_hooks_ran = True
844 for hook in self.pre_deps_hooks:
845 try:
846 start_time = time.time()
847 gclient_utils.CheckCallAndFilterAndHeader(
848 hook, cwd=self.root.root_dir, always=True)
849 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
850 # Use a discrete exit status code of 2 to indicate that a hook action
851 # failed. Users of this script may wish to treat hook action failures
852 # differently from VC failures.
853 print >> sys.stderr, 'Error: %s' % str(e)
854 sys.exit(2)
855 finally:
856 elapsed_time = time.time() - start_time
857 if elapsed_time > 10:
858 print "Hook '%s' took %.2f secs" % (
859 gclient_utils.CommandToStr(hook), elapsed_time)
860
861
maruel@chromium.org0d812442010-08-10 12:41:08 +0000862 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000863 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000864 dependencies = self.dependencies
865 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000866 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000867 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000868 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000869 for i in d.subtree(include_all):
870 yield i
871
872 def depth_first_tree(self):
873 """Depth-first recursion including the root node."""
874 yield self
875 for i in self.dependencies:
876 for j in i.depth_first_tree():
877 if j.should_process:
878 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +0000879
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000880 @gclient_utils.lockedmethod
881 def add_dependency(self, new_dep):
882 self._dependencies.append(new_dep)
883
884 @gclient_utils.lockedmethod
885 def _mark_as_parsed(self, new_hooks):
886 self._deps_hooks.extend(new_hooks)
887 self._deps_parsed = True
888
maruel@chromium.org68988972011-09-20 14:11:42 +0000889 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000890 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000891 def dependencies(self):
892 return tuple(self._dependencies)
893
894 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000895 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000896 def deps_hooks(self):
897 return tuple(self._deps_hooks)
898
899 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000900 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000901 def pre_deps_hooks(self):
902 return tuple(self._pre_deps_hooks)
903
904 @property
905 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000906 def parsed_url(self):
907 return self._parsed_url
908
909 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000910 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000911 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000912 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +0000913 return self._deps_parsed
914
915 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000916 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000917 def processed(self):
918 return self._processed
919
920 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000921 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000922 def pre_deps_hooks_ran(self):
923 return self._pre_deps_hooks_ran
924
925 @property
926 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000927 def hooks_ran(self):
928 return self._hooks_ran
929
930 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000931 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000932 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000933 return tuple(self._file_list)
934
935 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000936 def used_scm(self):
937 """SCMWrapper instance for this dependency or None if not processed yet."""
938 return self._used_scm
939
940 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000941 @gclient_utils.lockedmethod
942 def got_revision(self):
943 return self._got_revision
944
945 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000946 def file_list_and_children(self):
947 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000948 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000949 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +0000950 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000951
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000952 def __str__(self):
953 out = []
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +0000954 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +0000955 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000956 'processed', 'hooks_ran', 'deps_parsed', 'requirements'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +0000957 # First try the native property if it exists.
958 if hasattr(self, '_' + i):
959 value = getattr(self, '_' + i, False)
960 else:
961 value = getattr(self, i, False)
962 if value:
963 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000964
965 for d in self.dependencies:
966 out.extend([' ' + x for x in str(d).splitlines()])
967 out.append('')
968 return '\n'.join(out)
969
970 def __repr__(self):
971 return '%s: %s' % (self.name, self.url)
972
maruel@chromium.orgbffb9042010-07-22 20:59:36 +0000973 def hierarchy(self):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +0000974 """Returns a human-readable hierarchical reference to a Dependency."""
maruel@chromium.orgbffb9042010-07-22 20:59:36 +0000975 out = '%s(%s)' % (self.name, self.url)
976 i = self.parent
977 while i and i.name:
978 out = '%s(%s) -> %s' % (i.name, i.url, out)
979 i = i.parent
980 return out
981
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000982
983class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +0000984 """Object that represent a gclient checkout. A tree of Dependency(), one per
985 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000986
987 DEPS_OS_CHOICES = {
988 "win32": "win",
989 "win": "win",
990 "cygwin": "win",
991 "darwin": "mac",
992 "mac": "mac",
993 "unix": "unix",
994 "linux": "unix",
995 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +0000996 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +0000997 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +0000998 }
999
1000 DEFAULT_CLIENT_FILE_TEXT = ("""\
1001solutions = [
1002 { "name" : "%(solution_name)s",
1003 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001004 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001005 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001006 "custom_deps" : {
1007 },
maruel@chromium.org73e21142010-07-05 13:32:01 +00001008 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001009 },
1010]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001011cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001012""")
1013
1014 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
1015 { "name" : "%(solution_name)s",
1016 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001017 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001018 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001019 "custom_deps" : {
maruel@chromium.org73e21142010-07-05 13:32:01 +00001020%(solution_deps)s },
1021 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001022 },
1023""")
1024
1025 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1026# Snapshot generated with gclient revinfo --snapshot
1027solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001028%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001029""")
1030
1031 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001032 # Do not change previous behavior. Only solution level and immediate DEPS
1033 # are processed.
1034 self._recursion_limit = 2
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001035 Dependency.__init__(self, None, None, None, None, True, None, None, None,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001036 'unused', True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001037 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001038 if options.deps_os:
1039 enforced_os = options.deps_os.split(',')
1040 else:
1041 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1042 if 'all' in enforced_os:
1043 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001044 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001045 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001046 self.config_content = None
1047
borenet@google.com88d10082014-03-21 17:24:48 +00001048 def _CheckConfig(self):
1049 """Verify that the config matches the state of the existing checked-out
1050 solutions."""
1051 for dep in self.dependencies:
1052 if dep.managed and dep.url:
1053 scm = gclient_scm.CreateSCM(dep.url, self.root_dir, dep.name)
borenet@google.com4e9be262014-04-08 19:40:30 +00001054 actual_url = scm.GetActualRemoteURL(self._options)
1055 if actual_url and not scm.DoesRemoteURLMatch(self._options):
borenet@google.com0a427372014-04-02 19:12:13 +00001056 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001057Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001058is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001059
borenet@google.com97882362014-04-07 20:06:02 +00001060The .gclient file contains:
1061%(expected_url)s (%(expected_scm)s)
1062
1063The local checkout in %(checkout_path)s reports:
1064%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001065
1066You should ensure that the URL listed in .gclient is correct and either change
1067it or fix the checkout. If you're managing your own git checkout in
1068%(checkout_path)s but the URL in .gclient is for an svn repository, you probably
1069want to set 'managed': False in .gclient.
borenet@google.com88d10082014-03-21 17:24:48 +00001070''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1071 'expected_url': dep.url,
1072 'expected_scm': gclient_scm.GetScmName(dep.url),
1073 'actual_url': actual_url,
1074 'actual_scm': gclient_scm.GetScmName(actual_url)})
1075
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001076 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001077 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001078 config_dict = {}
1079 self.config_content = content
1080 try:
1081 exec(content, config_dict)
1082 except SyntaxError, e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001083 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001084
peter@chromium.org1efccc82012-04-27 16:34:38 +00001085 # Append any target OS that is not already being enforced to the tuple.
1086 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001087 if config_dict.get('target_os_only', False):
1088 self._enforced_os = tuple(set(target_os))
1089 else:
1090 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1091
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001092 gclient_scm.GitWrapper.cache_dir = config_dict.get('cache_dir')
1093
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001094 if not target_os and config_dict.get('target_os_only', False):
1095 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1096 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001097
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001098 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001099 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001100 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001101 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +00001102 self, s['name'], s['url'],
1103 s.get('safesync_url', None),
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001104 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001105 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001106 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001107 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001108 s.get('deps_file', 'DEPS'),
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001109 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001110 except KeyError:
1111 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1112 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001113 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1114 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001115
1116 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001117 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001118 self._options.config_filename),
1119 self.config_content)
1120
1121 @staticmethod
1122 def LoadCurrentConfig(options):
1123 """Searches for and loads a .gclient file relative to the current working
1124 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001125 if options.spec:
1126 client = GClient('.', options)
1127 client.SetConfig(options.spec)
1128 else:
1129 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1130 if not path:
1131 return None
1132 client = GClient(path, options)
1133 client.SetConfig(gclient_utils.FileRead(
1134 os.path.join(path, options.config_filename)))
maruel@chromium.org69392e72011-10-13 22:09:00 +00001135
1136 if (options.revisions and
1137 len(client.dependencies) > 1 and
1138 any('@' not in r for r in options.revisions)):
1139 print >> sys.stderr, (
1140 'You must specify the full solution name like --revision %s@%s\n'
1141 'when you have multiple solutions setup in your .gclient file.\n'
1142 'Other solutions present are: %s.') % (
1143 client.dependencies[0].name,
1144 options.revisions[0],
1145 ', '.join(s.name for s in client.dependencies[1:]))
maruel@chromium.org15804092010-09-02 17:07:37 +00001146 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001147
nsylvain@google.comefc80932011-05-31 21:27:56 +00001148 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001149 safesync_url, managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001150 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1151 'solution_name': solution_name,
1152 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001153 'deps_file': deps_file,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001154 'safesync_url' : safesync_url,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001155 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001156 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001157 })
1158
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001159 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001160 """Creates a .gclient_entries file to record the list of unique checkouts.
1161
1162 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001163 """
1164 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1165 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001166 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001167 for entry in self.root.subtree(False):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001168 # Skip over File() dependencies as we can't version them.
1169 if not isinstance(entry.parsed_url, self.FileImpl):
1170 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1171 pprint.pformat(entry.parsed_url))
1172 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001173 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001174 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001175 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001176
1177 def _ReadEntries(self):
1178 """Read the .gclient_entries file for the given client.
1179
1180 Returns:
1181 A sequence of solution names, which will be empty if there is the
1182 entries file hasn't been created yet.
1183 """
1184 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001185 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001186 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001187 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001188 try:
1189 exec(gclient_utils.FileRead(filename), scope)
1190 except SyntaxError, e:
1191 gclient_utils.SyntaxErrorToError(filename, e)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001192 return scope['entries']
1193
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001194 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001195 """Checks for revision overrides."""
1196 revision_overrides = {}
maruel@chromium.org307d1792010-05-31 20:03:13 +00001197 if self._options.head:
1198 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001199 # Do not check safesync_url if one or more --revision flag is specified.
1200 if not self._options.revisions:
1201 for s in self.dependencies:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001202 if not s.managed:
1203 self._options.revisions.append('%s@unmanaged' % s.name)
1204 elif s.safesync_url:
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001205 self._ApplySafeSyncRev(dep=s)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001206 if not self._options.revisions:
1207 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001208 solutions_names = [s.name for s in self.dependencies]
maruel@chromium.org307d1792010-05-31 20:03:13 +00001209 index = 0
1210 for revision in self._options.revisions:
1211 if not '@' in revision:
1212 # Support for --revision 123
1213 revision = '%s@%s' % (solutions_names[index], revision)
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001214 sol, rev = revision.split('@', 1)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001215 if not sol in solutions_names:
1216 #raise gclient_utils.Error('%s is not a valid solution.' % sol)
1217 print >> sys.stderr, ('Please fix your script, having invalid '
1218 '--revision flags will soon considered an error.')
1219 else:
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001220 revision_overrides[sol] = rev
maruel@chromium.org307d1792010-05-31 20:03:13 +00001221 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001222 return revision_overrides
1223
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001224 def _ApplySafeSyncRev(self, dep):
1225 """Finds a valid revision from the content of the safesync_url and apply it
1226 by appending revisions to the revision list. Throws if revision appears to
1227 be invalid for the given |dep|."""
1228 assert len(dep.safesync_url) > 0
1229 handle = urllib.urlopen(dep.safesync_url)
1230 rev = handle.read().strip()
1231 handle.close()
1232 if not rev:
1233 raise gclient_utils.Error(
1234 'It appears your safesync_url (%s) is not working properly\n'
1235 '(as it returned an empty response). Check your config.' %
1236 dep.safesync_url)
1237 scm = gclient_scm.CreateSCM(dep.url, dep.root.root_dir, dep.name)
iannucci@chromium.org4a4b33b2013-07-04 20:25:46 +00001238 safe_rev = scm.GetUsableRev(rev, self._options)
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001239 if self._options.verbose:
1240 print('Using safesync_url revision: %s.\n' % safe_rev)
1241 self._options.revisions.append('%s@%s' % (dep.name, safe_rev))
1242
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001243 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001244 """Runs a command on each dependency in a client and its dependencies.
1245
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001246 Args:
1247 command: The command to use (e.g., 'status' or 'diff')
1248 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001249 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001250 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001251 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001252
1253 self._CheckConfig()
1254
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001255 revision_overrides = {}
1256 # It's unnecessary to check for revision overrides for 'recurse'.
1257 # Save a few seconds by not calling _EnforceRevisions() in that case.
dbeam@chromium.org0f8a9442012-07-10 14:50:20 +00001258 if command not in ('diff', 'recurse', 'runhooks', 'status'):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001259 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001260 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001261 # Disable progress for non-tty stdout.
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001262 if (sys.stdout.isatty() and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001263 if command in ('update', 'revert'):
1264 pm = Progress('Syncing projects', 1)
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +00001265 elif command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001266 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001267 work_queue = gclient_utils.ExecutionQueue(
1268 self._options.jobs, pm, ignore_requirements=ignore_requirements)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001269 for s in self.dependencies:
1270 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001271 work_queue.flush(revision_overrides, command, args, options=self._options)
piman@chromium.org6f363722010-04-27 00:41:09 +00001272
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001273 # Once all the dependencies have been processed, it's now safe to run the
1274 # hooks.
1275 if not self._options.nohooks:
1276 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001277
1278 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001279 # Notify the user if there is an orphaned entry in their working copy.
1280 # Only delete the directory if there are no changes in it, and
1281 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001282 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001283 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1284 for e in entries]
1285
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001286 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001287 if not prev_url:
1288 # entry must have been overridden via .gclient custom_deps
1289 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001290 # Fix path separator on Windows.
1291 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001292 e_dir = os.path.join(self.root_dir, entry_fixed)
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001293
1294 def _IsParentOfAny(parent, path_list):
1295 parent_plus_slash = parent + '/'
1296 return any(
1297 path[:len(parent_plus_slash)] == parent_plus_slash
1298 for path in path_list)
1299
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001300 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001301 if (entry not in entries and
1302 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001303 os.path.exists(e_dir)):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001304 scm = gclient_scm.CreateSCM(prev_url, self.root_dir, entry_fixed)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001305
1306 # Check to see if this directory is now part of a higher-up checkout.
1307 if scm.GetCheckoutRoot() in full_entries:
1308 logging.info('%s is part of a higher level checkout, not '
1309 'removing.', scm.GetCheckoutRoot())
1310 continue
1311
1312 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001313 scm.status(self._options, [], file_list)
1314 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001315 if (not self._options.delete_unversioned_trees or
1316 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001317 # There are modified files in this entry. Keep warning until
1318 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001319 print(('\nWARNING: \'%s\' is no longer part of this client. '
1320 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001321 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001322 else:
1323 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001324 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001325 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001326 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001327 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001328 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001329 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001330
1331 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001332 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001333 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001334 # Load all the settings.
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001335 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, None, False)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001336 for s in self.dependencies:
1337 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001338 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001339
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001340 def GetURLAndRev(dep):
1341 """Returns the revision-qualified SCM url for a Dependency."""
1342 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001343 return None
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001344 if isinstance(dep.parsed_url, self.FileImpl):
1345 original_url = dep.parsed_url.file_location
1346 else:
1347 original_url = dep.parsed_url
nasser@codeaurora.org5d63eb82010-03-24 23:22:09 +00001348 url, _ = gclient_utils.SplitUrlRevision(original_url)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001349 scm = gclient_scm.CreateSCM(original_url, self.root_dir, dep.name)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001350 if not os.path.isdir(scm.checkout_path):
1351 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001352 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001353
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001354 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001355 new_gclient = ''
1356 # First level at .gclient
1357 for d in self.dependencies:
1358 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001359 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001360 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001361 for d in dep.dependencies:
1362 entries[d.name] = GetURLAndRev(d)
1363 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001364 GrabDeps(d)
1365 custom_deps = []
1366 for k in sorted(entries.keys()):
1367 if entries[k]:
1368 # Quotes aren't escaped...
1369 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1370 else:
1371 custom_deps.append(' \"%s\": None,\n' % k)
1372 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1373 'solution_name': d.name,
1374 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001375 'deps_file': d.deps_file,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001376 'safesync_url' : d.safesync_url or '',
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001377 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001378 'solution_deps': ''.join(custom_deps),
1379 }
1380 # Print the snapshot configuration file
1381 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001382 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001383 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001384 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001385 if self._options.actual:
1386 entries[d.name] = GetURLAndRev(d)
1387 else:
1388 entries[d.name] = d.parsed_url
1389 keys = sorted(entries.keys())
1390 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001391 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001392 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001393
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001394 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001395 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001396 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001397
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001398 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001399 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001400 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001401 return self._root_dir
1402
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001403 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001404 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001405 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001406 return self._enforced_os
1407
maruel@chromium.org68988972011-09-20 14:11:42 +00001408 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001409 def recursion_limit(self):
1410 """How recursive can each dependencies in DEPS file can load DEPS file."""
1411 return self._recursion_limit
1412
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001413 @property
1414 def target_os(self):
1415 return self._enforced_os
1416
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001417
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001418#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001419
1420
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001421def CMDcleanup(parser, args):
1422 """Cleans up all working copies.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001423
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001424 Mostly svn-specific. Simply runs 'svn cleanup' for each module.
1425 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001426 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1427 help='override deps for the specified (comma-separated) '
1428 'platform(s); \'all\' will process all deps_os '
1429 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001430 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001431 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001432 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001433 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001434 if options.verbose:
1435 # Print out the .gclient file. This is longer than if we just printed the
1436 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001437 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001438 return client.RunOnDeps('cleanup', args)
1439
1440
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001441@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001442def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001443 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001444
1445 Runs a shell command on all entries.
ilevy@chromium.org37116242012-11-28 01:32:48 +00001446 Sets GCLIENT_DEP_PATH enviroment variable as the dep's relative location to
1447 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001448 """
1449 # Stop parsing at the first non-arg so that these go through to the command
1450 parser.disable_interspersed_args()
1451 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001452 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001453 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001454 help='Ignore non-zero return codes from subcommands.')
1455 parser.add_option('--prepend-dir', action='store_true',
1456 help='Prepend relative dir for use with git <cmd> --null.')
1457 parser.add_option('--no-progress', action='store_true',
1458 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001459 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001460 if not args:
1461 print >> sys.stderr, 'Need to supply a command!'
1462 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001463 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1464 if not root_and_entries:
1465 print >> sys.stderr, (
1466 'You need to run gclient sync at least once to use \'recurse\'.\n'
1467 'This is because .gclient_entries needs to exist and be up to date.')
1468 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001469
1470 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001471 scm_set = set()
1472 for scm in options.scm:
1473 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001474 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001475
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001476 options.nohooks = True
1477 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001478 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1479 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001480
1481
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001482@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001483def CMDfetch(parser, args):
1484 """Fetches upstream commits for all modules.
1485
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001486 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1487 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001488 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001489 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001490 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1491
1492
1493def CMDgrep(parser, args):
1494 """Greps through git repos managed by gclient.
1495
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001496 Runs 'git grep [args...]' for each module.
1497 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001498 # We can't use optparse because it will try to parse arguments sent
1499 # to git grep and throw an error. :-(
1500 if not args or re.match('(-h|--help)$', args[0]):
1501 print >> sys.stderr, (
1502 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1503 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1504 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1505 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
1506 ' end of your query.'
1507 )
1508 return 1
1509
1510 jobs_arg = ['--jobs=1']
1511 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1512 jobs_arg, args = args[:1], args[1:]
1513 elif re.match(r'(-j|--jobs)$', args[0]):
1514 jobs_arg, args = args[:2], args[2:]
1515
1516 return CMDrecurse(
1517 parser,
1518 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1519 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001520
1521
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001522@subcommand.usage('[url] [safesync url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001523def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001524 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001525
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001526 This specifies the configuration for further commands. After update/sync,
1527 top-level DEPS files in each module are read to determine dependent
1528 modules to operate on as well. If optional [url] parameter is
1529 provided, then configuration is read from a specified Subversion server
1530 URL.
1531 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00001532 # We do a little dance with the --gclientfile option. 'gclient config' is the
1533 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1534 # arguments. So, we temporarily stash any --gclientfile parameter into
1535 # options.output_config_file until after the (gclientfile xor spec) error
1536 # check.
1537 parser.remove_option('--gclientfile')
1538 parser.add_option('--gclientfile', dest='output_config_file',
1539 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001540 parser.add_option('--name',
1541 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001542 parser.add_option('--deps-file', default='DEPS',
1543 help='overrides the default name for the DEPS file for the'
1544 'main solutions and all sub-dependencies')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001545 parser.add_option('--unmanaged', action='store_true', default=False,
1546 help='overrides the default behavior to make it possible '
1547 'to have the main solution untouched by gclient '
1548 '(gclient will check out unmanaged dependencies but '
1549 'will never sync them)')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001550 parser.add_option('--git-deps', action='store_true',
1551 help='sets the deps file to ".DEPS.git" instead of "DEPS"')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001552 parser.add_option('--cache-dir',
1553 help='(git only) Cache all git repos into this dir and do '
1554 'shared clones from the cache, instead of cloning '
1555 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001556 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001557 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001558 if options.output_config_file:
1559 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001560 if ((options.spec and args) or len(args) > 2 or
1561 (not options.spec and not args)):
1562 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1563
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001564 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001565 if options.spec:
1566 client.SetConfig(options.spec)
1567 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001568 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001569 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001570 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001571 if name.endswith('.git'):
1572 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001573 else:
1574 # specify an alternate relpath for the given URL.
1575 name = options.name
nsylvain@google.comefc80932011-05-31 21:27:56 +00001576 deps_file = options.deps_file
1577 if options.git_deps:
1578 deps_file = '.DEPS.git'
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001579 safesync_url = ''
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001580 if len(args) > 1:
1581 safesync_url = args[1]
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001582 client.SetDefaultConfig(name, deps_file, base_url, safesync_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001583 managed=not options.unmanaged,
1584 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001585 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001586 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001587
1588
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001589@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001590 gclient pack > patch.txt
1591 generate simple patch for configured client and dependences
1592""")
1593def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001594 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001595
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001596 Internally, runs 'svn diff'/'git diff' on each checked out module and
1597 dependencies, and performs minimal postprocessing of the output. The
1598 resulting patch is printed to stdout and can be applied to a freshly
1599 checked out tree via 'patch -p0 < patchfile'.
1600 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001601 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1602 help='override deps for the specified (comma-separated) '
1603 'platform(s); \'all\' will process all deps_os '
1604 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001605 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001606 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001607 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001608 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001609 client = GClient.LoadCurrentConfig(options)
1610 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001611 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001612 if options.verbose:
1613 # Print out the .gclient file. This is longer than if we just printed the
1614 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001615 print(client.config_content)
kbr@google.comab318592009-09-04 00:54:55 +00001616 return client.RunOnDeps('pack', args)
1617
1618
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001619def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001620 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001621 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1622 help='override deps for the specified (comma-separated) '
1623 'platform(s); \'all\' will process all deps_os '
1624 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001625 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001626 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001627 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001628 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001629 if options.verbose:
1630 # Print out the .gclient file. This is longer than if we just printed the
1631 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001632 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001633 return client.RunOnDeps('status', args)
1634
1635
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001636@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001637 gclient sync
1638 update files from SCM according to current configuration,
1639 *for modules which have changed since last update or sync*
1640 gclient sync --force
1641 update files from SCM according to current configuration, for
1642 all modules (useful for recovering files deleted from local copy)
1643 gclient sync --revision src@31000
1644 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001645
1646JSON output format:
1647If the --output-json option is specified, the following document structure will
1648be emitted to the provided file. 'null' entries may occur for subprojects which
1649are present in the gclient solution, but were not processed (due to custom_deps,
1650os_deps, etc.)
1651
1652{
1653 "solutions" : {
1654 "<name>": { # <name> is the posix-normalized path to the solution.
1655 "revision": [<svn rev int>|<git id hex string>|null],
1656 "scm": ["svn"|"git"|null],
1657 }
1658 }
1659}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001660""")
1661def CMDsync(parser, args):
1662 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001663 parser.add_option('-f', '--force', action='store_true',
1664 help='force update even for unchanged modules')
1665 parser.add_option('-n', '--nohooks', action='store_true',
1666 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001667 parser.add_option('-p', '--noprehooks', action='store_true',
1668 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001669 parser.add_option('-r', '--revision', action='append',
1670 dest='revisions', metavar='REV', default=[],
1671 help='Enforces revision/hash for the solutions with the '
1672 'format src@rev. The src@ part is optional and can be '
1673 'skipped. -r can be used multiple times when .gclient '
1674 'has multiple solutions configured and will work even '
joi@chromium.org792ea882010-11-10 02:37:27 +00001675 'if the src@ part is skipped. Note that specifying '
1676 '--revision means your safesync_url gets ignored.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00001677 parser.add_option('--with_branch_heads', action='store_true',
1678 help='Clone git "branch_heads" refspecs in addition to '
1679 'the default refspecs. This adds about 1/2GB to a '
1680 'full checkout. (git only)')
floitsch@google.comeaab7842011-04-28 09:07:58 +00001681 parser.add_option('-t', '--transitive', action='store_true',
1682 help='When a revision is specified (in the DEPS file or '
1683 'with the command-line flag), transitively update '
1684 'the dependencies to the date of the given revision. '
1685 'Only supported for SVN repositories.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001686 parser.add_option('-H', '--head', action='store_true',
1687 help='skips any safesync_urls specified in '
1688 'configured solutions and sync to head instead')
1689 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001690 help='Deletes from the working copy any dependencies that '
1691 'have been removed since the last sync, as long as '
1692 'there are no local modifications. When used with '
1693 '--force, such dependencies are removed even if they '
1694 'have local modifications. When used with --reset, '
1695 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00001696 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001697 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001698 parser.add_option('-R', '--reset', action='store_true',
1699 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00001700 parser.add_option('-M', '--merge', action='store_true',
1701 help='merge upstream changes instead of trying to '
1702 'fast-forward or rebase')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001703 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1704 help='override deps for the specified (comma-separated) '
1705 'platform(s); \'all\' will process all deps_os '
1706 'references')
1707 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
1708 help='Skip svn up whenever possible by requesting '
1709 'actual HEAD revision from the repository')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001710 parser.add_option('--upstream', action='store_true',
1711 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001712 parser.add_option('--output-json',
1713 help='Output a json document to this path containing '
1714 'summary information about the sync.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001715 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001716 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001717
1718 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001719 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001720
maruel@chromium.org307d1792010-05-31 20:03:13 +00001721 if options.revisions and options.head:
1722 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001723 print('Warning: you cannot use both --head and --revision')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001724
1725 if options.verbose:
1726 # Print out the .gclient file. This is longer than if we just printed the
1727 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001728 print(client.config_content)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001729 ret = client.RunOnDeps('update', args)
1730 if options.output_json:
1731 slns = {}
1732 for d in client.subtree(True):
1733 normed = d.name.replace('\\', '/').rstrip('/') + '/'
1734 slns[normed] = {
1735 'revision': d.got_revision,
1736 'scm': d.used_scm.name if d.used_scm else None,
1737 }
1738 with open(options.output_json, 'wb') as f:
1739 json.dump({'solutions': slns}, f)
1740 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001741
1742
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001743CMDupdate = CMDsync
1744
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001745
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001746def CMDdiff(parser, args):
1747 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001748 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1749 help='override deps for the specified (comma-separated) '
1750 'platform(s); \'all\' will process all deps_os '
1751 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001752 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001753 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001754 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001755 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001756 if options.verbose:
1757 # Print out the .gclient file. This is longer than if we just printed the
1758 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001759 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001760 return client.RunOnDeps('diff', args)
1761
1762
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001763def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001764 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001765
1766 That's the nuclear option to get back to a 'clean' state. It removes anything
1767 that shows up in svn status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001768 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1769 help='override deps for the specified (comma-separated) '
1770 'platform(s); \'all\' will process all deps_os '
1771 'references')
1772 parser.add_option('-n', '--nohooks', action='store_true',
1773 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001774 parser.add_option('-p', '--noprehooks', action='store_true',
1775 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001776 parser.add_option('--upstream', action='store_true',
1777 help='Make repo state match upstream branch.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001778 (options, args) = parser.parse_args(args)
1779 # --force is implied.
1780 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001781 options.reset = False
1782 options.delete_unversioned_trees = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001783 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001784 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001785 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001786 return client.RunOnDeps('revert', args)
1787
1788
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001789def CMDrunhooks(parser, args):
1790 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001791 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1792 help='override deps for the specified (comma-separated) '
1793 'platform(s); \'all\' will process all deps_os '
1794 'references')
1795 parser.add_option('-f', '--force', action='store_true', default=True,
1796 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001797 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001798 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001799 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001800 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001801 if options.verbose:
1802 # Print out the .gclient file. This is longer than if we just printed the
1803 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001804 print(client.config_content)
maruel@chromium.org5df6a462009-08-28 18:52:26 +00001805 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001806 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001807 return client.RunOnDeps('runhooks', args)
1808
1809
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001810def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001811 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001812
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001813 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001814 can be used to reproduce the same tree in the future. It is only useful for
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001815 'unpinned dependencies', i.e. DEPS/deps references without a svn revision
1816 number or a git hash. A git branch name isn't 'pinned' since the actual
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001817 commit can change.
1818 """
1819 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1820 help='override deps for the specified (comma-separated) '
1821 'platform(s); \'all\' will process all deps_os '
1822 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001823 parser.add_option('-a', '--actual', action='store_true',
1824 help='gets the actual checked out revisions instead of the '
1825 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00001826 parser.add_option('-s', '--snapshot', action='store_true',
1827 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001828 'version of all repositories to reproduce the tree, '
1829 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001830 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001831 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001832 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001833 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001834 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001835 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001836
1837
szager@google.comb9a78d32012-03-13 18:46:21 +00001838def CMDhookinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001839 """Outputs the hooks that would be run by `gclient runhooks`."""
szager@google.comb9a78d32012-03-13 18:46:21 +00001840 (options, args) = parser.parse_args(args)
1841 options.force = True
1842 client = GClient.LoadCurrentConfig(options)
1843 if not client:
1844 raise gclient_utils.Error('client not configured; see \'gclient config\'')
1845 client.RunOnDeps(None, [])
1846 print '; '.join(' '.join(hook) for hook in client.GetHooks(options))
1847 return 0
1848
1849
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001850class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00001851 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001852
1853 def __init__(self, **kwargs):
1854 optparse.OptionParser.__init__(
1855 self, version='%prog ' + __version__, **kwargs)
1856
1857 # Some arm boards have issues with parallel sync.
1858 if platform.machine().startswith('arm'):
1859 jobs = 1
1860 else:
1861 jobs = max(8, gclient_utils.NumLocalCpus())
1862 # cmp: 2013/06/19
1863 # Temporary workaround to lower bot-load on SVN server.
hinoka@google.com267f33e2014-02-28 22:02:32 +00001864 # Bypassed if a bot_update flag is detected.
1865 if (os.environ.get('CHROME_HEADLESS') == '1' and
1866 not os.path.exists('update.flag')):
xusydoc@chromium.org05028412013-07-29 13:40:10 +00001867 jobs = 1
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001868
1869 self.add_option(
1870 '-j', '--jobs', default=jobs, type='int',
1871 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00001872 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001873 self.add_option(
1874 '-v', '--verbose', action='count', default=0,
1875 help='Produces additional output for diagnostics. Can be used up to '
1876 'three times for more logging info.')
1877 self.add_option(
1878 '--gclientfile', dest='config_filename',
1879 help='Specify an alternate %s file' % self.gclientfile_default)
1880 self.add_option(
1881 '--spec',
1882 help='create a gclient file containing the provided string. Due to '
1883 'Cygwin/Python brokenness, it can\'t contain any newlines.')
1884 self.add_option(
1885 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00001886 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001887
1888 def parse_args(self, args=None, values=None):
1889 """Integrates standard options processing."""
1890 options, args = optparse.OptionParser.parse_args(self, args, values)
1891 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
1892 logging.basicConfig(
1893 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00001894 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001895 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001896 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00001897 if (options.config_filename and
1898 options.config_filename != os.path.basename(options.config_filename)):
1899 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001900 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001901 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00001902 options.entries_filename = options.config_filename + '_entries'
1903 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001904 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00001905
1906 # These hacks need to die.
1907 if not hasattr(options, 'revisions'):
1908 # GClient.RunOnDeps expects it even if not applicable.
1909 options.revisions = []
1910 if not hasattr(options, 'head'):
1911 options.head = None
1912 if not hasattr(options, 'nohooks'):
1913 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001914 if not hasattr(options, 'noprehooks'):
1915 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00001916 if not hasattr(options, 'deps_os'):
1917 options.deps_os = None
1918 if not hasattr(options, 'manually_grab_svn_rev'):
1919 options.manually_grab_svn_rev = None
1920 if not hasattr(options, 'force'):
1921 options.force = None
1922 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001923
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001924
1925def disable_buffering():
1926 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
1927 # operations. Python as a strong tendency to buffer sys.stdout.
1928 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
1929 # Make stdout annotated with the thread ids.
1930 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00001931
1932
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001933def Main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001934 """Doesn't parse the arguments here, just find the right subcommand to
1935 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00001936 if sys.hexversion < 0x02060000:
maruel@chromium.orgc3a15a22010-11-20 03:12:27 +00001937 print >> sys.stderr, (
maruel@chromium.org82798cb2012-02-23 18:16:12 +00001938 '\nYour python version %s is unsupported, please upgrade.\n' %
1939 sys.version.split(' ', 1)[0])
1940 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00001941 if not sys.executable:
1942 print >> sys.stderr, (
1943 '\nPython cannot find the location of it\'s own executable.\n')
1944 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001945 fix_encoding.fix_encoding()
1946 disable_buffering()
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +00001947 colorama.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001948 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001949 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001950 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00001951 except KeyboardInterrupt:
1952 gclient_utils.GClientChildren.KillAllRemainingChildren()
1953 raise
maruel@chromium.org31cb48a2011-04-04 18:01:36 +00001954 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001955 print >> sys.stderr, 'Error: %s' % str(e)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001956 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00001957 finally:
1958 gclient_utils.PrintWarnings()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001959
1960
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00001961if '__main__' == __name__:
maruel@chromium.org6e29d572010-06-04 17:32:20 +00001962 sys.exit(Main(sys.argv[1:]))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001963
1964# vim: ts=2:sw=2:tw=80:et: