blob: 15a75301ae7e202a6980985b8a78179035e93ffb [file] [log] [blame]
iannucci@chromium.org405b87e2015-11-12 18:08:34 +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
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +000079from __future__ import print_function
80
maruel@chromium.org39c0b222013-08-17 16:57:01 +000081__version__ = '0.7'
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000082
szager@chromium.org7b8b6de2014-08-23 00:57:31 +000083import ast
maruel@chromium.org9e5317a2010-08-13 20:35:11 +000084import copy
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +000085import json
maruel@chromium.org754960e2009-09-21 12:31:05 +000086import logging
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000087import optparse
88import os
bradnelson@google.com4949dab2012-04-19 16:41:07 +000089import platform
maruel@chromium.org621939b2010-08-10 20:12:00 +000090import posixpath
msb@chromium.org2e38de72009-09-28 17:04:47 +000091import pprint
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000092import re
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000093import sys
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +000094import time
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000095import urllib
bradnelson@google.com4949dab2012-04-19 16:41:07 +000096import urlparse
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000097
maruel@chromium.org35625c72011-03-23 17:34:02 +000098import fix_encoding
maruel@chromium.org5f3eee32009-09-17 00:34:30 +000099import gclient_scm
100import gclient_utils
szager@chromium.org848fd492014-04-09 19:06:44 +0000101import git_cache
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000102from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000103import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000104import subprocess2
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +0000105import setup_color
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000106
szager@chromium.org7b8b6de2014-08-23 00:57:31 +0000107CHROMIUM_SRC_URL = 'https://chromium.googlesource.com/chromium/src.git'
108
109
110def ast_dict_index(dnode, key):
111 """Search an ast.Dict for the argument key, and return its index."""
112 idx = [i for i in range(len(dnode.keys)) if (
113 type(dnode.keys[i]) is ast.Str and dnode.keys[i].s == key)]
114 if not idx:
115 return -1
116 elif len(idx) > 1:
117 raise gclient_utils.Error('Multiple dict entries with same key in AST')
118 return idx[-1]
119
120def ast2str(node, indent=0):
121 """Return a pretty-printed rendition of an ast.Node."""
122 t = type(node)
123 if t is ast.Module:
124 return '\n'.join([ast2str(x, indent) for x in node.body])
125 elif t is ast.Assign:
126 return ((' ' * indent) +
127 ' = '.join([ast2str(x) for x in node.targets] +
128 [ast2str(node.value, indent)]) + '\n')
129 elif t is ast.Name:
130 return node.id
131 elif t is ast.List:
132 if not node.elts:
133 return '[]'
134 elif len(node.elts) == 1:
135 return '[' + ast2str(node.elts[0], indent) + ']'
136 return ('[\n' + (' ' * (indent + 1)) +
137 (',\n' + (' ' * (indent + 1))).join(
138 [ast2str(x, indent + 1) for x in node.elts]) +
139 '\n' + (' ' * indent) + ']')
140 elif t is ast.Dict:
141 if not node.keys:
142 return '{}'
143 elif len(node.keys) == 1:
144 return '{%s: %s}' % (ast2str(node.keys[0]),
145 ast2str(node.values[0], indent + 1))
146 return ('{\n' + (' ' * (indent + 1)) +
147 (',\n' + (' ' * (indent + 1))).join(
148 ['%s: %s' % (ast2str(node.keys[i]),
149 ast2str(node.values[i], indent + 1))
150 for i in range(len(node.keys))]) +
151 '\n' + (' ' * indent) + '}')
152 elif t is ast.Str:
153 return "'%s'" % node.s
154 else:
155 raise gclient_utils.Error("Unexpected AST node at line %d, column %d: %s"
156 % (node.lineno, node.col_offset, t))
157
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000158
maruel@chromium.org116704f2010-06-11 17:34:38 +0000159class GClientKeywords(object):
160 class FromImpl(object):
161 """Used to implement the From() syntax."""
162
163 def __init__(self, module_name, sub_target_name=None):
164 """module_name is the dep module we want to include from. It can also be
165 the name of a subdirectory to include from.
166
167 sub_target_name is an optional parameter if the module name in the other
168 DEPS file is different. E.g., you might want to map src/net to net."""
169 self.module_name = module_name
170 self.sub_target_name = sub_target_name
171
172 def __str__(self):
173 return 'From(%s, %s)' % (repr(self.module_name),
174 repr(self.sub_target_name))
175
maruel@chromium.org116704f2010-06-11 17:34:38 +0000176 class FileImpl(object):
177 """Used to implement the File('') syntax which lets you sync a single file
maruel@chromium.orge3216c62010-07-08 03:31:43 +0000178 from a SVN repo."""
maruel@chromium.org116704f2010-06-11 17:34:38 +0000179
180 def __init__(self, file_location):
181 self.file_location = file_location
182
183 def __str__(self):
184 return 'File("%s")' % self.file_location
185
186 def GetPath(self):
187 return os.path.split(self.file_location)[0]
188
189 def GetFilename(self):
190 rev_tokens = self.file_location.split('@')
191 return os.path.split(rev_tokens[0])[1]
192
193 def GetRevision(self):
194 rev_tokens = self.file_location.split('@')
195 if len(rev_tokens) > 1:
196 return rev_tokens[1]
197 return None
198
199 class VarImpl(object):
200 def __init__(self, custom_vars, local_scope):
201 self._custom_vars = custom_vars
202 self._local_scope = local_scope
203
204 def Lookup(self, var_name):
205 """Implements the Var syntax."""
206 if var_name in self._custom_vars:
207 return self._custom_vars[var_name]
208 elif var_name in self._local_scope.get("vars", {}):
209 return self._local_scope["vars"][var_name]
210 raise gclient_utils.Error("Var is not defined: %s" % var_name)
211
212
maruel@chromium.org064186c2011-09-27 23:53:33 +0000213class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000214 """Immutable configuration settings."""
215 def __init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000216 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000217 custom_hooks, deps_file, should_process):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000218 GClientKeywords.__init__(self)
219
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000220 # These are not mutable:
221 self._parent = parent
222 self._safesync_url = safesync_url
mmoss@chromium.org8f93f792014-08-26 23:24:09 +0000223 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000224 self._url = url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000225 # 'managed' determines whether or not this dependency is synced/updated by
226 # gclient after gclient checks it out initially. The difference between
227 # 'managed' and 'should_process' is that the user specifies 'managed' via
228 # the --unmanaged command-line flag or a .gclient config, where
229 # 'should_process' is dynamically set by gclient if it goes over its
230 # recursion limit and controls gclient's behavior so it does not misbehave.
231 self._managed = managed
232 self._should_process = should_process
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000233 # This is a mutable value which has the list of 'target_os' OSes listed in
234 # the current deps file.
235 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000236
237 # These are only set in .gclient and not in DEPS files.
238 self._custom_vars = custom_vars or {}
239 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000240 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000241
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000242 # TODO(iannucci): Remove this when all masters are correctly substituting
243 # the new blink url.
244 if (self._custom_vars.get('webkit_trunk', '') ==
245 'svn://svn-mirror.golo.chromium.org/webkit-readonly/trunk'):
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000246 new_url = 'svn://svn-mirror.golo.chromium.org/blink/trunk'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000247 print('Overwriting Var("webkit_trunk") with %s' % new_url)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000248 self._custom_vars['webkit_trunk'] = new_url
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000249
maruel@chromium.org064186c2011-09-27 23:53:33 +0000250 # Post process the url to remove trailing slashes.
251 if isinstance(self._url, basestring):
252 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
253 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000254 self._url = self._url.replace('/@', '@')
255 elif not isinstance(self._url,
256 (self.FromImpl, self.FileImpl, None.__class__)):
257 raise gclient_utils.Error(
258 ('dependency url must be either a string, None, '
259 'File() or From() instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000260 # Make any deps_file path platform-appropriate.
261 for sep in ['/', '\\']:
262 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000263
264 @property
265 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000266 return self._deps_file
267
268 @property
269 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000270 return self._managed
271
272 @property
273 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000274 return self._parent
275
276 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000277 def root(self):
278 """Returns the root node, a GClient object."""
279 if not self.parent:
280 # This line is to signal pylint that it could be a GClient instance.
281 return self or GClient(None, None)
282 return self.parent.root
283
284 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000285 def safesync_url(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000286 return self._safesync_url
287
288 @property
289 def should_process(self):
290 """True if this dependency should be processed, i.e. checked out."""
291 return self._should_process
292
293 @property
294 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000295 return self._custom_vars.copy()
296
297 @property
298 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000299 return self._custom_deps.copy()
300
maruel@chromium.org064186c2011-09-27 23:53:33 +0000301 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000302 def custom_hooks(self):
303 return self._custom_hooks[:]
304
305 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000306 def url(self):
307 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000308
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000309 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000310 def target_os(self):
311 if self.local_target_os is not None:
312 return tuple(set(self.local_target_os).union(self.parent.target_os))
313 else:
314 return self.parent.target_os
315
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000316 def get_custom_deps(self, name, url):
317 """Returns a custom deps if applicable."""
318 if self.parent:
319 url = self.parent.get_custom_deps(name, url)
320 # None is a valid return value to disable a dependency.
321 return self.custom_deps.get(name, url)
322
maruel@chromium.org064186c2011-09-27 23:53:33 +0000323
324class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000325 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000326
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000327 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000328 custom_vars, custom_hooks, deps_file, should_process):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000329 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000330 DependencySettings.__init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000331 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000332 custom_hooks, deps_file, should_process)
maruel@chromium.org68988972011-09-20 14:11:42 +0000333
334 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000335 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000336
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000337 self._pre_deps_hooks = []
338
maruel@chromium.org68988972011-09-20 14:11:42 +0000339 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000340 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000341 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000342 # A cache of the files affected by the current operation, necessary for
343 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000344 self._file_list = []
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000345 # List of host names from which dependencies are allowed.
346 # Default is an empty set, meaning unspecified in DEPS file, and hence all
347 # hosts will be allowed. Non-empty set means whitelist of hosts.
348 # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
349 self._allowed_hosts = frozenset()
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000350 # If it is not set to True, the dependency wasn't processed for its child
351 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000352 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000353 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000354 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000355 # This dependency had its pre-DEPS hooks run
356 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000357 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000358 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000359 # This is the scm used to checkout self.url. It may be used by dependencies
360 # to get the datetime of the revision we checked out.
361 self._used_scm = None
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000362 self._used_revision = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000363 # The actual revision we ended up getting, or None if that information is
364 # unavailable
365 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000366
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000367 # This is a mutable value that overrides the normal recursion limit for this
368 # dependency. It is read from the actual DEPS file so cannot be set on
369 # class instantiation.
370 self.recursion_override = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000371 # recursedeps is a mutable value that selectively overrides the default
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000372 # 'no recursion' setting on a dep-by-dep basis. It will replace
373 # recursion_override.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000374 self.recursedeps = None
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000375
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000376 if not self.name and self.parent:
377 raise gclient_utils.Error('Dependency without name')
378
maruel@chromium.org470b5432011-10-11 18:18:19 +0000379 @property
380 def requirements(self):
381 """Calculate the list of requirements."""
382 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000383 # self.parent is implicitly a requirement. This will be recursive by
384 # definition.
385 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000386 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000387
388 # For a tree with at least 2 levels*, the leaf node needs to depend
389 # on the level higher up in an orderly way.
390 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
391 # thus unsorted, while the .gclient format is a list thus sorted.
392 #
393 # * _recursion_limit is hard coded 2 and there is no hope to change this
394 # value.
395 #
396 # Interestingly enough, the following condition only works in the case we
397 # want: self is a 2nd level node. 3nd level node wouldn't need this since
398 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000399 if self.parent and self.parent.parent and not self.parent.parent.parent:
400 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000401
402 if isinstance(self.url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000403 requirements.add(self.url.module_name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000404
maruel@chromium.org470b5432011-10-11 18:18:19 +0000405 if self.name:
406 requirements |= set(
407 obj.name for obj in self.root.subtree(False)
408 if (obj is not self
409 and obj.name and
410 self.name.startswith(posixpath.join(obj.name, ''))))
411 requirements = tuple(sorted(requirements))
412 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
413 return requirements
414
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000415 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000416 def try_recursedeps(self):
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000417 """Returns False if recursion_override is ever specified."""
418 if self.recursion_override is not None:
419 return False
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000420 return self.parent.try_recursedeps
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000421
422 @property
423 def recursion_limit(self):
424 """Returns > 0 if this dependency is not too recursed to be processed."""
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000425 # We continue to support the absence of recursedeps until tools and DEPS
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000426 # using recursion_override are updated.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000427 if self.try_recursedeps and self.parent.recursedeps != None:
428 if self.name in self.parent.recursedeps:
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000429 return 1
430
431 if self.recursion_override is not None:
432 return self.recursion_override
433 return max(self.parent.recursion_limit - 1, 0)
434
maruel@chromium.org470b5432011-10-11 18:18:19 +0000435 def verify_validity(self):
436 """Verifies that this Dependency is fine to add as a child of another one.
437
438 Returns True if this entry should be added, False if it is a duplicate of
439 another entry.
440 """
441 logging.info('Dependency(%s).verify_validity()' % self.name)
442 if self.name in [s.name for s in self.parent.dependencies]:
443 raise gclient_utils.Error(
444 'The same name "%s" appears multiple times in the deps section' %
445 self.name)
446 if not self.should_process:
447 # Return early, no need to set requirements.
448 return True
449
450 # This require a full tree traversal with locks.
451 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
452 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000453 self_url = self.LateOverride(self.url)
454 sibling_url = sibling.LateOverride(sibling.url)
455 # Allow to have only one to be None or ''.
456 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000457 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000458 ('Dependency %s specified more than once:\n'
459 ' %s [%s]\n'
460 'vs\n'
461 ' %s [%s]') % (
462 self.name,
463 sibling.hierarchy(),
464 sibling_url,
465 self.hierarchy(),
466 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000467 # In theory we could keep it as a shadow of the other one. In
468 # practice, simply ignore it.
469 logging.warn('Won\'t process duplicate dependency %s' % sibling)
470 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000471 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000472
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000473 def LateOverride(self, url):
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000474 """Resolves the parsed url from url.
475
476 Manages From() keyword accordingly. Do not touch self.parsed_url nor
477 self.url because it may called with other urls due to From()."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000478 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000479 parsed_url = self.get_custom_deps(self.name, url)
480 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000481 logging.info(
482 'Dependency(%s).LateOverride(%s) -> %s' %
483 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000484 return parsed_url
485
486 if isinstance(url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000487 # Requires tree traversal.
maruel@chromium.org68988972011-09-20 14:11:42 +0000488 ref = [
489 dep for dep in self.root.subtree(True) if url.module_name == dep.name
490 ]
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000491 if not ref:
492 raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
493 url.module_name, ref))
494 # It may happen that len(ref) > 1 but it's no big deal.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000495 ref = ref[0]
maruel@chromium.org98d05fa2010-07-22 21:58:01 +0000496 sub_target = url.sub_target_name or self.name
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000497 found_deps = [d for d in ref.dependencies if d.name == sub_target]
498 if len(found_deps) != 1:
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000499 raise gclient_utils.Error(
maruel@chromium.org98023df2011-09-07 18:44:47 +0000500 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
501 sub_target, ref.name, self.name, self.parent.name,
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000502 str(self.root)))
maruel@chromium.org98023df2011-09-07 18:44:47 +0000503
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000504 # Call LateOverride() again.
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000505 found_dep = found_deps[0]
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000506 parsed_url = found_dep.LateOverride(found_dep.url)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000507 logging.info(
maruel@chromium.org470b5432011-10-11 18:18:19 +0000508 'Dependency(%s).LateOverride(%s) -> %s (From)' %
509 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000510 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000511
512 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000513 parsed_url = urlparse.urlparse(url)
scr@chromium.orgf1eccaf2014-04-11 15:51:33 +0000514 if (not parsed_url[0] and
515 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000516 # A relative url. Fetch the real base.
517 path = parsed_url[2]
518 if not path.startswith('/'):
519 raise gclient_utils.Error(
520 'relative DEPS entry \'%s\' must begin with a slash' % url)
521 # Create a scm just to query the full url.
522 parent_url = self.parent.parsed_url
523 if isinstance(parent_url, self.FileImpl):
524 parent_url = parent_url.file_location
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000525 scm = gclient_scm.CreateSCM(
526 parent_url, self.root.root_dir, None, self.outbuf)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000527 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000528 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000529 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000530 logging.info(
531 'Dependency(%s).LateOverride(%s) -> %s' %
532 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000533 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000534
535 if isinstance(url, self.FileImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000536 logging.info(
537 'Dependency(%s).LateOverride(%s) -> %s (File)' %
538 (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000539 return url
540
541 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000542 logging.info(
543 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000544 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000545
546 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000547
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000548 @staticmethod
549 def MergeWithOsDeps(deps, deps_os, target_os_list):
550 """Returns a new "deps" structure that is the deps sent in updated
551 with information from deps_os (the deps_os section of the DEPS
552 file) that matches the list of target os."""
553 os_overrides = {}
554 for the_target_os in target_os_list:
555 the_target_os_deps = deps_os.get(the_target_os, {})
556 for os_dep_key, os_dep_value in the_target_os_deps.iteritems():
557 overrides = os_overrides.setdefault(os_dep_key, [])
558 overrides.append((the_target_os, os_dep_value))
559
560 # If any os didn't specify a value (we have fewer value entries
561 # than in the os list), then it wants to use the default value.
562 for os_dep_key, os_dep_value in os_overrides.iteritems():
563 if len(os_dep_value) != len(target_os_list):
564 # Record the default value too so that we don't accidently
565 # set it to None or miss a conflicting DEPS.
566 if os_dep_key in deps:
567 os_dep_value.append(('default', deps[os_dep_key]))
568
569 target_os_deps = {}
570 for os_dep_key, os_dep_value in os_overrides.iteritems():
571 # os_dep_value is a list of (os, value) pairs.
572 possible_values = set(x[1] for x in os_dep_value if x[1] is not None)
573 if not possible_values:
574 target_os_deps[os_dep_key] = None
575 else:
576 if len(possible_values) > 1:
577 # It would be possible to abort here but it would be
578 # unfortunate if we end up preventing any kind of checkout.
579 logging.error('Conflicting dependencies for %s: %s. (target_os=%s)',
580 os_dep_key, os_dep_value, target_os_list)
581 # Sorting to get the same result every time in case of conflicts.
582 target_os_deps[os_dep_key] = sorted(possible_values)[0]
583
584 new_deps = deps.copy()
585 new_deps.update(target_os_deps)
586 return new_deps
587
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000588 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000589 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000590 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000591 assert not self.dependencies
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000592
593 deps_content = None
594 use_strict = False
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000595
596 # First try to locate the configured deps file. If it's missing, fallback
597 # to DEPS.
598 deps_files = [self.deps_file]
599 if 'DEPS' not in deps_files:
600 deps_files.append('DEPS')
601 for deps_file in deps_files:
602 filepath = os.path.join(self.root.root_dir, self.name, deps_file)
603 if os.path.isfile(filepath):
604 logging.info(
605 'ParseDepsFile(%s): %s file found at %s', self.name, deps_file,
606 filepath)
607 break
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000608 logging.info(
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000609 'ParseDepsFile(%s): No %s file found at %s', self.name, deps_file,
610 filepath)
611
612 if os.path.isfile(filepath):
maruel@chromium.org46304292010-10-28 11:42:00 +0000613 deps_content = gclient_utils.FileRead(filepath)
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000614 logging.debug('ParseDepsFile(%s) read:\n%s', self.name, deps_content)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000615 use_strict = 'use strict' in deps_content.splitlines()[0]
616
617 local_scope = {}
618 if deps_content:
619 # One thing is unintuitive, vars = {} must happen before Var() use.
620 var = self.VarImpl(self.custom_vars, local_scope)
621 if use_strict:
622 logging.info(
623 'ParseDepsFile(%s): Strict Mode Enabled', self.name)
624 global_scope = {
625 '__builtins__': {'None': None},
626 'Var': var.Lookup,
627 'deps_os': {},
628 }
629 else:
630 global_scope = {
631 'File': self.FileImpl,
632 'From': self.FromImpl,
633 'Var': var.Lookup,
634 'deps_os': {},
635 }
maruel@chromium.org46304292010-10-28 11:42:00 +0000636 # Eval the content.
637 try:
638 exec(deps_content, global_scope, local_scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000639 except SyntaxError as e:
maruel@chromium.org46304292010-10-28 11:42:00 +0000640 gclient_utils.SyntaxErrorToError(filepath, e)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000641 if use_strict:
642 for key, val in local_scope.iteritems():
643 if not isinstance(val, (dict, list, tuple, str)):
644 raise gclient_utils.Error(
645 'ParseDepsFile(%s): Strict mode disallows %r -> %r' %
646 (self.name, key, val))
647
maruel@chromium.org271375b2010-06-23 19:17:38 +0000648 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000649 if 'recursion' in local_scope:
650 self.recursion_override = local_scope.get('recursion')
651 logging.warning(
652 'Setting %s recursion to %d.', self.name, self.recursion_limit)
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000653 self.recursedeps = local_scope.get('recursedeps', None)
654 if 'recursedeps' in local_scope:
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000655 self.recursedeps = set(self.recursedeps)
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000656 logging.warning('Found recursedeps %r.', repr(self.recursedeps))
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000657 # If present, save 'target_os' in the local_target_os property.
658 if 'target_os' in local_scope:
659 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000660 # load os specific dependencies if defined. these dependencies may
661 # override or extend the values defined by the 'deps' member.
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000662 target_os_list = self.target_os
663 if 'deps_os' in local_scope and target_os_list:
664 deps = self.MergeWithOsDeps(deps, local_scope['deps_os'], target_os_list)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000665
maruel@chromium.org271375b2010-06-23 19:17:38 +0000666 # If a line is in custom_deps, but not in the solution, we want to append
667 # this line to the solution.
668 for d in self.custom_deps:
669 if d not in deps:
670 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000671
672 # If use_relative_paths is set in the DEPS file, regenerate
673 # the dictionary using paths relative to the directory containing
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000674 # the DEPS file. Also update recursedeps if use_relative_paths is
675 # enabled.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000676 use_relative_paths = local_scope.get('use_relative_paths', False)
677 if use_relative_paths:
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000678 logging.warning('use_relative_paths enabled.')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000679 rel_deps = {}
680 for d, url in deps.items():
681 # normpath is required to allow DEPS to use .. in their
682 # dependency local path.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000683 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000684 logging.warning('Updating deps by prepending %s.', self.name)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000685 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000686
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000687 # Update recursedeps if it's set.
688 if self.recursedeps is not None:
689 logging.warning('Updating recursedeps by prepending %s.', self.name)
690 rel_deps = set()
691 for d in self.recursedeps:
692 rel_deps.add(os.path.normpath(os.path.join(self.name, d)))
693 self.recursedeps = rel_deps
694
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000695 if 'allowed_hosts' in local_scope:
696 try:
697 self._allowed_hosts = frozenset(local_scope.get('allowed_hosts'))
698 except TypeError: # raised if non-iterable
699 pass
700 if not self._allowed_hosts:
701 logging.warning("allowed_hosts is specified but empty %s",
702 self._allowed_hosts)
703 raise gclient_utils.Error(
704 'ParseDepsFile(%s): allowed_hosts must be absent '
705 'or a non-empty iterable' % self.name)
706
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000707 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000708 deps_to_add = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000709 for name, url in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000710 should_process = self.recursion_limit and self.should_process
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000711 deps_to_add.append(Dependency(
rwalker@codeaurora.orged89ff52016-03-31 19:18:40 +0000712 self, name, url, None, None, None, self.custom_vars, None,
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000713 self.deps_file, should_process))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000714 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000715
716 # override named sets of hooks by the custom hooks
717 hooks_to_run = []
718 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
719 for hook in local_scope.get('hooks', []):
720 if hook.get('name', '') not in hook_names_to_suppress:
721 hooks_to_run.append(hook)
722
723 # add the replacements and any additions
724 for hook in self.custom_hooks:
725 if 'action' in hook:
726 hooks_to_run.append(hook)
727
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000728 self._pre_deps_hooks = [self.GetHookAction(hook, []) for hook in
729 local_scope.get('pre_deps_hooks', [])]
730
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000731 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000732 logging.info('ParseDepsFile(%s) done' % self.name)
733
734 def add_dependencies_and_close(self, deps_to_add, hooks):
735 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000736 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000737 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000738 self.add_dependency(dep)
739 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000740
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000741 def maybeGetParentRevision(self, command, options, parsed_url, parent):
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000742 """Uses revision/timestamp of parent if no explicit revision was specified.
743
744 If we are performing an update and --transitive is set, use
745 - the parent's revision if 'self.url' is in the same repository
746 - the parent's timestamp otherwise
747 to update 'self.url'. The used revision/timestamp will be set in
748 'options.revision'.
749 If we have an explicit revision do nothing.
750 """
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000751 if command == 'update' and options.transitive and not options.revision:
752 _, revision = gclient_utils.SplitUrlRevision(parsed_url)
753 if not revision:
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000754 options.revision = getattr(parent, '_used_revision', None)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000755 if (options.revision and
756 not gclient_utils.IsDateRevision(options.revision)):
757 assert self.parent and self.parent.used_scm
758 # If this dependency is in the same repository as parent it's url will
759 # start with a slash. If so we take the parent revision instead of
760 # it's timestamp.
761 # (The timestamps of commits in google code are broken -- which can
762 # result in dependencies to be checked out at the wrong revision)
763 if self.url.startswith('/'):
764 if options.verbose:
765 print('Using parent\'s revision %s since we are in the same '
766 'repository.' % options.revision)
767 else:
768 parent_revision_date = self.parent.used_scm.GetRevisionDate(
769 options.revision)
770 options.revision = gclient_utils.MakeDateRevision(
771 parent_revision_date)
772 if options.verbose:
773 print('Using parent\'s revision date %s since we are in a '
774 'different repository.' % options.revision)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000775
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000776 def findDepsFromNotAllowedHosts(self):
777 """Returns a list of depenecies from not allowed hosts.
778
779 If allowed_hosts is not set, allows all hosts and returns empty list.
780 """
781 if not self._allowed_hosts:
782 return []
783 bad_deps = []
784 for dep in self._dependencies:
szager@chromium.orgbd772dd2014-11-05 18:43:08 +0000785 # Don't enforce this for custom_deps.
786 if dep.name in self._custom_deps:
787 continue
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000788 if isinstance(dep.url, basestring):
789 parsed_url = urlparse.urlparse(dep.url)
790 if parsed_url.netloc and parsed_url.netloc not in self._allowed_hosts:
791 bad_deps.append(dep)
792 return bad_deps
793
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000794 # Arguments number differs from overridden method
795 # pylint: disable=W0221
maruel@chromium.org3742c842010-09-09 19:27:14 +0000796 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000797 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000798 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000799 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000800 if not self.should_process:
801 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000802 # When running runhooks, there's no need to consult the SCM.
803 # All known hooks are expected to run unconditionally regardless of working
804 # copy state, so skip the SCM status check.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000805 run_scm = command not in ('runhooks', 'recurse', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000806 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000807 file_list = [] if not options.nohooks else None
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000808 revision_override = revision_overrides.pop(self.name, None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000809 if run_scm and parsed_url:
810 if isinstance(parsed_url, self.FileImpl):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000811 # Special support for single-file checkout.
812 if not command in (None, 'cleanup', 'diff', 'pack', 'status'):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000813 # Sadly, pylint doesn't realize that parsed_url is of FileImpl.
814 # pylint: disable=E1103
815 options.revision = parsed_url.GetRevision()
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000816 self._used_scm = gclient_scm.SVNWrapper(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000817 parsed_url.GetPath(), self.root.root_dir, self.name,
818 out_cb=work_queue.out_cb)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000819 self._used_scm.RunCommand('updatesingle',
820 options, args + [parsed_url.GetFilename()], file_list)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000821 else:
maruel@chromium.org9e5317a2010-08-13 20:35:11 +0000822 # Create a shallow copy to mutate revision.
823 options = copy.copy(options)
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000824 options.revision = revision_override
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000825 self.maybeGetParentRevision(
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000826 command, options, parsed_url, self.parent)
827 self._used_revision = options.revision
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000828 self._used_scm = gclient_scm.CreateSCM(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000829 parsed_url, self.root.root_dir, self.name, self.outbuf,
830 out_cb=work_queue.out_cb)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000831 self._got_revision = self._used_scm.RunCommand(command, options, args,
832 file_list)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000833 if file_list:
834 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000835
836 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
837 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000838 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000839 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000840 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000841 continue
842 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000843 [self.root.root_dir.lower(), file_list[i].lower()])
844 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000845 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000846 while file_list[i].startswith(('\\', '/')):
847 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000848
849 # Always parse the DEPS file.
850 self.ParseDepsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000851 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000852 if command in ('update', 'revert') and not options.noprehooks:
853 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000854
855 if self.recursion_limit:
856 # Parse the dependencies of this dependency.
857 for s in self.dependencies:
858 work_queue.enqueue(s)
859
860 if command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000861 if not isinstance(parsed_url, self.FileImpl):
862 # Skip file only checkout.
863 scm = gclient_scm.GetScmName(parsed_url)
864 if not options.scm or scm in options.scm:
865 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000866 # Pass in the SCM type as an env variable. Make sure we don't put
867 # unicode strings in the environment.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000868 env = os.environ.copy()
869 if scm:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000870 env['GCLIENT_SCM'] = str(scm)
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000871 if parsed_url:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000872 env['GCLIENT_URL'] = str(parsed_url)
873 env['GCLIENT_DEP_PATH'] = str(self.name)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000874 if options.prepend_dir and scm == 'git':
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000875 print_stdout = False
876 def filter_fn(line):
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000877 """Git-specific path marshaling. It is optimized for git-grep."""
878
879 def mod_path(git_pathspec):
880 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
881 modified_path = os.path.join(self.name, match.group(2))
882 branch = match.group(1) or ''
883 return '%s%s' % (branch, modified_path)
884
885 match = re.match('^Binary file ([^\0]+) matches$', line)
886 if match:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000887 print('Binary file %s matches\n' % mod_path(match.group(1)))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000888 return
889
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000890 items = line.split('\0')
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000891 if len(items) == 2 and items[1]:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000892 print('%s : %s' % (mod_path(items[0]), items[1]))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000893 elif len(items) >= 2:
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000894 # Multiple null bytes or a single trailing null byte indicate
895 # git is likely displaying filenames only (such as with -l)
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000896 print('\n'.join(mod_path(path) for path in items if path))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000897 else:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000898 print(line)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000899 else:
900 print_stdout = True
901 filter_fn = None
902
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000903 if parsed_url is None:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000904 print('Skipped omitted dependency %s' % cwd, file=sys.stderr)
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000905 elif os.path.isdir(cwd):
maruel@chromium.org288054d2012-03-05 00:43:07 +0000906 try:
907 gclient_utils.CheckCallAndFilter(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000908 args, cwd=cwd, env=env, print_stdout=print_stdout,
909 filter_fn=filter_fn,
910 )
maruel@chromium.org288054d2012-03-05 00:43:07 +0000911 except subprocess2.CalledProcessError:
912 if not options.ignore:
913 raise
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000914 else:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000915 print('Skipped missing %s' % cwd, file=sys.stderr)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000916
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000917
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000918 @gclient_utils.lockedmethod
919 def _run_is_done(self, file_list, parsed_url):
920 # Both these are kept for hooks that are run as a separate tree traversal.
921 self._file_list = file_list
922 self._parsed_url = parsed_url
923 self._processed = True
924
szager@google.comb9a78d32012-03-13 18:46:21 +0000925 @staticmethod
926 def GetHookAction(hook_dict, matching_file_list):
927 """Turns a parsed 'hook' dict into an executable command."""
928 logging.debug(hook_dict)
929 logging.debug(matching_file_list)
930 command = hook_dict['action'][:]
931 if command[0] == 'python':
932 # If the hook specified "python" as the first item, the action is a
933 # Python script. Run it by starting a new copy of the same
934 # interpreter.
935 command[0] = sys.executable
936 if '$matching_files' in command:
937 splice_index = command.index('$matching_files')
938 command[splice_index:splice_index + 1] = matching_file_list
939 return command
940
941 def GetHooks(self, options):
942 """Evaluates all hooks, and return them in a flat list.
943
944 RunOnDeps() must have been called before to load the DEPS.
945 """
946 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000947 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000948 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000949 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000950 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000951 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000952 if self.deps_hooks:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000953 # TODO(maruel): If the user is using git or git-svn, then we don't know
954 # what files have changed so we always run all hooks. It'd be nice to fix
955 # that.
956 if (options.force or
957 isinstance(self.parsed_url, self.FileImpl) or
958 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000959 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000960 for hook_dict in self.deps_hooks:
szager@google.comb9a78d32012-03-13 18:46:21 +0000961 result.append(self.GetHookAction(hook_dict, []))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000962 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000963 # Run hooks on the basis of whether the files from the gclient operation
964 # match each hook's pattern.
965 for hook_dict in self.deps_hooks:
966 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000967 matching_file_list = [
968 f for f in self.file_list_and_children if pattern.search(f)
969 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000970 if matching_file_list:
szager@google.comb9a78d32012-03-13 18:46:21 +0000971 result.append(self.GetHookAction(hook_dict, matching_file_list))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000972 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000973 result.extend(s.GetHooks(options))
974 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000975
szager@google.comb9a78d32012-03-13 18:46:21 +0000976 def RunHooksRecursively(self, options):
977 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000978 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000979 for hook in self.GetHooks(options):
980 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000981 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +0000982 gclient_utils.CheckCallAndFilterAndHeader(
983 hook, cwd=self.root.root_dir, always=True)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000984 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
szager@google.comb9a78d32012-03-13 18:46:21 +0000985 # Use a discrete exit status code of 2 to indicate that a hook action
986 # failed. Users of this script may wish to treat hook action failures
987 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000988 print('Error: %s' % str(e), file=sys.stderr)
szager@google.comb9a78d32012-03-13 18:46:21 +0000989 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000990 finally:
991 elapsed_time = time.time() - start_time
992 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000993 print("Hook '%s' took %.2f secs" % (
994 gclient_utils.CommandToStr(hook), elapsed_time))
maruel@chromium.orgeaf61062010-07-07 18:42:39 +0000995
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000996 def RunPreDepsHooks(self):
997 assert self.processed
998 assert self.deps_parsed
999 assert not self.pre_deps_hooks_ran
1000 assert not self.hooks_ran
1001 for s in self.dependencies:
1002 assert not s.processed
1003 self._pre_deps_hooks_ran = True
1004 for hook in self.pre_deps_hooks:
1005 try:
1006 start_time = time.time()
1007 gclient_utils.CheckCallAndFilterAndHeader(
1008 hook, cwd=self.root.root_dir, always=True)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001009 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001010 # Use a discrete exit status code of 2 to indicate that a hook action
1011 # failed. Users of this script may wish to treat hook action failures
1012 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001013 print('Error: %s' % str(e), file=sys.stderr)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001014 sys.exit(2)
1015 finally:
1016 elapsed_time = time.time() - start_time
1017 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001018 print("Hook '%s' took %.2f secs" % (
1019 gclient_utils.CommandToStr(hook), elapsed_time))
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001020
1021
maruel@chromium.org0d812442010-08-10 12:41:08 +00001022 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001023 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001024 dependencies = self.dependencies
1025 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001026 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001027 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001028 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001029 for i in d.subtree(include_all):
1030 yield i
1031
1032 def depth_first_tree(self):
1033 """Depth-first recursion including the root node."""
1034 yield self
1035 for i in self.dependencies:
1036 for j in i.depth_first_tree():
1037 if j.should_process:
1038 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +00001039
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001040 @gclient_utils.lockedmethod
1041 def add_dependency(self, new_dep):
1042 self._dependencies.append(new_dep)
1043
1044 @gclient_utils.lockedmethod
1045 def _mark_as_parsed(self, new_hooks):
1046 self._deps_hooks.extend(new_hooks)
1047 self._deps_parsed = True
1048
maruel@chromium.org68988972011-09-20 14:11:42 +00001049 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001050 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +00001051 def dependencies(self):
1052 return tuple(self._dependencies)
1053
1054 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001055 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001056 def deps_hooks(self):
1057 return tuple(self._deps_hooks)
1058
1059 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001060 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001061 def pre_deps_hooks(self):
1062 return tuple(self._pre_deps_hooks)
1063
1064 @property
1065 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001066 def parsed_url(self):
1067 return self._parsed_url
1068
1069 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001070 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001071 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +00001072 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +00001073 return self._deps_parsed
1074
1075 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001076 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001077 def processed(self):
1078 return self._processed
1079
1080 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001081 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001082 def pre_deps_hooks_ran(self):
1083 return self._pre_deps_hooks_ran
1084
1085 @property
1086 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001087 def hooks_ran(self):
1088 return self._hooks_ran
1089
1090 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001091 @gclient_utils.lockedmethod
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001092 def allowed_hosts(self):
1093 return self._allowed_hosts
1094
1095 @property
1096 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001097 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001098 return tuple(self._file_list)
1099
1100 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +00001101 def used_scm(self):
1102 """SCMWrapper instance for this dependency or None if not processed yet."""
1103 return self._used_scm
1104
1105 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001106 @gclient_utils.lockedmethod
1107 def got_revision(self):
1108 return self._got_revision
1109
1110 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001111 def file_list_and_children(self):
1112 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001113 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001114 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +00001115 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001116
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001117 def __str__(self):
1118 out = []
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001119 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001120 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001121 'processed', 'hooks_ran', 'deps_parsed', 'requirements',
1122 'allowed_hosts'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001123 # First try the native property if it exists.
1124 if hasattr(self, '_' + i):
1125 value = getattr(self, '_' + i, False)
1126 else:
1127 value = getattr(self, i, False)
1128 if value:
1129 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001130
1131 for d in self.dependencies:
1132 out.extend([' ' + x for x in str(d).splitlines()])
1133 out.append('')
1134 return '\n'.join(out)
1135
1136 def __repr__(self):
1137 return '%s: %s' % (self.name, self.url)
1138
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001139 def hierarchy(self):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +00001140 """Returns a human-readable hierarchical reference to a Dependency."""
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001141 out = '%s(%s)' % (self.name, self.url)
1142 i = self.parent
1143 while i and i.name:
1144 out = '%s(%s) -> %s' % (i.name, i.url, out)
1145 i = i.parent
1146 return out
1147
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001148
1149class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001150 """Object that represent a gclient checkout. A tree of Dependency(), one per
1151 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001152
1153 DEPS_OS_CHOICES = {
1154 "win32": "win",
1155 "win": "win",
1156 "cygwin": "win",
1157 "darwin": "mac",
1158 "mac": "mac",
1159 "unix": "unix",
1160 "linux": "unix",
1161 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +00001162 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +00001163 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001164 }
1165
1166 DEFAULT_CLIENT_FILE_TEXT = ("""\
1167solutions = [
1168 { "name" : "%(solution_name)s",
1169 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001170 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001171 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001172 "custom_deps" : {
1173 },
maruel@chromium.org73e21142010-07-05 13:32:01 +00001174 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001175 },
1176]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001177cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001178""")
1179
1180 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
1181 { "name" : "%(solution_name)s",
1182 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001183 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001184 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001185 "custom_deps" : {
maruel@chromium.org73e21142010-07-05 13:32:01 +00001186%(solution_deps)s },
1187 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001188 },
1189""")
1190
1191 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1192# Snapshot generated with gclient revinfo --snapshot
1193solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001194%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001195""")
1196
1197 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001198 # Do not change previous behavior. Only solution level and immediate DEPS
1199 # are processed.
1200 self._recursion_limit = 2
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001201 Dependency.__init__(self, None, None, None, None, True, None, None, None,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001202 'unused', True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001203 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001204 if options.deps_os:
1205 enforced_os = options.deps_os.split(',')
1206 else:
1207 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1208 if 'all' in enforced_os:
1209 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001210 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001211 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001212 self.config_content = None
1213
borenet@google.com88d10082014-03-21 17:24:48 +00001214 def _CheckConfig(self):
1215 """Verify that the config matches the state of the existing checked-out
1216 solutions."""
1217 for dep in self.dependencies:
1218 if dep.managed and dep.url:
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001219 scm = gclient_scm.CreateSCM(
1220 dep.url, self.root_dir, dep.name, self.outbuf)
smut@google.comd33eab32014-07-07 19:35:18 +00001221 actual_url = scm.GetActualRemoteURL(self._options)
borenet@google.com4e9be262014-04-08 19:40:30 +00001222 if actual_url and not scm.DoesRemoteURLMatch(self._options):
borenet@google.com0a427372014-04-02 19:12:13 +00001223 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001224Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001225is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001226
borenet@google.com97882362014-04-07 20:06:02 +00001227The .gclient file contains:
1228%(expected_url)s (%(expected_scm)s)
1229
1230The local checkout in %(checkout_path)s reports:
1231%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001232
1233You should ensure that the URL listed in .gclient is correct and either change
1234it or fix the checkout. If you're managing your own git checkout in
1235%(checkout_path)s but the URL in .gclient is for an svn repository, you probably
1236want to set 'managed': False in .gclient.
borenet@google.com88d10082014-03-21 17:24:48 +00001237''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1238 'expected_url': dep.url,
1239 'expected_scm': gclient_scm.GetScmName(dep.url),
1240 'actual_url': actual_url,
1241 'actual_scm': gclient_scm.GetScmName(actual_url)})
1242
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001243 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001244 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001245 config_dict = {}
1246 self.config_content = content
1247 try:
1248 exec(content, config_dict)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001249 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001250 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001251
peter@chromium.org1efccc82012-04-27 16:34:38 +00001252 # Append any target OS that is not already being enforced to the tuple.
1253 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001254 if config_dict.get('target_os_only', False):
1255 self._enforced_os = tuple(set(target_os))
1256 else:
1257 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1258
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001259 cache_dir = config_dict.get('cache_dir')
1260 if cache_dir:
1261 cache_dir = os.path.join(self.root_dir, cache_dir)
1262 cache_dir = os.path.abspath(cache_dir)
szager@chromium.orgcaf5bef2014-08-24 18:56:32 +00001263 # If running on a bot, force break any stale git cache locks.
dnj@chromium.orgb682b3e2014-08-25 19:17:12 +00001264 if os.path.exists(cache_dir) and os.environ.get('CHROME_HEADLESS'):
szager@chromium.org4848fb62014-08-24 19:16:31 +00001265 subprocess2.check_call(['git', 'cache', 'unlock', '--cache-dir',
1266 cache_dir, '--force', '--all'])
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001267 gclient_scm.GitWrapper.cache_dir = cache_dir
1268 git_cache.Mirror.SetCachePath(cache_dir)
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001269
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001270 if not target_os and config_dict.get('target_os_only', False):
1271 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1272 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001273
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001274 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001275 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001276 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001277 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +00001278 self, s['name'], s['url'],
1279 s.get('safesync_url', None),
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001280 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001281 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001282 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001283 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001284 s.get('deps_file', 'DEPS'),
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001285 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001286 except KeyError:
1287 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1288 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001289 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1290 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001291
1292 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001293 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001294 self._options.config_filename),
1295 self.config_content)
1296
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001297 def MigrateConfigToGit(self, path, options):
1298 svn_url_re = re.compile('^(https?://src\.chromium\.org/svn|'
1299 'svn://svn\.chromium\.org/chrome)/'
1300 '(trunk|branches/[^/]+)/src')
1301 old_git_re = re.compile('^(https?://git\.chromium\.org|'
1302 'ssh://([a-zA-Z_][a-zA-Z0-9_-]*@)?'
1303 'gerrit\.chromium\.org(:2941[89])?)/'
1304 'chromium/src\.git')
1305 # Scan existing .gclient file for obsolete settings. It would be simpler
1306 # to traverse self.dependencies, but working with the AST allows the code to
1307 # dump an updated .gclient file that preserves the ordering of the original.
1308 a = ast.parse(self.config_content, options.config_filename, 'exec')
1309 modified = False
1310 solutions = [elem for elem in a.body if 'solutions' in
1311 [target.id for target in elem.targets]]
1312 if not solutions:
1313 return self
1314 solutions = solutions[-1]
1315 for solution in solutions.value.elts:
1316 # Check for obsolete URL's
1317 url_idx = ast_dict_index(solution, 'url')
1318 if url_idx == -1:
1319 continue
1320 url_val = solution.values[url_idx]
1321 if type(url_val) is not ast.Str:
1322 continue
1323 if (svn_url_re.match(url_val.s.strip())):
1324 raise gclient_utils.Error(
1325"""
1326The chromium code repository has migrated completely to git.
1327Your SVN-based checkout is now obsolete; you need to create a brand-new
1328git checkout by following these instructions:
1329
1330http://www.chromium.org/developers/how-tos/get-the-code
1331""")
1332 if (old_git_re.match(url_val.s.strip())):
1333 url_val.s = CHROMIUM_SRC_URL
1334 modified = True
1335
szager@chromium.org808bcfb2014-08-24 19:38:43 +00001336 # Ensure deps_file is set to .DEPS.git. We enforce this here to smooth
1337 # over switching between pre-git-migration and post-git-migration
1338 # revisions.
1339 # - For pre-migration revisions, .DEPS.git must be explicitly set.
1340 # - For post-migration revisions, .DEPS.git is not present, so gclient
1341 # will correctly fall back to DEPS.
1342 if url_val.s == CHROMIUM_SRC_URL:
1343 deps_file_idx = ast_dict_index(solution, 'deps_file')
1344 if deps_file_idx != -1:
1345 continue
1346 solution.keys.append(ast.Str('deps_file'))
1347 solution.values.append(ast.Str('.DEPS.git'))
1348 modified = True
1349
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001350 if not modified:
1351 return self
1352
1353 print(
1354"""
1355WARNING: gclient detected an obsolete setting in your %s file. The file has
1356been automagically updated. The previous version is available at %s.old.
1357""" % (options.config_filename, options.config_filename))
1358
1359 # Replace existing .gclient with the updated version.
1360 # Return a new GClient instance based on the new content.
1361 new_content = ast2str(a)
1362 dot_gclient_fn = os.path.join(path, options.config_filename)
iannucci@chromium.orgc7c41682014-08-23 03:44:18 +00001363 try:
1364 os.rename(dot_gclient_fn, dot_gclient_fn + '.old')
1365 except OSError:
1366 pass
1367 with open(dot_gclient_fn, 'w') as fh:
1368 fh.write(new_content)
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001369 client = GClient(path, options)
1370 client.SetConfig(new_content)
1371 return client
1372
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001373 @staticmethod
1374 def LoadCurrentConfig(options):
1375 """Searches for and loads a .gclient file relative to the current working
1376 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001377 if options.spec:
1378 client = GClient('.', options)
1379 client.SetConfig(options.spec)
1380 else:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001381 if options.verbose:
1382 print('Looking for %s starting from %s\n' % (
1383 options.config_filename, os.getcwd()))
szager@chromium.orge2e03202012-07-31 18:05:16 +00001384 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1385 if not path:
1386 return None
1387 client = GClient(path, options)
1388 client.SetConfig(gclient_utils.FileRead(
1389 os.path.join(path, options.config_filename)))
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001390 client = client.MigrateConfigToGit(path, options)
maruel@chromium.org69392e72011-10-13 22:09:00 +00001391
1392 if (options.revisions and
1393 len(client.dependencies) > 1 and
1394 any('@' not in r for r in options.revisions)):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001395 print(
1396 ('You must specify the full solution name like --revision %s@%s\n'
1397 'when you have multiple solutions setup in your .gclient file.\n'
1398 'Other solutions present are: %s.') % (
maruel@chromium.org69392e72011-10-13 22:09:00 +00001399 client.dependencies[0].name,
1400 options.revisions[0],
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001401 ', '.join(s.name for s in client.dependencies[1:])),
1402 file=sys.stderr)
maruel@chromium.org15804092010-09-02 17:07:37 +00001403 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001404
nsylvain@google.comefc80932011-05-31 21:27:56 +00001405 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001406 safesync_url, managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001407 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1408 'solution_name': solution_name,
1409 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001410 'deps_file': deps_file,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001411 'safesync_url' : safesync_url,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001412 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001413 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001414 })
1415
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001416 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001417 """Creates a .gclient_entries file to record the list of unique checkouts.
1418
1419 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001420 """
1421 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1422 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001423 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001424 for entry in self.root.subtree(False):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001425 # Skip over File() dependencies as we can't version them.
1426 if not isinstance(entry.parsed_url, self.FileImpl):
1427 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1428 pprint.pformat(entry.parsed_url))
1429 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001430 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001431 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001432 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001433
1434 def _ReadEntries(self):
1435 """Read the .gclient_entries file for the given client.
1436
1437 Returns:
1438 A sequence of solution names, which will be empty if there is the
1439 entries file hasn't been created yet.
1440 """
1441 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001442 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001443 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001444 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001445 try:
1446 exec(gclient_utils.FileRead(filename), scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001447 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001448 gclient_utils.SyntaxErrorToError(filename, e)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001449 return scope['entries']
1450
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001451 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001452 """Checks for revision overrides."""
1453 revision_overrides = {}
maruel@chromium.org307d1792010-05-31 20:03:13 +00001454 if self._options.head:
1455 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001456 # Do not check safesync_url if one or more --revision flag is specified.
1457 if not self._options.revisions:
1458 for s in self.dependencies:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001459 if not s.managed:
1460 self._options.revisions.append('%s@unmanaged' % s.name)
1461 elif s.safesync_url:
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001462 self._ApplySafeSyncRev(dep=s)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001463 if not self._options.revisions:
1464 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001465 solutions_names = [s.name for s in self.dependencies]
maruel@chromium.org307d1792010-05-31 20:03:13 +00001466 index = 0
1467 for revision in self._options.revisions:
1468 if not '@' in revision:
1469 # Support for --revision 123
1470 revision = '%s@%s' % (solutions_names[index], revision)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001471 name, rev = revision.split('@', 1)
1472 revision_overrides[name] = rev
maruel@chromium.org307d1792010-05-31 20:03:13 +00001473 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001474 return revision_overrides
1475
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001476 def _ApplySafeSyncRev(self, dep):
1477 """Finds a valid revision from the content of the safesync_url and apply it
1478 by appending revisions to the revision list. Throws if revision appears to
1479 be invalid for the given |dep|."""
1480 assert len(dep.safesync_url) > 0
1481 handle = urllib.urlopen(dep.safesync_url)
1482 rev = handle.read().strip()
1483 handle.close()
1484 if not rev:
1485 raise gclient_utils.Error(
1486 'It appears your safesync_url (%s) is not working properly\n'
1487 '(as it returned an empty response). Check your config.' %
1488 dep.safesync_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001489 scm = gclient_scm.CreateSCM(
1490 dep.url, dep.root.root_dir, dep.name, self.outbuf)
iannucci@chromium.org4a4b33b2013-07-04 20:25:46 +00001491 safe_rev = scm.GetUsableRev(rev, self._options)
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001492 if self._options.verbose:
1493 print('Using safesync_url revision: %s.\n' % safe_rev)
1494 self._options.revisions.append('%s@%s' % (dep.name, safe_rev))
1495
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001496 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001497 """Runs a command on each dependency in a client and its dependencies.
1498
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001499 Args:
1500 command: The command to use (e.g., 'status' or 'diff')
1501 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001502 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001503 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001504 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001505
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001506 revision_overrides = {}
1507 # It's unnecessary to check for revision overrides for 'recurse'.
1508 # Save a few seconds by not calling _EnforceRevisions() in that case.
agable@chromium.org0242eb42015-06-09 00:45:31 +00001509 if command not in ('diff', 'recurse', 'runhooks', 'status', 'revert'):
szager@chromium.org5273b8a2014-08-21 15:10:10 +00001510 self._CheckConfig()
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001511 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001512 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001513 # Disable progress for non-tty stdout.
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00001514 if (setup_color.IS_TTY and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001515 if command in ('update', 'revert'):
1516 pm = Progress('Syncing projects', 1)
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +00001517 elif command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001518 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001519 work_queue = gclient_utils.ExecutionQueue(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001520 self._options.jobs, pm, ignore_requirements=ignore_requirements,
1521 verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001522 for s in self.dependencies:
1523 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001524 work_queue.flush(revision_overrides, command, args, options=self._options)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001525 if revision_overrides:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001526 print('Please fix your script, having invalid --revision flags will soon '
1527 'considered an error.', file=sys.stderr)
piman@chromium.org6f363722010-04-27 00:41:09 +00001528
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001529 # Once all the dependencies have been processed, it's now safe to run the
1530 # hooks.
1531 if not self._options.nohooks:
1532 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001533
1534 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001535 # Notify the user if there is an orphaned entry in their working copy.
1536 # Only delete the directory if there are no changes in it, and
1537 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001538 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001539 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1540 for e in entries]
1541
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001542 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001543 if not prev_url:
1544 # entry must have been overridden via .gclient custom_deps
1545 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001546 # Fix path separator on Windows.
1547 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001548 e_dir = os.path.join(self.root_dir, entry_fixed)
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001549 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001550 if (entry not in entries and
1551 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001552 os.path.exists(e_dir)):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001553 # The entry has been removed from DEPS.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001554 scm = gclient_scm.CreateSCM(
1555 prev_url, self.root_dir, entry_fixed, self.outbuf)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001556
1557 # Check to see if this directory is now part of a higher-up checkout.
borenet@google.com359bb642014-05-13 17:28:19 +00001558 # The directory might be part of a git OR svn checkout.
1559 scm_root = None
primiano@chromium.org1c127382015-02-17 11:15:40 +00001560 scm_class = None
borenet@google.com359bb642014-05-13 17:28:19 +00001561 for scm_class in (gclient_scm.scm.GIT, gclient_scm.scm.SVN):
1562 try:
1563 scm_root = scm_class.GetCheckoutRoot(scm.checkout_path)
1564 except subprocess2.CalledProcessError:
1565 pass
1566 if scm_root:
1567 break
1568 else:
1569 logging.warning('Could not find checkout root for %s. Unable to '
1570 'determine whether it is part of a higher-level '
1571 'checkout, so not removing.' % entry)
1572 continue
primiano@chromium.org1c127382015-02-17 11:15:40 +00001573
1574 # This is to handle the case of third_party/WebKit migrating from
1575 # being a DEPS entry to being part of the main project.
1576 # If the subproject is a Git project, we need to remove its .git
1577 # folder. Otherwise git operations on that folder will have different
1578 # effects depending on the current working directory.
1579 if scm_class == gclient_scm.scm.GIT and (
1580 os.path.abspath(scm_root) == os.path.abspath(e_dir)):
1581 e_par_dir = os.path.join(e_dir, os.pardir)
1582 if scm_class.IsInsideWorkTree(e_par_dir):
1583 par_scm_root = scm_class.GetCheckoutRoot(e_par_dir)
1584 # rel_e_dir : relative path of entry w.r.t. its parent repo.
1585 rel_e_dir = os.path.relpath(e_dir, par_scm_root)
1586 if scm_class.IsDirectoryVersioned(par_scm_root, rel_e_dir):
1587 save_dir = scm.GetGitBackupDirPath()
1588 # Remove any eventual stale backup dir for the same project.
1589 if os.path.exists(save_dir):
1590 gclient_utils.rmtree(save_dir)
1591 os.rename(os.path.join(e_dir, '.git'), save_dir)
1592 # When switching between the two states (entry/ is a subproject
1593 # -> entry/ is part of the outer project), it is very likely
1594 # that some files are changed in the checkout, unless we are
1595 # jumping *exactly* across the commit which changed just DEPS.
1596 # In such case we want to cleanup any eventual stale files
1597 # (coming from the old subproject) in order to end up with a
1598 # clean checkout.
1599 scm_class.CleanupDir(par_scm_root, rel_e_dir)
1600 assert not os.path.exists(os.path.join(e_dir, '.git'))
1601 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher '
1602 'level checkout. The git folder containing all the local'
1603 ' branches has been saved to %s.\n'
1604 'If you don\'t care about its state you can safely '
1605 'remove that folder to free up space.') %
1606 (entry, save_dir))
1607 continue
1608
borenet@google.com359bb642014-05-13 17:28:19 +00001609 if scm_root in full_entries:
primiano@chromium.org1c127382015-02-17 11:15:40 +00001610 logging.info('%s is part of a higher level checkout, not removing',
1611 scm.GetCheckoutRoot())
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001612 continue
1613
1614 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001615 scm.status(self._options, [], file_list)
1616 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001617 if (not self._options.delete_unversioned_trees or
1618 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001619 # There are modified files in this entry. Keep warning until
1620 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001621 print(('\nWARNING: \'%s\' is no longer part of this client. '
1622 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001623 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001624 else:
1625 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001626 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001627 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001628 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001629 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001630 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001631 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001632
1633 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001634 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001635 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001636 # Load all the settings.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001637 work_queue = gclient_utils.ExecutionQueue(
1638 self._options.jobs, None, False, verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001639 for s in self.dependencies:
1640 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001641 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001642
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001643 def GetURLAndRev(dep):
1644 """Returns the revision-qualified SCM url for a Dependency."""
1645 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001646 return None
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001647 if isinstance(dep.parsed_url, self.FileImpl):
1648 original_url = dep.parsed_url.file_location
1649 else:
1650 original_url = dep.parsed_url
nasser@codeaurora.org5d63eb82010-03-24 23:22:09 +00001651 url, _ = gclient_utils.SplitUrlRevision(original_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001652 scm = gclient_scm.CreateSCM(
1653 original_url, self.root_dir, dep.name, self.outbuf)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001654 if not os.path.isdir(scm.checkout_path):
1655 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001656 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001657
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001658 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001659 new_gclient = ''
1660 # First level at .gclient
1661 for d in self.dependencies:
1662 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001663 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001664 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001665 for d in dep.dependencies:
1666 entries[d.name] = GetURLAndRev(d)
1667 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001668 GrabDeps(d)
1669 custom_deps = []
1670 for k in sorted(entries.keys()):
1671 if entries[k]:
1672 # Quotes aren't escaped...
1673 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1674 else:
1675 custom_deps.append(' \"%s\": None,\n' % k)
1676 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1677 'solution_name': d.name,
1678 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001679 'deps_file': d.deps_file,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001680 'safesync_url' : d.safesync_url or '',
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001681 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001682 'solution_deps': ''.join(custom_deps),
1683 }
1684 # Print the snapshot configuration file
1685 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001686 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001687 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001688 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001689 if self._options.actual:
1690 entries[d.name] = GetURLAndRev(d)
1691 else:
1692 entries[d.name] = d.parsed_url
1693 keys = sorted(entries.keys())
1694 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001695 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001696 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001697
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001698 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001699 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001700 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001701
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001702 def PrintLocationAndContents(self):
1703 # Print out the .gclient file. This is longer than if we just printed the
1704 # client dict, but more legible, and it might contain helpful comments.
1705 print('Loaded .gclient config in %s:\n%s' % (
1706 self.root_dir, self.config_content))
1707
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001708 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001709 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001710 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001711 return self._root_dir
1712
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001713 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001714 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001715 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001716 return self._enforced_os
1717
maruel@chromium.org68988972011-09-20 14:11:42 +00001718 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001719 def recursion_limit(self):
1720 """How recursive can each dependencies in DEPS file can load DEPS file."""
1721 return self._recursion_limit
1722
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001723 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +00001724 def try_recursedeps(self):
1725 """Whether to attempt using recursedeps-style recursion processing."""
cmp@chromium.orge84ac912014-06-30 23:14:35 +00001726 return True
1727
1728 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001729 def target_os(self):
1730 return self._enforced_os
1731
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001732
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001733#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001734
1735
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001736def CMDcleanup(parser, args):
1737 """Cleans up all working copies.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001738
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001739 Mostly svn-specific. Simply runs 'svn cleanup' for each module.
1740 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001741 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1742 help='override deps for the specified (comma-separated) '
1743 'platform(s); \'all\' will process all deps_os '
1744 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001745 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001746 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001747 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001748 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001749 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001750 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001751 return client.RunOnDeps('cleanup', args)
1752
1753
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001754@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001755def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001756 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001757
1758 Runs a shell command on all entries.
ilevy@chromium.org37116242012-11-28 01:32:48 +00001759 Sets GCLIENT_DEP_PATH enviroment variable as the dep's relative location to
1760 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001761 """
1762 # Stop parsing at the first non-arg so that these go through to the command
1763 parser.disable_interspersed_args()
1764 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001765 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001766 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001767 help='Ignore non-zero return codes from subcommands.')
1768 parser.add_option('--prepend-dir', action='store_true',
1769 help='Prepend relative dir for use with git <cmd> --null.')
1770 parser.add_option('--no-progress', action='store_true',
1771 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001772 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001773 if not args:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001774 print('Need to supply a command!', file=sys.stderr)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001775 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001776 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1777 if not root_and_entries:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001778 print(
maruel@chromium.org78cba522010-10-18 13:32:05 +00001779 'You need to run gclient sync at least once to use \'recurse\'.\n'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001780 'This is because .gclient_entries needs to exist and be up to date.',
1781 file=sys.stderr)
maruel@chromium.org78cba522010-10-18 13:32:05 +00001782 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001783
1784 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001785 scm_set = set()
1786 for scm in options.scm:
1787 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001788 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001789
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001790 options.nohooks = True
1791 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001792 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1793 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001794
1795
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001796@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001797def CMDfetch(parser, args):
1798 """Fetches upstream commits for all modules.
1799
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001800 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1801 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001802 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001803 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001804 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1805
1806
1807def CMDgrep(parser, args):
1808 """Greps through git repos managed by gclient.
1809
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001810 Runs 'git grep [args...]' for each module.
1811 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001812 # We can't use optparse because it will try to parse arguments sent
1813 # to git grep and throw an error. :-(
1814 if not args or re.match('(-h|--help)$', args[0]):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001815 print(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001816 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1817 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1818 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1819 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001820 ' end of your query.',
1821 file=sys.stderr)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001822 return 1
1823
1824 jobs_arg = ['--jobs=1']
1825 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1826 jobs_arg, args = args[:1], args[1:]
1827 elif re.match(r'(-j|--jobs)$', args[0]):
1828 jobs_arg, args = args[:2], args[2:]
1829
1830 return CMDrecurse(
1831 parser,
1832 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1833 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001834
1835
stip@chromium.orga735da22015-04-29 23:18:20 +00001836def CMDroot(parser, args):
1837 """Outputs the solution root (or current dir if there isn't one)."""
1838 (options, args) = parser.parse_args(args)
1839 client = GClient.LoadCurrentConfig(options)
1840 if client:
1841 print(os.path.abspath(client.root_dir))
1842 else:
1843 print(os.path.abspath('.'))
1844
1845
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001846@subcommand.usage('[url] [safesync url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001847def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001848 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001849
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001850 This specifies the configuration for further commands. After update/sync,
1851 top-level DEPS files in each module are read to determine dependent
1852 modules to operate on as well. If optional [url] parameter is
1853 provided, then configuration is read from a specified Subversion server
1854 URL.
1855 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00001856 # We do a little dance with the --gclientfile option. 'gclient config' is the
1857 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1858 # arguments. So, we temporarily stash any --gclientfile parameter into
1859 # options.output_config_file until after the (gclientfile xor spec) error
1860 # check.
1861 parser.remove_option('--gclientfile')
1862 parser.add_option('--gclientfile', dest='output_config_file',
1863 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001864 parser.add_option('--name',
1865 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001866 parser.add_option('--deps-file', default='DEPS',
1867 help='overrides the default name for the DEPS file for the'
1868 'main solutions and all sub-dependencies')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001869 parser.add_option('--unmanaged', action='store_true', default=False,
1870 help='overrides the default behavior to make it possible '
1871 'to have the main solution untouched by gclient '
1872 '(gclient will check out unmanaged dependencies but '
1873 'will never sync them)')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001874 parser.add_option('--cache-dir',
1875 help='(git only) Cache all git repos into this dir and do '
1876 'shared clones from the cache, instead of cloning '
1877 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001878 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001879 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001880 if options.output_config_file:
1881 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001882 if ((options.spec and args) or len(args) > 2 or
1883 (not options.spec and not args)):
1884 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1885
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001886 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001887 if options.spec:
1888 client.SetConfig(options.spec)
1889 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001890 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001891 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001892 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001893 if name.endswith('.git'):
1894 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001895 else:
1896 # specify an alternate relpath for the given URL.
1897 name = options.name
agable@chromium.orgf2214672015-10-27 21:02:48 +00001898 if not os.path.abspath(os.path.join(os.getcwd(), name)).startswith(
1899 os.getcwd()):
1900 parser.error('Do not pass a relative path for --name.')
1901 if any(x in ('..', '.', '/', '\\') for x in name.split(os.sep)):
1902 parser.error('Do not include relative path components in --name.')
1903
nsylvain@google.comefc80932011-05-31 21:27:56 +00001904 deps_file = options.deps_file
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001905 safesync_url = ''
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001906 if len(args) > 1:
1907 safesync_url = args[1]
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001908 client.SetDefaultConfig(name, deps_file, base_url, safesync_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001909 managed=not options.unmanaged,
1910 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001911 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001912 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001913
1914
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001915@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001916 gclient pack > patch.txt
1917 generate simple patch for configured client and dependences
1918""")
1919def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001920 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001921
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001922 Internally, runs 'svn diff'/'git diff' on each checked out module and
1923 dependencies, and performs minimal postprocessing of the output. The
1924 resulting patch is printed to stdout and can be applied to a freshly
1925 checked out tree via 'patch -p0 < patchfile'.
1926 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001927 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1928 help='override deps for the specified (comma-separated) '
1929 'platform(s); \'all\' will process all deps_os '
1930 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001931 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001932 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001933 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001934 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001935 client = GClient.LoadCurrentConfig(options)
1936 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001937 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001938 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001939 client.PrintLocationAndContents()
kbr@google.comab318592009-09-04 00:54:55 +00001940 return client.RunOnDeps('pack', args)
1941
1942
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001943def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001944 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001945 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1946 help='override deps for the specified (comma-separated) '
1947 'platform(s); \'all\' will process all deps_os '
1948 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001949 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001950 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001951 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001952 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001953 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001954 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001955 return client.RunOnDeps('status', args)
1956
1957
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001958@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001959 gclient sync
1960 update files from SCM according to current configuration,
1961 *for modules which have changed since last update or sync*
1962 gclient sync --force
1963 update files from SCM according to current configuration, for
1964 all modules (useful for recovering files deleted from local copy)
1965 gclient sync --revision src@31000
1966 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001967
1968JSON output format:
1969If the --output-json option is specified, the following document structure will
1970be emitted to the provided file. 'null' entries may occur for subprojects which
1971are present in the gclient solution, but were not processed (due to custom_deps,
1972os_deps, etc.)
1973
1974{
1975 "solutions" : {
1976 "<name>": { # <name> is the posix-normalized path to the solution.
1977 "revision": [<svn rev int>|<git id hex string>|null],
1978 "scm": ["svn"|"git"|null],
1979 }
1980 }
1981}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001982""")
1983def CMDsync(parser, args):
1984 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001985 parser.add_option('-f', '--force', action='store_true',
1986 help='force update even for unchanged modules')
1987 parser.add_option('-n', '--nohooks', action='store_true',
1988 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001989 parser.add_option('-p', '--noprehooks', action='store_true',
1990 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001991 parser.add_option('-r', '--revision', action='append',
1992 dest='revisions', metavar='REV', default=[],
1993 help='Enforces revision/hash for the solutions with the '
1994 'format src@rev. The src@ part is optional and can be '
1995 'skipped. -r can be used multiple times when .gclient '
1996 'has multiple solutions configured and will work even '
joi@chromium.org792ea882010-11-10 02:37:27 +00001997 'if the src@ part is skipped. Note that specifying '
1998 '--revision means your safesync_url gets ignored.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00001999 parser.add_option('--with_branch_heads', action='store_true',
2000 help='Clone git "branch_heads" refspecs in addition to '
2001 'the default refspecs. This adds about 1/2GB to a '
2002 'full checkout. (git only)')
szager@chromium.org8d3348f2014-08-19 22:49:16 +00002003 parser.add_option('--with_tags', action='store_true',
2004 help='Clone git tags in addition to the default refspecs.')
floitsch@google.comeaab7842011-04-28 09:07:58 +00002005 parser.add_option('-t', '--transitive', action='store_true',
2006 help='When a revision is specified (in the DEPS file or '
2007 'with the command-line flag), transitively update '
2008 'the dependencies to the date of the given revision. '
2009 'Only supported for SVN repositories.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002010 parser.add_option('-H', '--head', action='store_true',
2011 help='skips any safesync_urls specified in '
2012 'configured solutions and sync to head instead')
2013 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002014 help='Deletes from the working copy any dependencies that '
2015 'have been removed since the last sync, as long as '
2016 'there are no local modifications. When used with '
2017 '--force, such dependencies are removed even if they '
2018 'have local modifications. When used with --reset, '
2019 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002020 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002021 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002022 parser.add_option('-R', '--reset', action='store_true',
2023 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00002024 parser.add_option('-M', '--merge', action='store_true',
2025 help='merge upstream changes instead of trying to '
2026 'fast-forward or rebase')
dnj@chromium.org5b23e872015-02-20 21:25:57 +00002027 parser.add_option('-A', '--auto_rebase', action='store_true',
2028 help='Automatically rebase repositories against local '
2029 'checkout during update (git only).')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002030 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2031 help='override deps for the specified (comma-separated) '
2032 'platform(s); \'all\' will process all deps_os '
2033 'references')
2034 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
2035 help='Skip svn up whenever possible by requesting '
2036 'actual HEAD revision from the repository')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002037 parser.add_option('--upstream', action='store_true',
2038 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002039 parser.add_option('--output-json',
2040 help='Output a json document to this path containing '
2041 'summary information about the sync.')
primiano@chromium.org5439ea52014-08-06 17:18:18 +00002042 parser.add_option('--no-history', action='store_true',
2043 help='GIT ONLY - Reduces the size/time of the checkout at '
2044 'the cost of no history. Requires Git 1.9+')
hinoka@chromium.org46b87412014-05-15 00:42:05 +00002045 parser.add_option('--shallow', action='store_true',
2046 help='GIT ONLY - Do a shallow clone into the cache dir. '
2047 'Requires Git 1.9+')
e.hakkinen@samsung.come8bc1aa2015-04-08 08:00:37 +00002048 parser.add_option('--no_bootstrap', '--no-bootstrap',
2049 action='store_true',
2050 help='Don\'t bootstrap from Google Storage.')
hinoka@chromium.org8a10f6d2014-06-23 18:38:57 +00002051 parser.add_option('--ignore_locks', action='store_true',
2052 help='GIT ONLY - Ignore cache locks.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +00002053 parser.add_option('--break_repo_locks', action='store_true',
2054 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2055 'index.lock). This should only be used if you know for '
2056 'certain that this invocation of gclient is the only '
2057 'thing operating on the git repos (e.g. on a bot).')
nodir@chromium.org5b48e482016-03-18 20:27:54 +00002058 parser.add_option('--lock_timeout', type='int', default=5000,
szager@chromium.orgdbb6f822016-02-02 22:59:30 +00002059 help='GIT ONLY - Deadline (in seconds) to wait for git '
nodir@chromium.org5b48e482016-03-18 20:27:54 +00002060 'cache lock to become available. Default is %default.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002061 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002062 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002063
2064 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002065 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002066
maruel@chromium.org307d1792010-05-31 20:03:13 +00002067 if options.revisions and options.head:
2068 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002069 print('Warning: you cannot use both --head and --revision')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002070
2071 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002072 client.PrintLocationAndContents()
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002073 ret = client.RunOnDeps('update', args)
2074 if options.output_json:
2075 slns = {}
2076 for d in client.subtree(True):
2077 normed = d.name.replace('\\', '/').rstrip('/') + '/'
2078 slns[normed] = {
2079 'revision': d.got_revision,
2080 'scm': d.used_scm.name if d.used_scm else None,
hinoka@chromium.org17db9052014-05-10 01:11:29 +00002081 'url': str(d.url) if d.url else None,
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002082 }
2083 with open(options.output_json, 'wb') as f:
2084 json.dump({'solutions': slns}, f)
2085 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002086
2087
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002088CMDupdate = CMDsync
2089
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002090
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002091def CMDdiff(parser, args):
2092 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002093 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2094 help='override deps for the specified (comma-separated) '
2095 'platform(s); \'all\' will process all deps_os '
2096 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002097 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002098 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002099 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002100 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002101 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002102 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002103 return client.RunOnDeps('diff', args)
2104
2105
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002106def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002107 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00002108
2109 That's the nuclear option to get back to a 'clean' state. It removes anything
2110 that shows up in svn status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002111 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2112 help='override deps for the specified (comma-separated) '
2113 'platform(s); \'all\' will process all deps_os '
2114 'references')
2115 parser.add_option('-n', '--nohooks', action='store_true',
2116 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002117 parser.add_option('-p', '--noprehooks', action='store_true',
2118 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002119 parser.add_option('--upstream', action='store_true',
2120 help='Make repo state match upstream branch.')
iannucci@chromium.orgbf525dc2016-04-07 22:00:28 +00002121 parser.add_option('--break_repo_locks', action='store_true',
2122 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2123 'index.lock). This should only be used if you know for '
2124 'certain that this invocation of gclient is the only '
2125 'thing operating on the git repos (e.g. on a bot).')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002126 (options, args) = parser.parse_args(args)
2127 # --force is implied.
2128 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002129 options.reset = False
2130 options.delete_unversioned_trees = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002131 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002132 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002133 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002134 return client.RunOnDeps('revert', args)
2135
2136
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002137def CMDrunhooks(parser, args):
2138 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002139 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2140 help='override deps for the specified (comma-separated) '
2141 'platform(s); \'all\' will process all deps_os '
2142 'references')
2143 parser.add_option('-f', '--force', action='store_true', default=True,
2144 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002145 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002146 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002147 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002148 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002149 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002150 client.PrintLocationAndContents()
maruel@chromium.org5df6a462009-08-28 18:52:26 +00002151 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002152 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002153 return client.RunOnDeps('runhooks', args)
2154
2155
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002156def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002157 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002158
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002159 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002160 can be used to reproduce the same tree in the future. It is only useful for
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002161 'unpinned dependencies', i.e. DEPS/deps references without a svn revision
2162 number or a git hash. A git branch name isn't 'pinned' since the actual
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002163 commit can change.
2164 """
2165 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2166 help='override deps for the specified (comma-separated) '
2167 'platform(s); \'all\' will process all deps_os '
2168 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002169 parser.add_option('-a', '--actual', action='store_true',
2170 help='gets the actual checked out revisions instead of the '
2171 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002172 parser.add_option('-s', '--snapshot', action='store_true',
2173 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002174 'version of all repositories to reproduce the tree, '
2175 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002176 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002177 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002178 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002179 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002180 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002181 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002182
2183
szager@google.comb9a78d32012-03-13 18:46:21 +00002184def CMDhookinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002185 """Outputs the hooks that would be run by `gclient runhooks`."""
szager@google.comb9a78d32012-03-13 18:46:21 +00002186 (options, args) = parser.parse_args(args)
2187 options.force = True
2188 client = GClient.LoadCurrentConfig(options)
2189 if not client:
2190 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2191 client.RunOnDeps(None, [])
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002192 print('; '.join(' '.join(hook) for hook in client.GetHooks(options)))
szager@google.comb9a78d32012-03-13 18:46:21 +00002193 return 0
2194
2195
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002196def CMDverify(parser, args):
2197 """Verifies the DEPS file deps are only from allowed_hosts."""
2198 (options, args) = parser.parse_args(args)
2199 client = GClient.LoadCurrentConfig(options)
2200 if not client:
2201 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2202 client.RunOnDeps(None, [])
2203 # Look at each first-level dependency of this gclient only.
2204 for dep in client.dependencies:
2205 bad_deps = dep.findDepsFromNotAllowedHosts()
2206 if not bad_deps:
2207 continue
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002208 print("There are deps from not allowed hosts in file %s" % dep.deps_file)
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002209 for bad_dep in bad_deps:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002210 print("\t%s at %s" % (bad_dep.name, bad_dep.url))
2211 print("allowed_hosts:", ', '.join(dep.allowed_hosts))
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002212 sys.stdout.flush()
2213 raise gclient_utils.Error(
2214 'dependencies from disallowed hosts; check your DEPS file.')
2215 return 0
2216
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002217class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00002218 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002219
2220 def __init__(self, **kwargs):
2221 optparse.OptionParser.__init__(
2222 self, version='%prog ' + __version__, **kwargs)
2223
2224 # Some arm boards have issues with parallel sync.
2225 if platform.machine().startswith('arm'):
2226 jobs = 1
2227 else:
2228 jobs = max(8, gclient_utils.NumLocalCpus())
2229 # cmp: 2013/06/19
2230 # Temporary workaround to lower bot-load on SVN server.
hinoka@google.com267f33e2014-02-28 22:02:32 +00002231 # Bypassed if a bot_update flag is detected.
2232 if (os.environ.get('CHROME_HEADLESS') == '1' and
2233 not os.path.exists('update.flag')):
xusydoc@chromium.org05028412013-07-29 13:40:10 +00002234 jobs = 1
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002235
2236 self.add_option(
2237 '-j', '--jobs', default=jobs, type='int',
2238 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002239 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002240 self.add_option(
2241 '-v', '--verbose', action='count', default=0,
2242 help='Produces additional output for diagnostics. Can be used up to '
2243 'three times for more logging info.')
2244 self.add_option(
2245 '--gclientfile', dest='config_filename',
2246 help='Specify an alternate %s file' % self.gclientfile_default)
2247 self.add_option(
2248 '--spec',
2249 help='create a gclient file containing the provided string. Due to '
2250 'Cygwin/Python brokenness, it can\'t contain any newlines.')
2251 self.add_option(
2252 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00002253 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002254
2255 def parse_args(self, args=None, values=None):
2256 """Integrates standard options processing."""
2257 options, args = optparse.OptionParser.parse_args(self, args, values)
2258 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
2259 logging.basicConfig(
2260 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00002261 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002262 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002263 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00002264 if (options.config_filename and
2265 options.config_filename != os.path.basename(options.config_filename)):
2266 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002267 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002268 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00002269 options.entries_filename = options.config_filename + '_entries'
2270 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002271 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00002272
2273 # These hacks need to die.
2274 if not hasattr(options, 'revisions'):
2275 # GClient.RunOnDeps expects it even if not applicable.
2276 options.revisions = []
2277 if not hasattr(options, 'head'):
2278 options.head = None
2279 if not hasattr(options, 'nohooks'):
2280 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002281 if not hasattr(options, 'noprehooks'):
2282 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00002283 if not hasattr(options, 'deps_os'):
2284 options.deps_os = None
2285 if not hasattr(options, 'manually_grab_svn_rev'):
2286 options.manually_grab_svn_rev = None
2287 if not hasattr(options, 'force'):
2288 options.force = None
2289 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002290
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002291
2292def disable_buffering():
2293 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
2294 # operations. Python as a strong tendency to buffer sys.stdout.
2295 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
2296 # Make stdout annotated with the thread ids.
2297 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00002298
2299
sbc@chromium.org013731e2015-02-26 18:28:43 +00002300def main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002301 """Doesn't parse the arguments here, just find the right subcommand to
2302 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002303 if sys.hexversion < 0x02060000:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002304 print(
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002305 '\nYour python version %s is unsupported, please upgrade.\n' %
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002306 sys.version.split(' ', 1)[0],
2307 file=sys.stderr)
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002308 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002309 if not sys.executable:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002310 print(
2311 '\nPython cannot find the location of it\'s own executable.\n',
2312 file=sys.stderr)
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002313 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002314 fix_encoding.fix_encoding()
2315 disable_buffering()
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00002316 setup_color.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002317 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002318 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002319 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00002320 except KeyboardInterrupt:
2321 gclient_utils.GClientChildren.KillAllRemainingChildren()
2322 raise
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00002323 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002324 print('Error: %s' % str(e), file=sys.stderr)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002325 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00002326 finally:
2327 gclient_utils.PrintWarnings()
sbc@chromium.org013731e2015-02-26 18:28:43 +00002328 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002329
2330
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00002331if '__main__' == __name__:
sbc@chromium.org013731e2015-02-26 18:28:43 +00002332 try:
2333 sys.exit(main(sys.argv[1:]))
2334 except KeyboardInterrupt:
2335 sys.stderr.write('interrupted\n')
2336 sys.exit(1)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002337
2338# vim: ts=2:sw=2:tw=80:et: