blob: 7dd3a6c80fecfa7f66063e606072ee1a487744b1 [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
agabled437d762016-10-17 09:35:11 -07006"""Meta checkout dependency manager for Git."""
maruel@chromium.org39c0b222013-08-17 16:57:01 +00007# 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
Scott Grahamc4826742017-05-11 16:59:23 -070067# processing the deps_os/hooks_os dict of a DEPS file.
maruel@chromium.org39c0b222013-08-17 16:57:01 +000068#
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
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +020099import gclient_eval
maruel@chromium.org5f3eee32009-09-17 00:34:30 +0000100import gclient_scm
101import gclient_utils
szager@chromium.org848fd492014-04-09 19:06:44 +0000102import git_cache
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000103from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000104import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000105import subprocess2
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +0000106import setup_color
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000107
szager@chromium.org7b8b6de2014-08-23 00:57:31 +0000108CHROMIUM_SRC_URL = 'https://chromium.googlesource.com/chromium/src.git'
109
110
111def ast_dict_index(dnode, key):
112 """Search an ast.Dict for the argument key, and return its index."""
113 idx = [i for i in range(len(dnode.keys)) if (
114 type(dnode.keys[i]) is ast.Str and dnode.keys[i].s == key)]
115 if not idx:
116 return -1
117 elif len(idx) > 1:
118 raise gclient_utils.Error('Multiple dict entries with same key in AST')
119 return idx[-1]
120
121def ast2str(node, indent=0):
122 """Return a pretty-printed rendition of an ast.Node."""
123 t = type(node)
124 if t is ast.Module:
125 return '\n'.join([ast2str(x, indent) for x in node.body])
126 elif t is ast.Assign:
127 return ((' ' * indent) +
128 ' = '.join([ast2str(x) for x in node.targets] +
129 [ast2str(node.value, indent)]) + '\n')
130 elif t is ast.Name:
131 return node.id
132 elif t is ast.List:
133 if not node.elts:
134 return '[]'
135 elif len(node.elts) == 1:
136 return '[' + ast2str(node.elts[0], indent) + ']'
137 return ('[\n' + (' ' * (indent + 1)) +
138 (',\n' + (' ' * (indent + 1))).join(
139 [ast2str(x, indent + 1) for x in node.elts]) +
140 '\n' + (' ' * indent) + ']')
141 elif t is ast.Dict:
142 if not node.keys:
143 return '{}'
144 elif len(node.keys) == 1:
145 return '{%s: %s}' % (ast2str(node.keys[0]),
146 ast2str(node.values[0], indent + 1))
147 return ('{\n' + (' ' * (indent + 1)) +
148 (',\n' + (' ' * (indent + 1))).join(
149 ['%s: %s' % (ast2str(node.keys[i]),
150 ast2str(node.values[i], indent + 1))
151 for i in range(len(node.keys))]) +
152 '\n' + (' ' * indent) + '}')
153 elif t is ast.Str:
154 return "'%s'" % node.s
155 else:
156 raise gclient_utils.Error("Unexpected AST node at line %d, column %d: %s"
157 % (node.lineno, node.col_offset, t))
158
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000159
maruel@chromium.org116704f2010-06-11 17:34:38 +0000160class GClientKeywords(object):
maruel@chromium.org116704f2010-06-11 17:34:38 +0000161 class VarImpl(object):
162 def __init__(self, custom_vars, local_scope):
163 self._custom_vars = custom_vars
164 self._local_scope = local_scope
165
166 def Lookup(self, var_name):
167 """Implements the Var syntax."""
168 if var_name in self._custom_vars:
169 return self._custom_vars[var_name]
170 elif var_name in self._local_scope.get("vars", {}):
171 return self._local_scope["vars"][var_name]
172 raise gclient_utils.Error("Var is not defined: %s" % var_name)
173
174
maruel@chromium.org064186c2011-09-27 23:53:33 +0000175class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000176 """Immutable configuration settings."""
177 def __init__(
agablea98a6cd2016-11-15 14:30:10 -0800178 self, parent, url, managed, custom_deps, custom_vars,
agabledce6ddc2016-09-08 10:02:16 -0700179 custom_hooks, deps_file, should_process, relative):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000180 GClientKeywords.__init__(self)
181
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000182 # These are not mutable:
183 self._parent = parent
mmoss@chromium.org8f93f792014-08-26 23:24:09 +0000184 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000185 self._url = url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000186 # 'managed' determines whether or not this dependency is synced/updated by
187 # gclient after gclient checks it out initially. The difference between
188 # 'managed' and 'should_process' is that the user specifies 'managed' via
smutae7ea312016-07-18 11:59:41 -0700189 # the --unmanaged command-line flag or a .gclient config, where
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000190 # 'should_process' is dynamically set by gclient if it goes over its
191 # recursion limit and controls gclient's behavior so it does not misbehave.
192 self._managed = managed
193 self._should_process = should_process
agabledce6ddc2016-09-08 10:02:16 -0700194 # If this is a recursed-upon sub-dependency, and the parent has
195 # use_relative_paths set, then this dependency should check out its own
196 # dependencies relative to that parent's path for this, rather than
197 # relative to the .gclient file.
198 self._relative = relative
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000199 # This is a mutable value which has the list of 'target_os' OSes listed in
200 # the current deps file.
201 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000202
203 # These are only set in .gclient and not in DEPS files.
204 self._custom_vars = custom_vars or {}
205 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000206 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000207
maruel@chromium.org064186c2011-09-27 23:53:33 +0000208 # Post process the url to remove trailing slashes.
209 if isinstance(self._url, basestring):
210 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
211 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000212 self._url = self._url.replace('/@', '@')
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200213 elif not isinstance(self._url, (None.__class__)):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000214 raise gclient_utils.Error(
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200215 ('dependency url must be either string or None, '
216 'instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000217 # Make any deps_file path platform-appropriate.
218 for sep in ['/', '\\']:
219 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000220
221 @property
222 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000223 return self._deps_file
224
225 @property
226 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000227 return self._managed
228
229 @property
230 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000231 return self._parent
232
233 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000234 def root(self):
235 """Returns the root node, a GClient object."""
236 if not self.parent:
237 # This line is to signal pylint that it could be a GClient instance.
238 return self or GClient(None, None)
239 return self.parent.root
240
241 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000242 def should_process(self):
243 """True if this dependency should be processed, i.e. checked out."""
244 return self._should_process
245
246 @property
247 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000248 return self._custom_vars.copy()
249
250 @property
251 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000252 return self._custom_deps.copy()
253
maruel@chromium.org064186c2011-09-27 23:53:33 +0000254 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000255 def custom_hooks(self):
256 return self._custom_hooks[:]
257
258 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000259 def url(self):
260 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000261
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000262 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000263 def target_os(self):
264 if self.local_target_os is not None:
265 return tuple(set(self.local_target_os).union(self.parent.target_os))
266 else:
267 return self.parent.target_os
268
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000269 def get_custom_deps(self, name, url):
270 """Returns a custom deps if applicable."""
271 if self.parent:
272 url = self.parent.get_custom_deps(name, url)
273 # None is a valid return value to disable a dependency.
274 return self.custom_deps.get(name, url)
275
maruel@chromium.org064186c2011-09-27 23:53:33 +0000276
277class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000278 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000279
agablea98a6cd2016-11-15 14:30:10 -0800280 def __init__(self, parent, name, url, managed, custom_deps,
agabledce6ddc2016-09-08 10:02:16 -0700281 custom_vars, custom_hooks, deps_file, should_process,
282 relative):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000283 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000284 DependencySettings.__init__(
agablea98a6cd2016-11-15 14:30:10 -0800285 self, parent, url, managed, custom_deps, custom_vars,
agabledce6ddc2016-09-08 10:02:16 -0700286 custom_hooks, deps_file, should_process, relative)
maruel@chromium.org68988972011-09-20 14:11:42 +0000287
288 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000289 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000290
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000291 self._pre_deps_hooks = []
292
maruel@chromium.org68988972011-09-20 14:11:42 +0000293 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000294 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000295 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000296 # A cache of the files affected by the current operation, necessary for
297 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000298 self._file_list = []
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000299 # List of host names from which dependencies are allowed.
300 # Default is an empty set, meaning unspecified in DEPS file, and hence all
301 # hosts will be allowed. Non-empty set means whitelist of hosts.
302 # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
303 self._allowed_hosts = frozenset()
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000304 # If it is not set to True, the dependency wasn't processed for its child
305 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000306 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000307 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000308 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000309 # This dependency had its pre-DEPS hooks run
310 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000311 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000312 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000313 # This is the scm used to checkout self.url. It may be used by dependencies
314 # to get the datetime of the revision we checked out.
315 self._used_scm = None
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000316 self._used_revision = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000317 # The actual revision we ended up getting, or None if that information is
318 # unavailable
319 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000320
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000321 # This is a mutable value that overrides the normal recursion limit for this
322 # dependency. It is read from the actual DEPS file so cannot be set on
323 # class instantiation.
324 self.recursion_override = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000325 # recursedeps is a mutable value that selectively overrides the default
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000326 # 'no recursion' setting on a dep-by-dep basis. It will replace
327 # recursion_override.
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000328 #
329 # It will be a dictionary of {deps_name: {"deps_file": depfile_name}} or
330 # None.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000331 self.recursedeps = None
hinoka885e5b12016-06-08 14:40:09 -0700332 # This is inherited from WorkItem. We want the URL to be a resource.
333 if url and isinstance(url, basestring):
334 # The url is usually given to gclient either as https://blah@123
qyearsley12fa6ff2016-08-24 09:18:40 -0700335 # or just https://blah. The @123 portion is irrelevant.
hinoka885e5b12016-06-08 14:40:09 -0700336 self.resources.append(url.split('@')[0])
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000337
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000338 if not self.name and self.parent:
339 raise gclient_utils.Error('Dependency without name')
340
maruel@chromium.org470b5432011-10-11 18:18:19 +0000341 @property
342 def requirements(self):
343 """Calculate the list of requirements."""
344 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000345 # self.parent is implicitly a requirement. This will be recursive by
346 # definition.
347 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000348 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000349
350 # For a tree with at least 2 levels*, the leaf node needs to depend
351 # on the level higher up in an orderly way.
352 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
353 # thus unsorted, while the .gclient format is a list thus sorted.
354 #
355 # * _recursion_limit is hard coded 2 and there is no hope to change this
356 # value.
357 #
358 # Interestingly enough, the following condition only works in the case we
359 # want: self is a 2nd level node. 3nd level node wouldn't need this since
360 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000361 if self.parent and self.parent.parent and not self.parent.parent.parent:
362 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000363
maruel@chromium.org470b5432011-10-11 18:18:19 +0000364 if self.name:
365 requirements |= set(
366 obj.name for obj in self.root.subtree(False)
367 if (obj is not self
368 and obj.name and
369 self.name.startswith(posixpath.join(obj.name, ''))))
370 requirements = tuple(sorted(requirements))
371 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
372 return requirements
373
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000374 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000375 def try_recursedeps(self):
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000376 """Returns False if recursion_override is ever specified."""
377 if self.recursion_override is not None:
378 return False
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000379 return self.parent.try_recursedeps
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000380
381 @property
382 def recursion_limit(self):
383 """Returns > 0 if this dependency is not too recursed to be processed."""
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000384 # We continue to support the absence of recursedeps until tools and DEPS
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000385 # using recursion_override are updated.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000386 if self.try_recursedeps and self.parent.recursedeps != None:
387 if self.name in self.parent.recursedeps:
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000388 return 1
389
390 if self.recursion_override is not None:
391 return self.recursion_override
392 return max(self.parent.recursion_limit - 1, 0)
393
maruel@chromium.org470b5432011-10-11 18:18:19 +0000394 def verify_validity(self):
395 """Verifies that this Dependency is fine to add as a child of another one.
396
397 Returns True if this entry should be added, False if it is a duplicate of
398 another entry.
399 """
400 logging.info('Dependency(%s).verify_validity()' % self.name)
401 if self.name in [s.name for s in self.parent.dependencies]:
402 raise gclient_utils.Error(
403 'The same name "%s" appears multiple times in the deps section' %
404 self.name)
405 if not self.should_process:
406 # Return early, no need to set requirements.
407 return True
408
409 # This require a full tree traversal with locks.
410 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
411 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000412 self_url = self.LateOverride(self.url)
413 sibling_url = sibling.LateOverride(sibling.url)
414 # Allow to have only one to be None or ''.
415 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000416 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000417 ('Dependency %s specified more than once:\n'
418 ' %s [%s]\n'
419 'vs\n'
420 ' %s [%s]') % (
421 self.name,
422 sibling.hierarchy(),
423 sibling_url,
424 self.hierarchy(),
425 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000426 # In theory we could keep it as a shadow of the other one. In
427 # practice, simply ignore it.
428 logging.warn('Won\'t process duplicate dependency %s' % sibling)
429 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000430 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000431
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000432 def LateOverride(self, url):
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200433 """Resolves the parsed url from url."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000434 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000435 parsed_url = self.get_custom_deps(self.name, url)
436 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000437 logging.info(
438 'Dependency(%s).LateOverride(%s) -> %s' %
439 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000440 return parsed_url
441
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000442 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000443 parsed_url = urlparse.urlparse(url)
scr@chromium.orgf1eccaf2014-04-11 15:51:33 +0000444 if (not parsed_url[0] and
445 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000446 # A relative url. Fetch the real base.
447 path = parsed_url[2]
448 if not path.startswith('/'):
449 raise gclient_utils.Error(
450 'relative DEPS entry \'%s\' must begin with a slash' % url)
451 # Create a scm just to query the full url.
452 parent_url = self.parent.parsed_url
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000453 scm = gclient_scm.CreateSCM(
454 parent_url, self.root.root_dir, None, self.outbuf)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000455 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000456 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000457 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000458 logging.info(
459 'Dependency(%s).LateOverride(%s) -> %s' %
460 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000461 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000462
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000463 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000464 logging.info(
465 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000466 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000467
468 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000469
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000470 @staticmethod
471 def MergeWithOsDeps(deps, deps_os, target_os_list):
472 """Returns a new "deps" structure that is the deps sent in updated
473 with information from deps_os (the deps_os section of the DEPS
474 file) that matches the list of target os."""
475 os_overrides = {}
476 for the_target_os in target_os_list:
477 the_target_os_deps = deps_os.get(the_target_os, {})
478 for os_dep_key, os_dep_value in the_target_os_deps.iteritems():
479 overrides = os_overrides.setdefault(os_dep_key, [])
480 overrides.append((the_target_os, os_dep_value))
481
482 # If any os didn't specify a value (we have fewer value entries
483 # than in the os list), then it wants to use the default value.
484 for os_dep_key, os_dep_value in os_overrides.iteritems():
485 if len(os_dep_value) != len(target_os_list):
qyearsley12fa6ff2016-08-24 09:18:40 -0700486 # Record the default value too so that we don't accidentally
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000487 # set it to None or miss a conflicting DEPS.
488 if os_dep_key in deps:
489 os_dep_value.append(('default', deps[os_dep_key]))
490
491 target_os_deps = {}
492 for os_dep_key, os_dep_value in os_overrides.iteritems():
493 # os_dep_value is a list of (os, value) pairs.
494 possible_values = set(x[1] for x in os_dep_value if x[1] is not None)
495 if not possible_values:
496 target_os_deps[os_dep_key] = None
497 else:
498 if len(possible_values) > 1:
499 # It would be possible to abort here but it would be
500 # unfortunate if we end up preventing any kind of checkout.
501 logging.error('Conflicting dependencies for %s: %s. (target_os=%s)',
502 os_dep_key, os_dep_value, target_os_list)
503 # Sorting to get the same result every time in case of conflicts.
504 target_os_deps[os_dep_key] = sorted(possible_values)[0]
505
506 new_deps = deps.copy()
507 new_deps.update(target_os_deps)
508 return new_deps
509
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000510 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000511 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000512 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000513 assert not self.dependencies
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000514
515 deps_content = None
516 use_strict = False
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000517
518 # First try to locate the configured deps file. If it's missing, fallback
519 # to DEPS.
520 deps_files = [self.deps_file]
521 if 'DEPS' not in deps_files:
522 deps_files.append('DEPS')
523 for deps_file in deps_files:
524 filepath = os.path.join(self.root.root_dir, self.name, deps_file)
525 if os.path.isfile(filepath):
526 logging.info(
527 'ParseDepsFile(%s): %s file found at %s', self.name, deps_file,
528 filepath)
529 break
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000530 logging.info(
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000531 'ParseDepsFile(%s): No %s file found at %s', self.name, deps_file,
532 filepath)
533
534 if os.path.isfile(filepath):
maruel@chromium.org46304292010-10-28 11:42:00 +0000535 deps_content = gclient_utils.FileRead(filepath)
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000536 logging.debug('ParseDepsFile(%s) read:\n%s', self.name, deps_content)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000537 use_strict = 'use strict' in deps_content.splitlines()[0]
538
539 local_scope = {}
540 if deps_content:
541 # One thing is unintuitive, vars = {} must happen before Var() use.
542 var = self.VarImpl(self.custom_vars, local_scope)
543 if use_strict:
544 logging.info(
545 'ParseDepsFile(%s): Strict Mode Enabled', self.name)
546 global_scope = {
547 '__builtins__': {'None': None},
548 'Var': var.Lookup,
549 'deps_os': {},
550 }
551 else:
552 global_scope = {
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000553 'Var': var.Lookup,
554 'deps_os': {},
555 }
maruel@chromium.org46304292010-10-28 11:42:00 +0000556 # Eval the content.
557 try:
558 exec(deps_content, global_scope, local_scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000559 except SyntaxError as e:
maruel@chromium.org46304292010-10-28 11:42:00 +0000560 gclient_utils.SyntaxErrorToError(filepath, e)
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +0200561 if self._get_option('validate_syntax', False):
562 gclient_eval.Check(deps_content, filepath, global_scope, local_scope)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000563 if use_strict:
564 for key, val in local_scope.iteritems():
565 if not isinstance(val, (dict, list, tuple, str)):
566 raise gclient_utils.Error(
567 'ParseDepsFile(%s): Strict mode disallows %r -> %r' %
568 (self.name, key, val))
569
maruel@chromium.org271375b2010-06-23 19:17:38 +0000570 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000571 if 'recursion' in local_scope:
572 self.recursion_override = local_scope.get('recursion')
573 logging.warning(
574 'Setting %s recursion to %d.', self.name, self.recursion_limit)
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000575 self.recursedeps = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000576 if 'recursedeps' in local_scope:
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000577 self.recursedeps = {}
578 for ent in local_scope['recursedeps']:
579 if isinstance(ent, basestring):
580 self.recursedeps[ent] = {"deps_file": self.deps_file}
581 else: # (depname, depsfilename)
582 self.recursedeps[ent[0]] = {"deps_file": ent[1]}
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000583 logging.warning('Found recursedeps %r.', repr(self.recursedeps))
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000584 # If present, save 'target_os' in the local_target_os property.
585 if 'target_os' in local_scope:
586 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000587 # load os specific dependencies if defined. these dependencies may
588 # override or extend the values defined by the 'deps' member.
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000589 target_os_list = self.target_os
590 if 'deps_os' in local_scope and target_os_list:
591 deps = self.MergeWithOsDeps(deps, local_scope['deps_os'], target_os_list)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000592
maruel@chromium.org271375b2010-06-23 19:17:38 +0000593 # If a line is in custom_deps, but not in the solution, we want to append
594 # this line to the solution.
595 for d in self.custom_deps:
596 if d not in deps:
597 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000598
599 # If use_relative_paths is set in the DEPS file, regenerate
600 # the dictionary using paths relative to the directory containing
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000601 # the DEPS file. Also update recursedeps if use_relative_paths is
602 # enabled.
agabledce6ddc2016-09-08 10:02:16 -0700603 # If the deps file doesn't set use_relative_paths, but the parent did
604 # (and therefore set self.relative on this Dependency object), then we
605 # want to modify the deps and recursedeps by prepending the parent
606 # directory of this dependency.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000607 use_relative_paths = local_scope.get('use_relative_paths', False)
agabledce6ddc2016-09-08 10:02:16 -0700608 rel_prefix = None
maruel@chromium.org271375b2010-06-23 19:17:38 +0000609 if use_relative_paths:
agabledce6ddc2016-09-08 10:02:16 -0700610 rel_prefix = self.name
611 elif self._relative:
612 rel_prefix = os.path.dirname(self.name)
613 if rel_prefix:
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000614 logging.warning('use_relative_paths enabled.')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000615 rel_deps = {}
616 for d, url in deps.items():
617 # normpath is required to allow DEPS to use .. in their
618 # dependency local path.
agabledce6ddc2016-09-08 10:02:16 -0700619 rel_deps[os.path.normpath(os.path.join(rel_prefix, d))] = url
620 logging.warning('Updating deps by prepending %s.', rel_prefix)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000621 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000622
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000623 # Update recursedeps if it's set.
624 if self.recursedeps is not None:
agabledce6ddc2016-09-08 10:02:16 -0700625 logging.warning('Updating recursedeps by prepending %s.', rel_prefix)
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000626 rel_deps = {}
627 for depname, options in self.recursedeps.iteritems():
agabledce6ddc2016-09-08 10:02:16 -0700628 rel_deps[
629 os.path.normpath(os.path.join(rel_prefix, depname))] = options
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000630 self.recursedeps = rel_deps
631
agabledce6ddc2016-09-08 10:02:16 -0700632
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000633 if 'allowed_hosts' in local_scope:
634 try:
635 self._allowed_hosts = frozenset(local_scope.get('allowed_hosts'))
636 except TypeError: # raised if non-iterable
637 pass
638 if not self._allowed_hosts:
639 logging.warning("allowed_hosts is specified but empty %s",
640 self._allowed_hosts)
641 raise gclient_utils.Error(
642 'ParseDepsFile(%s): allowed_hosts must be absent '
643 'or a non-empty iterable' % self.name)
644
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000645 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000646 deps_to_add = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000647 for name, url in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000648 should_process = self.recursion_limit and self.should_process
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000649 deps_file = self.deps_file
650 if self.recursedeps is not None:
651 ent = self.recursedeps.get(name)
652 if ent is not None:
653 deps_file = ent['deps_file']
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000654 deps_to_add.append(Dependency(
agablea98a6cd2016-11-15 14:30:10 -0800655 self, name, url, None, None, self.custom_vars, None,
agabledce6ddc2016-09-08 10:02:16 -0700656 deps_file, should_process, use_relative_paths))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000657 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000658
659 # override named sets of hooks by the custom hooks
660 hooks_to_run = []
661 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
662 for hook in local_scope.get('hooks', []):
663 if hook.get('name', '') not in hook_names_to_suppress:
664 hooks_to_run.append(hook)
Scott Grahamc4826742017-05-11 16:59:23 -0700665 if 'hooks_os' in local_scope and target_os_list:
666 hooks_os = local_scope['hooks_os']
667 # Specifically append these to ensure that hooks_os run after hooks.
668 for the_target_os in target_os_list:
669 the_target_os_hooks = hooks_os.get(the_target_os, [])
670 hooks_to_run.extend(the_target_os_hooks)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000671
672 # add the replacements and any additions
673 for hook in self.custom_hooks:
674 if 'action' in hook:
675 hooks_to_run.append(hook)
676
Dirk Prankeda3a29e2017-02-27 15:29:36 -0800677 if self.recursion_limit:
Paweł Hajdan, Jr35b298f2017-05-23 14:37:05 +0200678 self._pre_deps_hooks = [self.GetHookAction(hook) for hook in
Dirk Prankeda3a29e2017-02-27 15:29:36 -0800679 local_scope.get('pre_deps_hooks', [])]
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000680
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000681 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000682 logging.info('ParseDepsFile(%s) done' % self.name)
683
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +0200684 def _get_option(self, attr, default):
685 obj = self
686 while not hasattr(obj, '_options'):
687 obj = obj.parent
688 return getattr(obj._options, attr, default)
689
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000690 def add_dependencies_and_close(self, deps_to_add, hooks):
691 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000692 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000693 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000694 self.add_dependency(dep)
695 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000696
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000697 def findDepsFromNotAllowedHosts(self):
698 """Returns a list of depenecies from not allowed hosts.
699
700 If allowed_hosts is not set, allows all hosts and returns empty list.
701 """
702 if not self._allowed_hosts:
703 return []
704 bad_deps = []
705 for dep in self._dependencies:
szager@chromium.orgbd772dd2014-11-05 18:43:08 +0000706 # Don't enforce this for custom_deps.
707 if dep.name in self._custom_deps:
708 continue
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000709 if isinstance(dep.url, basestring):
710 parsed_url = urlparse.urlparse(dep.url)
711 if parsed_url.netloc and parsed_url.netloc not in self._allowed_hosts:
712 bad_deps.append(dep)
713 return bad_deps
714
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000715 # Arguments number differs from overridden method
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800716 # pylint: disable=arguments-differ
maruel@chromium.org3742c842010-09-09 19:27:14 +0000717 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000718 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000719 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000720 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000721 if not self.should_process:
722 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000723 # When running runhooks, there's no need to consult the SCM.
724 # All known hooks are expected to run unconditionally regardless of working
725 # copy state, so skip the SCM status check.
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +0200726 run_scm = command not in (
727 'flatten', 'runhooks', 'recurse', 'validate', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000728 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000729 file_list = [] if not options.nohooks else None
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000730 revision_override = revision_overrides.pop(self.name, None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000731 if run_scm and parsed_url:
agabled437d762016-10-17 09:35:11 -0700732 # Create a shallow copy to mutate revision.
733 options = copy.copy(options)
734 options.revision = revision_override
735 self._used_revision = options.revision
736 self._used_scm = gclient_scm.CreateSCM(
737 parsed_url, self.root.root_dir, self.name, self.outbuf,
738 out_cb=work_queue.out_cb)
739 self._got_revision = self._used_scm.RunCommand(command, options, args,
740 file_list)
741 if file_list:
742 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000743
744 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
745 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000746 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000747 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000748 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000749 continue
750 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000751 [self.root.root_dir.lower(), file_list[i].lower()])
752 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000753 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000754 while file_list[i].startswith(('\\', '/')):
755 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000756
757 # Always parse the DEPS file.
758 self.ParseDepsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000759 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000760 if command in ('update', 'revert') and not options.noprehooks:
761 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000762
763 if self.recursion_limit:
764 # Parse the dependencies of this dependency.
765 for s in self.dependencies:
766 work_queue.enqueue(s)
767
768 if command == 'recurse':
agabled437d762016-10-17 09:35:11 -0700769 # Skip file only checkout.
770 scm = gclient_scm.GetScmName(parsed_url)
771 if not options.scm or scm in options.scm:
772 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
773 # Pass in the SCM type as an env variable. Make sure we don't put
774 # unicode strings in the environment.
775 env = os.environ.copy()
776 if scm:
777 env['GCLIENT_SCM'] = str(scm)
778 if parsed_url:
779 env['GCLIENT_URL'] = str(parsed_url)
780 env['GCLIENT_DEP_PATH'] = str(self.name)
781 if options.prepend_dir and scm == 'git':
782 print_stdout = False
783 def filter_fn(line):
784 """Git-specific path marshaling. It is optimized for git-grep."""
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000785
agabled437d762016-10-17 09:35:11 -0700786 def mod_path(git_pathspec):
787 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
788 modified_path = os.path.join(self.name, match.group(2))
789 branch = match.group(1) or ''
790 return '%s%s' % (branch, modified_path)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000791
agabled437d762016-10-17 09:35:11 -0700792 match = re.match('^Binary file ([^\0]+) matches$', line)
793 if match:
794 print('Binary file %s matches\n' % mod_path(match.group(1)))
795 return
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000796
agabled437d762016-10-17 09:35:11 -0700797 items = line.split('\0')
798 if len(items) == 2 and items[1]:
799 print('%s : %s' % (mod_path(items[0]), items[1]))
800 elif len(items) >= 2:
801 # Multiple null bytes or a single trailing null byte indicate
802 # git is likely displaying filenames only (such as with -l)
803 print('\n'.join(mod_path(path) for path in items if path))
804 else:
805 print(line)
806 else:
807 print_stdout = True
808 filter_fn = None
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000809
agabled437d762016-10-17 09:35:11 -0700810 if parsed_url is None:
811 print('Skipped omitted dependency %s' % cwd, file=sys.stderr)
812 elif os.path.isdir(cwd):
813 try:
814 gclient_utils.CheckCallAndFilter(
815 args, cwd=cwd, env=env, print_stdout=print_stdout,
816 filter_fn=filter_fn,
817 )
818 except subprocess2.CalledProcessError:
819 if not options.ignore:
820 raise
821 else:
822 print('Skipped missing %s' % cwd, file=sys.stderr)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000823
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000824
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000825 @gclient_utils.lockedmethod
826 def _run_is_done(self, file_list, parsed_url):
827 # Both these are kept for hooks that are run as a separate tree traversal.
828 self._file_list = file_list
829 self._parsed_url = parsed_url
830 self._processed = True
831
szager@google.comb9a78d32012-03-13 18:46:21 +0000832 @staticmethod
Paweł Hajdan, Jr35b298f2017-05-23 14:37:05 +0200833 def GetHookAction(hook_dict):
szager@google.comb9a78d32012-03-13 18:46:21 +0000834 """Turns a parsed 'hook' dict into an executable command."""
835 logging.debug(hook_dict)
szager@google.comb9a78d32012-03-13 18:46:21 +0000836 command = hook_dict['action'][:]
837 if command[0] == 'python':
838 # If the hook specified "python" as the first item, the action is a
839 # Python script. Run it by starting a new copy of the same
840 # interpreter.
841 command[0] = sys.executable
szager@google.comb9a78d32012-03-13 18:46:21 +0000842 return command
843
844 def GetHooks(self, options):
845 """Evaluates all hooks, and return them in a flat list.
846
847 RunOnDeps() must have been called before to load the DEPS.
848 """
849 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000850 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000851 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000852 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000853 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000854 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000855 if self.deps_hooks:
agabled437d762016-10-17 09:35:11 -0700856 # TODO(maruel): If the user is using git, then we don't know
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000857 # what files have changed so we always run all hooks. It'd be nice to fix
858 # that.
859 if (options.force or
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000860 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000861 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000862 for hook_dict in self.deps_hooks:
Paweł Hajdan, Jr35b298f2017-05-23 14:37:05 +0200863 result.append(self.GetHookAction(hook_dict))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000864 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000865 # Run hooks on the basis of whether the files from the gclient operation
866 # match each hook's pattern.
867 for hook_dict in self.deps_hooks:
868 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000869 matching_file_list = [
870 f for f in self.file_list_and_children if pattern.search(f)
871 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000872 if matching_file_list:
Paweł Hajdan, Jr35b298f2017-05-23 14:37:05 +0200873 result.append(self.GetHookAction(hook_dict))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000874 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000875 result.extend(s.GetHooks(options))
876 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000877
szager@google.comb9a78d32012-03-13 18:46:21 +0000878 def RunHooksRecursively(self, options):
879 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000880 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000881 for hook in self.GetHooks(options):
882 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000883 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +0000884 gclient_utils.CheckCallAndFilterAndHeader(
885 hook, cwd=self.root.root_dir, always=True)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000886 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
szager@google.comb9a78d32012-03-13 18:46:21 +0000887 # Use a discrete exit status code of 2 to indicate that a hook action
888 # failed. Users of this script may wish to treat hook action failures
889 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000890 print('Error: %s' % str(e), file=sys.stderr)
szager@google.comb9a78d32012-03-13 18:46:21 +0000891 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000892 finally:
893 elapsed_time = time.time() - start_time
894 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000895 print("Hook '%s' took %.2f secs" % (
896 gclient_utils.CommandToStr(hook), elapsed_time))
maruel@chromium.orgeaf61062010-07-07 18:42:39 +0000897
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000898 def RunPreDepsHooks(self):
899 assert self.processed
900 assert self.deps_parsed
901 assert not self.pre_deps_hooks_ran
902 assert not self.hooks_ran
903 for s in self.dependencies:
904 assert not s.processed
905 self._pre_deps_hooks_ran = True
906 for hook in self.pre_deps_hooks:
907 try:
908 start_time = time.time()
909 gclient_utils.CheckCallAndFilterAndHeader(
910 hook, cwd=self.root.root_dir, always=True)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000911 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000912 # Use a discrete exit status code of 2 to indicate that a hook action
913 # failed. Users of this script may wish to treat hook action failures
914 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000915 print('Error: %s' % str(e), file=sys.stderr)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000916 sys.exit(2)
917 finally:
918 elapsed_time = time.time() - start_time
919 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000920 print("Hook '%s' took %.2f secs" % (
921 gclient_utils.CommandToStr(hook), elapsed_time))
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000922
923
maruel@chromium.org0d812442010-08-10 12:41:08 +0000924 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000925 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000926 dependencies = self.dependencies
927 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000928 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000929 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000930 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000931 for i in d.subtree(include_all):
932 yield i
933
934 def depth_first_tree(self):
935 """Depth-first recursion including the root node."""
936 yield self
937 for i in self.dependencies:
938 for j in i.depth_first_tree():
939 if j.should_process:
940 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +0000941
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000942 @gclient_utils.lockedmethod
943 def add_dependency(self, new_dep):
944 self._dependencies.append(new_dep)
945
946 @gclient_utils.lockedmethod
947 def _mark_as_parsed(self, new_hooks):
948 self._deps_hooks.extend(new_hooks)
949 self._deps_parsed = True
950
maruel@chromium.org68988972011-09-20 14:11:42 +0000951 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000952 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000953 def dependencies(self):
954 return tuple(self._dependencies)
955
956 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000957 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000958 def deps_hooks(self):
959 return tuple(self._deps_hooks)
960
961 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000962 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000963 def pre_deps_hooks(self):
964 return tuple(self._pre_deps_hooks)
965
966 @property
967 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000968 def parsed_url(self):
969 return self._parsed_url
970
971 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000972 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000973 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000974 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +0000975 return self._deps_parsed
976
977 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000978 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000979 def processed(self):
980 return self._processed
981
982 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000983 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000984 def pre_deps_hooks_ran(self):
985 return self._pre_deps_hooks_ran
986
987 @property
988 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000989 def hooks_ran(self):
990 return self._hooks_ran
991
992 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000993 @gclient_utils.lockedmethod
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000994 def allowed_hosts(self):
995 return self._allowed_hosts
996
997 @property
998 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000999 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001000 return tuple(self._file_list)
1001
1002 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +00001003 def used_scm(self):
1004 """SCMWrapper instance for this dependency or None if not processed yet."""
1005 return self._used_scm
1006
1007 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001008 @gclient_utils.lockedmethod
1009 def got_revision(self):
1010 return self._got_revision
1011
1012 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001013 def file_list_and_children(self):
1014 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001015 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001016 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +00001017 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001018
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001019 def __str__(self):
1020 out = []
agablea98a6cd2016-11-15 14:30:10 -08001021 for i in ('name', 'url', 'parsed_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001022 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001023 'processed', 'hooks_ran', 'deps_parsed', 'requirements',
1024 'allowed_hosts'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001025 # First try the native property if it exists.
1026 if hasattr(self, '_' + i):
1027 value = getattr(self, '_' + i, False)
1028 else:
1029 value = getattr(self, i, False)
1030 if value:
1031 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001032
1033 for d in self.dependencies:
1034 out.extend([' ' + x for x in str(d).splitlines()])
1035 out.append('')
1036 return '\n'.join(out)
1037
1038 def __repr__(self):
1039 return '%s: %s' % (self.name, self.url)
1040
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001041 def hierarchy(self, include_url=True):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +00001042 """Returns a human-readable hierarchical reference to a Dependency."""
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001043 def format_name(d):
1044 if include_url:
1045 return '%s(%s)' % (d.name, d.url)
1046 return d.name
1047 out = format_name(self)
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001048 i = self.parent
1049 while i and i.name:
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001050 out = '%s -> %s' % (format_name(i), out)
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001051 i = i.parent
1052 return out
1053
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001054
1055class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001056 """Object that represent a gclient checkout. A tree of Dependency(), one per
1057 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001058
1059 DEPS_OS_CHOICES = {
1060 "win32": "win",
1061 "win": "win",
1062 "cygwin": "win",
1063 "darwin": "mac",
1064 "mac": "mac",
1065 "unix": "unix",
1066 "linux": "unix",
1067 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +00001068 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +00001069 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001070 }
1071
1072 DEFAULT_CLIENT_FILE_TEXT = ("""\
1073solutions = [
smutae7ea312016-07-18 11:59:41 -07001074 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001075 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001076 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001077 "managed" : %(managed)s,
smutae7ea312016-07-18 11:59:41 -07001078 "custom_deps" : {
1079 },
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001080 },
1081]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001082cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001083""")
1084
1085 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
smutae7ea312016-07-18 11:59:41 -07001086 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001087 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001088 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001089 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001090 "custom_deps" : {
smutae7ea312016-07-18 11:59:41 -07001091%(solution_deps)s },
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001092 },
1093""")
1094
1095 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1096# Snapshot generated with gclient revinfo --snapshot
1097solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001098%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001099""")
1100
1101 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001102 # Do not change previous behavior. Only solution level and immediate DEPS
1103 # are processed.
1104 self._recursion_limit = 2
agablea98a6cd2016-11-15 14:30:10 -08001105 Dependency.__init__(self, None, None, None, True, None, None, None,
agabledce6ddc2016-09-08 10:02:16 -07001106 'unused', True, None)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001107 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001108 if options.deps_os:
1109 enforced_os = options.deps_os.split(',')
1110 else:
1111 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1112 if 'all' in enforced_os:
1113 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001114 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001115 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001116 self.config_content = None
1117
borenet@google.com88d10082014-03-21 17:24:48 +00001118 def _CheckConfig(self):
1119 """Verify that the config matches the state of the existing checked-out
1120 solutions."""
1121 for dep in self.dependencies:
1122 if dep.managed and dep.url:
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001123 scm = gclient_scm.CreateSCM(
1124 dep.url, self.root_dir, dep.name, self.outbuf)
smut@google.comd33eab32014-07-07 19:35:18 +00001125 actual_url = scm.GetActualRemoteURL(self._options)
borenet@google.com4e9be262014-04-08 19:40:30 +00001126 if actual_url and not scm.DoesRemoteURLMatch(self._options):
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001127 mirror = scm.GetCacheMirror()
1128 if mirror:
1129 mirror_string = '%s (exists=%s)' % (mirror.mirror_path,
1130 mirror.exists())
1131 else:
1132 mirror_string = 'not used'
borenet@google.com0a427372014-04-02 19:12:13 +00001133 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001134Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001135is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001136
borenet@google.com97882362014-04-07 20:06:02 +00001137The .gclient file contains:
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001138URL: %(expected_url)s (%(expected_scm)s)
1139Cache mirror: %(mirror_string)s
borenet@google.com97882362014-04-07 20:06:02 +00001140
1141The local checkout in %(checkout_path)s reports:
1142%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001143
1144You should ensure that the URL listed in .gclient is correct and either change
agabled437d762016-10-17 09:35:11 -07001145it or fix the checkout.
borenet@google.com88d10082014-03-21 17:24:48 +00001146''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1147 'expected_url': dep.url,
1148 'expected_scm': gclient_scm.GetScmName(dep.url),
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001149 'mirror_string' : mirror_string,
borenet@google.com88d10082014-03-21 17:24:48 +00001150 'actual_url': actual_url,
1151 'actual_scm': gclient_scm.GetScmName(actual_url)})
1152
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001153 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001154 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001155 config_dict = {}
1156 self.config_content = content
1157 try:
1158 exec(content, config_dict)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001159 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001160 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001161
peter@chromium.org1efccc82012-04-27 16:34:38 +00001162 # Append any target OS that is not already being enforced to the tuple.
1163 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001164 if config_dict.get('target_os_only', False):
1165 self._enforced_os = tuple(set(target_os))
1166 else:
1167 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1168
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001169 cache_dir = config_dict.get('cache_dir')
1170 if cache_dir:
1171 cache_dir = os.path.join(self.root_dir, cache_dir)
1172 cache_dir = os.path.abspath(cache_dir)
szager@chromium.orgcaf5bef2014-08-24 18:56:32 +00001173 # If running on a bot, force break any stale git cache locks.
dnj@chromium.orgb682b3e2014-08-25 19:17:12 +00001174 if os.path.exists(cache_dir) and os.environ.get('CHROME_HEADLESS'):
szager@chromium.org4848fb62014-08-24 19:16:31 +00001175 subprocess2.check_call(['git', 'cache', 'unlock', '--cache-dir',
1176 cache_dir, '--force', '--all'])
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001177 gclient_scm.GitWrapper.cache_dir = cache_dir
1178 git_cache.Mirror.SetCachePath(cache_dir)
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001179
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001180 if not target_os and config_dict.get('target_os_only', False):
1181 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1182 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001183
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001184 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001185 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001186 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001187 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +00001188 self, s['name'], s['url'],
smutae7ea312016-07-18 11:59:41 -07001189 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001190 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001191 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001192 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001193 s.get('deps_file', 'DEPS'),
agabledce6ddc2016-09-08 10:02:16 -07001194 True,
1195 None))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001196 except KeyError:
1197 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1198 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001199 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1200 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001201
1202 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001203 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001204 self._options.config_filename),
1205 self.config_content)
1206
1207 @staticmethod
1208 def LoadCurrentConfig(options):
1209 """Searches for and loads a .gclient file relative to the current working
1210 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001211 if options.spec:
1212 client = GClient('.', options)
1213 client.SetConfig(options.spec)
1214 else:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001215 if options.verbose:
1216 print('Looking for %s starting from %s\n' % (
1217 options.config_filename, os.getcwd()))
szager@chromium.orge2e03202012-07-31 18:05:16 +00001218 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1219 if not path:
1220 return None
1221 client = GClient(path, options)
1222 client.SetConfig(gclient_utils.FileRead(
1223 os.path.join(path, options.config_filename)))
maruel@chromium.org69392e72011-10-13 22:09:00 +00001224
1225 if (options.revisions and
1226 len(client.dependencies) > 1 and
1227 any('@' not in r for r in options.revisions)):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001228 print(
1229 ('You must specify the full solution name like --revision %s@%s\n'
1230 'when you have multiple solutions setup in your .gclient file.\n'
1231 'Other solutions present are: %s.') % (
maruel@chromium.org69392e72011-10-13 22:09:00 +00001232 client.dependencies[0].name,
1233 options.revisions[0],
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001234 ', '.join(s.name for s in client.dependencies[1:])),
1235 file=sys.stderr)
maruel@chromium.org15804092010-09-02 17:07:37 +00001236 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001237
nsylvain@google.comefc80932011-05-31 21:27:56 +00001238 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
agablea98a6cd2016-11-15 14:30:10 -08001239 managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001240 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1241 'solution_name': solution_name,
1242 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001243 'deps_file': deps_file,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001244 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001245 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001246 })
1247
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001248 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001249 """Creates a .gclient_entries file to record the list of unique checkouts.
1250
1251 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001252 """
1253 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1254 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001255 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001256 for entry in self.root.subtree(False):
agabled437d762016-10-17 09:35:11 -07001257 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1258 pprint.pformat(entry.parsed_url))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001259 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001260 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001261 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001262 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001263
1264 def _ReadEntries(self):
1265 """Read the .gclient_entries file for the given client.
1266
1267 Returns:
1268 A sequence of solution names, which will be empty if there is the
1269 entries file hasn't been created yet.
1270 """
1271 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001272 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001273 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001274 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001275 try:
1276 exec(gclient_utils.FileRead(filename), scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001277 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001278 gclient_utils.SyntaxErrorToError(filename, e)
Aaron Gable3721ee92017-04-03 14:53:14 -07001279 return scope.get('entries', {})
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001280
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001281 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001282 """Checks for revision overrides."""
1283 revision_overrides = {}
smutae7ea312016-07-18 11:59:41 -07001284 if self._options.head:
1285 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001286 if not self._options.revisions:
1287 for s in self.dependencies:
smutae7ea312016-07-18 11:59:41 -07001288 if not s.managed:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001289 self._options.revisions.append('%s@unmanaged' % s.name)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001290 if not self._options.revisions:
1291 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001292 solutions_names = [s.name for s in self.dependencies]
smutae7ea312016-07-18 11:59:41 -07001293 index = 0
1294 for revision in self._options.revisions:
1295 if not '@' in revision:
maruel@chromium.org307d1792010-05-31 20:03:13 +00001296 # Support for --revision 123
smutae7ea312016-07-18 11:59:41 -07001297 revision = '%s@%s' % (solutions_names[index], revision)
1298 name, rev = revision.split('@', 1)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001299 revision_overrides[name] = rev
smutae7ea312016-07-18 11:59:41 -07001300 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001301 return revision_overrides
1302
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001303 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001304 """Runs a command on each dependency in a client and its dependencies.
1305
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001306 Args:
1307 command: The command to use (e.g., 'status' or 'diff')
1308 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001309 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001310 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001311 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001312
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001313 revision_overrides = {}
1314 # It's unnecessary to check for revision overrides for 'recurse'.
1315 # Save a few seconds by not calling _EnforceRevisions() in that case.
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02001316 if command not in ('diff', 'recurse', 'runhooks', 'status', 'revert',
1317 'validate'):
szager@chromium.org5273b8a2014-08-21 15:10:10 +00001318 self._CheckConfig()
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001319 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001320 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001321 # Disable progress for non-tty stdout.
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00001322 if (setup_color.IS_TTY and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001323 if command in ('update', 'revert'):
1324 pm = Progress('Syncing projects', 1)
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02001325 elif command in ('recurse', 'validate'):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001326 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001327 work_queue = gclient_utils.ExecutionQueue(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001328 self._options.jobs, pm, ignore_requirements=ignore_requirements,
1329 verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001330 for s in self.dependencies:
1331 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001332 work_queue.flush(revision_overrides, command, args, options=self._options)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001333 if revision_overrides:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001334 print('Please fix your script, having invalid --revision flags will soon '
1335 'considered an error.', file=sys.stderr)
piman@chromium.org6f363722010-04-27 00:41:09 +00001336
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001337 # Once all the dependencies have been processed, it's now safe to run the
1338 # hooks.
1339 if not self._options.nohooks:
1340 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001341
1342 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001343 # Notify the user if there is an orphaned entry in their working copy.
1344 # Only delete the directory if there are no changes in it, and
1345 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001346 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001347 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1348 for e in entries]
1349
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001350 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001351 if not prev_url:
1352 # entry must have been overridden via .gclient custom_deps
1353 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001354 # Fix path separator on Windows.
1355 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001356 e_dir = os.path.join(self.root_dir, entry_fixed)
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001357 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001358 if (entry not in entries and
1359 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001360 os.path.exists(e_dir)):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001361 # The entry has been removed from DEPS.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001362 scm = gclient_scm.CreateSCM(
1363 prev_url, self.root_dir, entry_fixed, self.outbuf)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001364
1365 # Check to see if this directory is now part of a higher-up checkout.
borenet@google.com359bb642014-05-13 17:28:19 +00001366 scm_root = None
agabled437d762016-10-17 09:35:11 -07001367 try:
1368 scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(scm.checkout_path)
1369 except subprocess2.CalledProcessError:
1370 pass
1371 if not scm_root:
borenet@google.com359bb642014-05-13 17:28:19 +00001372 logging.warning('Could not find checkout root for %s. Unable to '
1373 'determine whether it is part of a higher-level '
1374 'checkout, so not removing.' % entry)
1375 continue
primiano@chromium.org1c127382015-02-17 11:15:40 +00001376
1377 # This is to handle the case of third_party/WebKit migrating from
1378 # being a DEPS entry to being part of the main project.
1379 # If the subproject is a Git project, we need to remove its .git
1380 # folder. Otherwise git operations on that folder will have different
1381 # effects depending on the current working directory.
agabled437d762016-10-17 09:35:11 -07001382 if os.path.abspath(scm_root) == os.path.abspath(e_dir):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001383 e_par_dir = os.path.join(e_dir, os.pardir)
agabled437d762016-10-17 09:35:11 -07001384 if gclient_scm.scm.GIT.IsInsideWorkTree(e_par_dir):
1385 par_scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(e_par_dir)
primiano@chromium.org1c127382015-02-17 11:15:40 +00001386 # rel_e_dir : relative path of entry w.r.t. its parent repo.
1387 rel_e_dir = os.path.relpath(e_dir, par_scm_root)
agabled437d762016-10-17 09:35:11 -07001388 if gclient_scm.scm.GIT.IsDirectoryVersioned(
1389 par_scm_root, rel_e_dir):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001390 save_dir = scm.GetGitBackupDirPath()
1391 # Remove any eventual stale backup dir for the same project.
1392 if os.path.exists(save_dir):
1393 gclient_utils.rmtree(save_dir)
1394 os.rename(os.path.join(e_dir, '.git'), save_dir)
1395 # When switching between the two states (entry/ is a subproject
1396 # -> entry/ is part of the outer project), it is very likely
1397 # that some files are changed in the checkout, unless we are
1398 # jumping *exactly* across the commit which changed just DEPS.
1399 # In such case we want to cleanup any eventual stale files
1400 # (coming from the old subproject) in order to end up with a
1401 # clean checkout.
agabled437d762016-10-17 09:35:11 -07001402 gclient_scm.scm.GIT.CleanupDir(par_scm_root, rel_e_dir)
primiano@chromium.org1c127382015-02-17 11:15:40 +00001403 assert not os.path.exists(os.path.join(e_dir, '.git'))
1404 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher '
1405 'level checkout. The git folder containing all the local'
1406 ' branches has been saved to %s.\n'
1407 'If you don\'t care about its state you can safely '
1408 'remove that folder to free up space.') %
1409 (entry, save_dir))
1410 continue
1411
borenet@google.com359bb642014-05-13 17:28:19 +00001412 if scm_root in full_entries:
primiano@chromium.org1c127382015-02-17 11:15:40 +00001413 logging.info('%s is part of a higher level checkout, not removing',
1414 scm.GetCheckoutRoot())
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001415 continue
1416
1417 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001418 scm.status(self._options, [], file_list)
1419 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001420 if (not self._options.delete_unversioned_trees or
1421 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001422 # There are modified files in this entry. Keep warning until
1423 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001424 print(('\nWARNING: \'%s\' is no longer part of this client. '
1425 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001426 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001427 else:
1428 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001429 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001430 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001431 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001432 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001433 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001434 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001435
1436 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001437 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001438 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001439 # Load all the settings.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001440 work_queue = gclient_utils.ExecutionQueue(
1441 self._options.jobs, None, False, verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001442 for s in self.dependencies:
1443 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001444 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001445
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001446 def GetURLAndRev(dep):
1447 """Returns the revision-qualified SCM url for a Dependency."""
1448 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001449 return None
agabled437d762016-10-17 09:35:11 -07001450 url, _ = gclient_utils.SplitUrlRevision(dep.parsed_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001451 scm = gclient_scm.CreateSCM(
agabled437d762016-10-17 09:35:11 -07001452 dep.parsed_url, self.root_dir, dep.name, self.outbuf)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001453 if not os.path.isdir(scm.checkout_path):
1454 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001455 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001456
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001457 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001458 new_gclient = ''
1459 # First level at .gclient
1460 for d in self.dependencies:
1461 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001462 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001463 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001464 for d in dep.dependencies:
1465 entries[d.name] = GetURLAndRev(d)
1466 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001467 GrabDeps(d)
1468 custom_deps = []
1469 for k in sorted(entries.keys()):
1470 if entries[k]:
1471 # Quotes aren't escaped...
1472 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1473 else:
1474 custom_deps.append(' \"%s\": None,\n' % k)
1475 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1476 'solution_name': d.name,
1477 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001478 'deps_file': d.deps_file,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001479 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001480 'solution_deps': ''.join(custom_deps),
1481 }
1482 # Print the snapshot configuration file
1483 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001484 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001485 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001486 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001487 if self._options.actual:
1488 entries[d.name] = GetURLAndRev(d)
1489 else:
1490 entries[d.name] = d.parsed_url
1491 keys = sorted(entries.keys())
1492 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001493 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001494 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001495
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001496 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001497 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001498 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001499
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001500 def PrintLocationAndContents(self):
1501 # Print out the .gclient file. This is longer than if we just printed the
1502 # client dict, but more legible, and it might contain helpful comments.
1503 print('Loaded .gclient config in %s:\n%s' % (
1504 self.root_dir, self.config_content))
1505
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001506 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001507 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001508 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001509 return self._root_dir
1510
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001511 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001512 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001513 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001514 return self._enforced_os
1515
maruel@chromium.org68988972011-09-20 14:11:42 +00001516 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001517 def recursion_limit(self):
1518 """How recursive can each dependencies in DEPS file can load DEPS file."""
1519 return self._recursion_limit
1520
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001521 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +00001522 def try_recursedeps(self):
1523 """Whether to attempt using recursedeps-style recursion processing."""
cmp@chromium.orge84ac912014-06-30 23:14:35 +00001524 return True
1525
1526 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001527 def target_os(self):
1528 return self._enforced_os
1529
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001530
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001531#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001532
1533
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001534@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001535def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001536 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001537
1538 Runs a shell command on all entries.
qyearsley12fa6ff2016-08-24 09:18:40 -07001539 Sets GCLIENT_DEP_PATH environment variable as the dep's relative location to
ilevy@chromium.org37116242012-11-28 01:32:48 +00001540 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001541 """
1542 # Stop parsing at the first non-arg so that these go through to the command
1543 parser.disable_interspersed_args()
1544 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001545 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001546 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001547 help='Ignore non-zero return codes from subcommands.')
1548 parser.add_option('--prepend-dir', action='store_true',
1549 help='Prepend relative dir for use with git <cmd> --null.')
1550 parser.add_option('--no-progress', action='store_true',
1551 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001552 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001553 if not args:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001554 print('Need to supply a command!', file=sys.stderr)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001555 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001556 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1557 if not root_and_entries:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001558 print(
maruel@chromium.org78cba522010-10-18 13:32:05 +00001559 'You need to run gclient sync at least once to use \'recurse\'.\n'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001560 'This is because .gclient_entries needs to exist and be up to date.',
1561 file=sys.stderr)
maruel@chromium.org78cba522010-10-18 13:32:05 +00001562 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001563
1564 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001565 scm_set = set()
1566 for scm in options.scm:
1567 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001568 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001569
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001570 options.nohooks = True
1571 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001572 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1573 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001574
1575
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001576@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001577def CMDfetch(parser, args):
1578 """Fetches upstream commits for all modules.
1579
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001580 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1581 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001582 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001583 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001584 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1585
1586
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001587def CMDflatten(parser, args):
1588 """Flattens the solutions into a single DEPS file."""
1589 parser.add_option('--output-deps', help='Path to the output DEPS file')
1590 parser.add_option(
1591 '--require-pinned-revisions', action='store_true',
1592 help='Fail if any of the dependencies uses unpinned revision.')
1593 options, args = parser.parse_args(args)
1594
1595 options.nohooks = True
1596 client = GClient.LoadCurrentConfig(options)
1597
1598 # Only print progress if we're writing to a file. Otherwise, progress updates
1599 # could obscure intended output.
1600 code = client.RunOnDeps('flatten', args, progress=options.output_deps)
1601 if code != 0:
1602 return code
1603
1604 deps = {}
1605 hooks = []
1606 pre_deps_hooks = []
1607 unpinned_deps = {}
1608
1609 for solution in client.dependencies:
1610 _FlattenSolution(solution, deps, hooks, pre_deps_hooks, unpinned_deps)
1611
1612 if options.require_pinned_revisions and unpinned_deps:
1613 sys.stderr.write('The following dependencies are not pinned:\n')
1614 sys.stderr.write('\n'.join(sorted(unpinned_deps)))
1615 return 1
1616
1617 flattened_deps = '\n'.join(
1618 _DepsToLines(deps) +
1619 _HooksToLines('hooks', hooks) +
1620 _HooksToLines('pre_deps_hooks', pre_deps_hooks) +
1621 [''] # Ensure newline at end of file.
1622 )
1623
1624 if options.output_deps:
1625 with open(options.output_deps, 'w') as f:
1626 f.write(flattened_deps)
1627 else:
1628 print(flattened_deps)
1629
1630 return 0
1631
1632
1633def _FlattenSolution(solution, deps, hooks, pre_deps_hooks, unpinned_deps):
1634 """Visits a solution in order to flatten it (see CMDflatten).
1635
1636 Arguments:
1637 solution (Dependency): one of top-level solutions in .gclient
1638
1639 Out-parameters:
1640 deps (dict of name -> Dependency): will be filled with all Dependency
1641 objects indexed by their name
1642 hooks (list of (Dependency, hook)): will be filled with flattened hooks
1643 pre_deps_hooks (list of (Dependency, hook)): will be filled with flattened
1644 pre_deps_hooks
1645 unpinned_deps (dict of name -> Dependency): will be filled with unpinned
1646 deps
1647 """
1648 logging.debug('_FlattenSolution(%r)', solution)
1649
1650 _FlattenDep(solution, deps, hooks, pre_deps_hooks, unpinned_deps)
1651 _FlattenRecurse(solution, deps, hooks, pre_deps_hooks, unpinned_deps)
1652
1653
1654def _FlattenDep(dep, deps, hooks, pre_deps_hooks, unpinned_deps):
1655 """Visits a dependency in order to flatten it (see CMDflatten).
1656
1657 Arguments:
1658 dep (Dependency): dependency to process
1659
1660 Out-parameters:
1661 deps (dict): will be filled with flattened deps
1662 hooks (list): will be filled with flattened hooks
1663 pre_deps_hooks (list): will be filled with flattened pre_deps_hooks
1664 unpinned_deps (dict): will be filled with unpinned deps
1665 """
1666 logging.debug('_FlattenDep(%r)', dep)
1667
1668 _AddDep(dep, deps, unpinned_deps)
1669
1670 deps_by_name = dict((d.name, d) for d in dep.dependencies)
1671 for recurse_dep_name in (dep.recursedeps or []):
1672 _FlattenRecurse(
1673 deps_by_name[recurse_dep_name], deps, hooks, pre_deps_hooks,
1674 unpinned_deps)
1675
1676 # TODO(phajdan.jr): also handle hooks_os.
1677 hooks.extend([(dep, hook) for hook in dep.deps_hooks])
1678 pre_deps_hooks.extend(
1679 [(dep, {'action': hook}) for hook in dep.pre_deps_hooks])
1680
1681
1682def _FlattenRecurse(dep, deps, hooks, pre_deps_hooks, unpinned_deps):
1683 """Helper for flatten that recurses into |dep|'s dependencies.
1684
1685 Arguments:
1686 dep (Dependency): dependency to process
1687
1688 Out-parameters:
1689 deps (dict): will be filled with flattened deps
1690 hooks (list): will be filled with flattened hooks
1691 pre_deps_hooks (list): will be filled with flattened pre_deps_hooks
1692 unpinned_deps (dict): will be filled with unpinned deps
1693 """
1694 logging.debug('_FlattenRecurse(%r)', dep)
1695
1696 # TODO(phajdan.jr): also handle deps_os.
1697 for dep in dep.dependencies:
1698 _FlattenDep(dep, deps, hooks, pre_deps_hooks, unpinned_deps)
1699
1700
1701def _AddDep(dep, deps, unpinned_deps):
1702 """Helper to add a dependency to flattened lists.
1703
1704 Arguments:
1705 dep (Dependency): dependency to process
1706
1707 Out-parameters:
1708 deps (dict): will be filled with flattened deps
1709 unpinned_deps (dict): will be filled with unpinned deps
1710 """
1711 logging.debug('_AddDep(%r)', dep)
1712
1713 assert dep.name not in deps
1714 deps[dep.name] = dep
1715
1716 # Detect unpinned deps.
1717 _, revision = gclient_utils.SplitUrlRevision(dep.url)
1718 if not revision or not gclient_utils.IsGitSha(revision):
1719 unpinned_deps[dep.name] = dep
1720
1721
1722def _DepsToLines(deps):
1723 """Converts |deps| dict to list of lines for output."""
1724 s = ['deps = {']
1725 for name, dep in sorted(deps.iteritems()):
1726 s.extend([
1727 ' # %s' % dep.hierarchy(include_url=False),
1728 ' "%s": "%s",' % (name, dep.url),
1729 '',
1730 ])
1731 s.extend(['}', ''])
1732 return s
1733
1734
1735def _HooksToLines(name, hooks):
1736 """Converts |hooks| list to list of lines for output."""
1737 s = ['%s = [' % name]
1738 for dep, hook in hooks:
1739 s.extend([
1740 ' # %s' % dep.hierarchy(include_url=False),
1741 ' {',
1742 ])
1743 if 'name' in hook:
1744 s.append(' "name": "%s",' % hook['name'])
1745 if 'pattern' in hook:
1746 s.append(' "pattern": "%s",' % hook['pattern'])
1747 # TODO(phajdan.jr): actions may contain paths that need to be adjusted,
1748 # i.e. they may be relative to the dependency path, not solution root.
1749 s.extend(
1750 [' "action": ['] +
1751 [' "%s",' % arg for arg in hook['action']] +
1752 [' ]', ' },', '']
1753 )
1754 s.extend([']', ''])
1755 return s
1756
1757
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001758def CMDgrep(parser, args):
1759 """Greps through git repos managed by gclient.
1760
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001761 Runs 'git grep [args...]' for each module.
1762 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001763 # We can't use optparse because it will try to parse arguments sent
1764 # to git grep and throw an error. :-(
1765 if not args or re.match('(-h|--help)$', args[0]):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001766 print(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001767 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1768 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1769 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1770 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001771 ' end of your query.',
1772 file=sys.stderr)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001773 return 1
1774
1775 jobs_arg = ['--jobs=1']
1776 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1777 jobs_arg, args = args[:1], args[1:]
1778 elif re.match(r'(-j|--jobs)$', args[0]):
1779 jobs_arg, args = args[:2], args[2:]
1780
1781 return CMDrecurse(
1782 parser,
1783 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1784 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001785
1786
stip@chromium.orga735da22015-04-29 23:18:20 +00001787def CMDroot(parser, args):
1788 """Outputs the solution root (or current dir if there isn't one)."""
1789 (options, args) = parser.parse_args(args)
1790 client = GClient.LoadCurrentConfig(options)
1791 if client:
1792 print(os.path.abspath(client.root_dir))
1793 else:
1794 print(os.path.abspath('.'))
1795
1796
agablea98a6cd2016-11-15 14:30:10 -08001797@subcommand.usage('[url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001798def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001799 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001800
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001801 This specifies the configuration for further commands. After update/sync,
1802 top-level DEPS files in each module are read to determine dependent
1803 modules to operate on as well. If optional [url] parameter is
1804 provided, then configuration is read from a specified Subversion server
1805 URL.
1806 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00001807 # We do a little dance with the --gclientfile option. 'gclient config' is the
1808 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1809 # arguments. So, we temporarily stash any --gclientfile parameter into
1810 # options.output_config_file until after the (gclientfile xor spec) error
1811 # check.
1812 parser.remove_option('--gclientfile')
1813 parser.add_option('--gclientfile', dest='output_config_file',
1814 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001815 parser.add_option('--name',
1816 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001817 parser.add_option('--deps-file', default='DEPS',
1818 help='overrides the default name for the DEPS file for the'
1819 'main solutions and all sub-dependencies')
smutae7ea312016-07-18 11:59:41 -07001820 parser.add_option('--unmanaged', action='store_true', default=False,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001821 help='overrides the default behavior to make it possible '
smutae7ea312016-07-18 11:59:41 -07001822 'to have the main solution untouched by gclient '
1823 '(gclient will check out unmanaged dependencies but '
1824 'will never sync them)')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001825 parser.add_option('--cache-dir',
1826 help='(git only) Cache all git repos into this dir and do '
1827 'shared clones from the cache, instead of cloning '
1828 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001829 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001830 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001831 if options.output_config_file:
1832 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001833 if ((options.spec and args) or len(args) > 2 or
1834 (not options.spec and not args)):
1835 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1836
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001837 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001838 if options.spec:
1839 client.SetConfig(options.spec)
1840 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001841 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001842 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001843 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001844 if name.endswith('.git'):
1845 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001846 else:
1847 # specify an alternate relpath for the given URL.
1848 name = options.name
agable@chromium.orgf2214672015-10-27 21:02:48 +00001849 if not os.path.abspath(os.path.join(os.getcwd(), name)).startswith(
1850 os.getcwd()):
1851 parser.error('Do not pass a relative path for --name.')
1852 if any(x in ('..', '.', '/', '\\') for x in name.split(os.sep)):
1853 parser.error('Do not include relative path components in --name.')
1854
nsylvain@google.comefc80932011-05-31 21:27:56 +00001855 deps_file = options.deps_file
agablea98a6cd2016-11-15 14:30:10 -08001856 client.SetDefaultConfig(name, deps_file, base_url,
smutae7ea312016-07-18 11:59:41 -07001857 managed=not options.unmanaged,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001858 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001859 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001860 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001861
1862
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001863@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001864 gclient pack > patch.txt
1865 generate simple patch for configured client and dependences
1866""")
1867def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001868 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001869
agabled437d762016-10-17 09:35:11 -07001870 Internally, runs 'git diff' on each checked out module and
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001871 dependencies, and performs minimal postprocessing of the output. The
1872 resulting patch is printed to stdout and can be applied to a freshly
1873 checked out tree via 'patch -p0 < patchfile'.
1874 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001875 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1876 help='override deps for the specified (comma-separated) '
1877 'platform(s); \'all\' will process all deps_os '
1878 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001879 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001880 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001881 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001882 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001883 client = GClient.LoadCurrentConfig(options)
1884 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001885 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001886 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001887 client.PrintLocationAndContents()
kbr@google.comab318592009-09-04 00:54:55 +00001888 return client.RunOnDeps('pack', args)
1889
1890
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001891def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001892 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001893 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1894 help='override deps for the specified (comma-separated) '
1895 'platform(s); \'all\' will process all deps_os '
1896 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001897 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001898 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001899 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001900 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001901 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001902 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001903 return client.RunOnDeps('status', args)
1904
1905
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001906@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001907 gclient sync
1908 update files from SCM according to current configuration,
1909 *for modules which have changed since last update or sync*
1910 gclient sync --force
1911 update files from SCM according to current configuration, for
1912 all modules (useful for recovering files deleted from local copy)
1913 gclient sync --revision src@31000
1914 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001915
1916JSON output format:
1917If the --output-json option is specified, the following document structure will
1918be emitted to the provided file. 'null' entries may occur for subprojects which
1919are present in the gclient solution, but were not processed (due to custom_deps,
1920os_deps, etc.)
1921
1922{
1923 "solutions" : {
1924 "<name>": { # <name> is the posix-normalized path to the solution.
agabled437d762016-10-17 09:35:11 -07001925 "revision": [<git id hex string>|null],
1926 "scm": ["git"|null],
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001927 }
1928 }
1929}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001930""")
1931def CMDsync(parser, args):
1932 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001933 parser.add_option('-f', '--force', action='store_true',
1934 help='force update even for unchanged modules')
1935 parser.add_option('-n', '--nohooks', action='store_true',
1936 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001937 parser.add_option('-p', '--noprehooks', action='store_true',
1938 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001939 parser.add_option('-r', '--revision', action='append',
1940 dest='revisions', metavar='REV', default=[],
1941 help='Enforces revision/hash for the solutions with the '
1942 'format src@rev. The src@ part is optional and can be '
1943 'skipped. -r can be used multiple times when .gclient '
1944 'has multiple solutions configured and will work even '
agablea98a6cd2016-11-15 14:30:10 -08001945 'if the src@ part is skipped.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00001946 parser.add_option('--with_branch_heads', action='store_true',
1947 help='Clone git "branch_heads" refspecs in addition to '
1948 'the default refspecs. This adds about 1/2GB to a '
1949 'full checkout. (git only)')
szager@chromium.org8d3348f2014-08-19 22:49:16 +00001950 parser.add_option('--with_tags', action='store_true',
1951 help='Clone git tags in addition to the default refspecs.')
agable2697cd12016-06-28 10:23:53 -07001952 parser.add_option('-H', '--head', action='store_true',
agablea98a6cd2016-11-15 14:30:10 -08001953 help='DEPRECATED: only made sense with safesync urls.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001954 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001955 help='Deletes from the working copy any dependencies that '
1956 'have been removed since the last sync, as long as '
1957 'there are no local modifications. When used with '
1958 '--force, such dependencies are removed even if they '
1959 'have local modifications. When used with --reset, '
1960 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00001961 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001962 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001963 parser.add_option('-R', '--reset', action='store_true',
1964 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00001965 parser.add_option('-M', '--merge', action='store_true',
1966 help='merge upstream changes instead of trying to '
1967 'fast-forward or rebase')
dnj@chromium.org5b23e872015-02-20 21:25:57 +00001968 parser.add_option('-A', '--auto_rebase', action='store_true',
1969 help='Automatically rebase repositories against local '
1970 'checkout during update (git only).')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001971 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1972 help='override deps for the specified (comma-separated) '
1973 'platform(s); \'all\' will process all deps_os '
1974 'references')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00001975 parser.add_option('--upstream', action='store_true',
1976 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001977 parser.add_option('--output-json',
1978 help='Output a json document to this path containing '
1979 'summary information about the sync.')
primiano@chromium.org5439ea52014-08-06 17:18:18 +00001980 parser.add_option('--no-history', action='store_true',
1981 help='GIT ONLY - Reduces the size/time of the checkout at '
1982 'the cost of no history. Requires Git 1.9+')
hinoka@chromium.org46b87412014-05-15 00:42:05 +00001983 parser.add_option('--shallow', action='store_true',
1984 help='GIT ONLY - Do a shallow clone into the cache dir. '
1985 'Requires Git 1.9+')
e.hakkinen@samsung.come8bc1aa2015-04-08 08:00:37 +00001986 parser.add_option('--no_bootstrap', '--no-bootstrap',
1987 action='store_true',
1988 help='Don\'t bootstrap from Google Storage.')
hinoka@chromium.org8a10f6d2014-06-23 18:38:57 +00001989 parser.add_option('--ignore_locks', action='store_true',
1990 help='GIT ONLY - Ignore cache locks.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +00001991 parser.add_option('--break_repo_locks', action='store_true',
1992 help='GIT ONLY - Forcibly remove repo locks (e.g. '
1993 'index.lock). This should only be used if you know for '
1994 'certain that this invocation of gclient is the only '
1995 'thing operating on the git repos (e.g. on a bot).')
nodir@chromium.org5b48e482016-03-18 20:27:54 +00001996 parser.add_option('--lock_timeout', type='int', default=5000,
szager@chromium.orgdbb6f822016-02-02 22:59:30 +00001997 help='GIT ONLY - Deadline (in seconds) to wait for git '
nodir@chromium.org5b48e482016-03-18 20:27:54 +00001998 'cache lock to become available. Default is %default.')
agabled437d762016-10-17 09:35:11 -07001999 # TODO(agable): Remove these when the oldest CrOS release milestone is M56.
2000 parser.add_option('-t', '--transitive', action='store_true',
2001 help='DEPRECATED: This is a no-op.')
sdefresne69b1be12016-10-18 05:48:02 -07002002 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
agabled437d762016-10-17 09:35:11 -07002003 help='DEPRECATED: This is a no-op.')
Paweł Hajdan, Jr7c7b5592017-05-23 15:06:05 +02002004 # TODO(phajdan.jr): Remove validation options once default (crbug/570091).
Paweł Hajdan, Jr694773d2017-05-29 16:06:23 +02002005 parser.add_option('--validate-syntax', action='store_true', default=True,
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02002006 help='Validate the .gclient and DEPS syntax')
Paweł Hajdan, Jr7c7b5592017-05-23 15:06:05 +02002007 parser.add_option('--disable-syntax-validation', action='store_false',
2008 dest='validate_syntax',
2009 help='Disable validation of .gclient and DEPS syntax.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002010 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002011 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002012
2013 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002014 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002015
smutae7ea312016-07-18 11:59:41 -07002016 if options.revisions and options.head:
2017 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
2018 print('Warning: you cannot use both --head and --revision')
2019
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002020 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002021 client.PrintLocationAndContents()
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002022 ret = client.RunOnDeps('update', args)
2023 if options.output_json:
2024 slns = {}
2025 for d in client.subtree(True):
2026 normed = d.name.replace('\\', '/').rstrip('/') + '/'
2027 slns[normed] = {
2028 'revision': d.got_revision,
2029 'scm': d.used_scm.name if d.used_scm else None,
hinoka@chromium.org17db9052014-05-10 01:11:29 +00002030 'url': str(d.url) if d.url else None,
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002031 }
2032 with open(options.output_json, 'wb') as f:
2033 json.dump({'solutions': slns}, f)
2034 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002035
2036
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002037CMDupdate = CMDsync
2038
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002039
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02002040def CMDvalidate(parser, args):
2041 """Validates the .gclient and DEPS syntax."""
2042 options, args = parser.parse_args(args)
2043 options.validate_syntax = True
2044 client = GClient.LoadCurrentConfig(options)
2045 rv = client.RunOnDeps('validate', args)
2046 if rv == 0:
2047 print('validate: SUCCESS')
2048 else:
2049 print('validate: FAILURE')
2050 return rv
2051
2052
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002053def CMDdiff(parser, args):
2054 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002055 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2056 help='override deps for the specified (comma-separated) '
2057 'platform(s); \'all\' will process all deps_os '
2058 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002059 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002060 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002061 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002062 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002063 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002064 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002065 return client.RunOnDeps('diff', args)
2066
2067
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002068def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002069 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00002070
2071 That's the nuclear option to get back to a 'clean' state. It removes anything
agabled437d762016-10-17 09:35:11 -07002072 that shows up in git status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002073 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2074 help='override deps for the specified (comma-separated) '
2075 'platform(s); \'all\' will process all deps_os '
2076 'references')
2077 parser.add_option('-n', '--nohooks', action='store_true',
2078 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002079 parser.add_option('-p', '--noprehooks', action='store_true',
2080 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002081 parser.add_option('--upstream', action='store_true',
2082 help='Make repo state match upstream branch.')
iannucci@chromium.orgbf525dc2016-04-07 22:00:28 +00002083 parser.add_option('--break_repo_locks', action='store_true',
2084 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2085 'index.lock). This should only be used if you know for '
2086 'certain that this invocation of gclient is the only '
2087 'thing operating on the git repos (e.g. on a bot).')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002088 (options, args) = parser.parse_args(args)
2089 # --force is implied.
2090 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002091 options.reset = False
2092 options.delete_unversioned_trees = False
agablec903d732016-07-26 09:07:24 -07002093 options.merge = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002094 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002095 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002096 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002097 return client.RunOnDeps('revert', args)
2098
2099
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002100def CMDrunhooks(parser, args):
2101 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002102 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2103 help='override deps for the specified (comma-separated) '
2104 'platform(s); \'all\' will process all deps_os '
2105 'references')
2106 parser.add_option('-f', '--force', action='store_true', default=True,
2107 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002108 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002109 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002110 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002111 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002112 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002113 client.PrintLocationAndContents()
maruel@chromium.org5df6a462009-08-28 18:52:26 +00002114 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002115 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002116 return client.RunOnDeps('runhooks', args)
2117
2118
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002119def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002120 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002121
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002122 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002123 can be used to reproduce the same tree in the future. It is only useful for
agabled437d762016-10-17 09:35:11 -07002124 'unpinned dependencies', i.e. DEPS/deps references without a git hash.
2125 A git branch name isn't 'pinned' since the actual commit can change.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002126 """
2127 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2128 help='override deps for the specified (comma-separated) '
2129 'platform(s); \'all\' will process all deps_os '
2130 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002131 parser.add_option('-a', '--actual', action='store_true',
2132 help='gets the actual checked out revisions instead of the '
2133 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002134 parser.add_option('-s', '--snapshot', action='store_true',
2135 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002136 'version of all repositories to reproduce the tree, '
2137 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002138 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002139 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002140 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002141 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002142 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002143 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002144
2145
szager@google.comb9a78d32012-03-13 18:46:21 +00002146def CMDhookinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002147 """Outputs the hooks that would be run by `gclient runhooks`."""
szager@google.comb9a78d32012-03-13 18:46:21 +00002148 (options, args) = parser.parse_args(args)
2149 options.force = True
2150 client = GClient.LoadCurrentConfig(options)
2151 if not client:
2152 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2153 client.RunOnDeps(None, [])
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002154 print('; '.join(' '.join(hook) for hook in client.GetHooks(options)))
szager@google.comb9a78d32012-03-13 18:46:21 +00002155 return 0
2156
2157
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002158def CMDverify(parser, args):
2159 """Verifies the DEPS file deps are only from allowed_hosts."""
2160 (options, args) = parser.parse_args(args)
2161 client = GClient.LoadCurrentConfig(options)
2162 if not client:
2163 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2164 client.RunOnDeps(None, [])
2165 # Look at each first-level dependency of this gclient only.
2166 for dep in client.dependencies:
2167 bad_deps = dep.findDepsFromNotAllowedHosts()
2168 if not bad_deps:
2169 continue
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002170 print("There are deps from not allowed hosts in file %s" % dep.deps_file)
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002171 for bad_dep in bad_deps:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002172 print("\t%s at %s" % (bad_dep.name, bad_dep.url))
2173 print("allowed_hosts:", ', '.join(dep.allowed_hosts))
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002174 sys.stdout.flush()
2175 raise gclient_utils.Error(
2176 'dependencies from disallowed hosts; check your DEPS file.')
2177 return 0
2178
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002179class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00002180 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002181
2182 def __init__(self, **kwargs):
2183 optparse.OptionParser.__init__(
2184 self, version='%prog ' + __version__, **kwargs)
2185
2186 # Some arm boards have issues with parallel sync.
2187 if platform.machine().startswith('arm'):
2188 jobs = 1
2189 else:
2190 jobs = max(8, gclient_utils.NumLocalCpus())
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002191
2192 self.add_option(
2193 '-j', '--jobs', default=jobs, type='int',
2194 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002195 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002196 self.add_option(
2197 '-v', '--verbose', action='count', default=0,
2198 help='Produces additional output for diagnostics. Can be used up to '
2199 'three times for more logging info.')
2200 self.add_option(
2201 '--gclientfile', dest='config_filename',
2202 help='Specify an alternate %s file' % self.gclientfile_default)
2203 self.add_option(
2204 '--spec',
2205 help='create a gclient file containing the provided string. Due to '
2206 'Cygwin/Python brokenness, it can\'t contain any newlines.')
2207 self.add_option(
2208 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00002209 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002210
2211 def parse_args(self, args=None, values=None):
2212 """Integrates standard options processing."""
2213 options, args = optparse.OptionParser.parse_args(self, args, values)
2214 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
2215 logging.basicConfig(
2216 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00002217 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002218 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002219 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00002220 if (options.config_filename and
2221 options.config_filename != os.path.basename(options.config_filename)):
2222 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002223 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002224 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00002225 options.entries_filename = options.config_filename + '_entries'
2226 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002227 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00002228
2229 # These hacks need to die.
2230 if not hasattr(options, 'revisions'):
2231 # GClient.RunOnDeps expects it even if not applicable.
2232 options.revisions = []
smutae7ea312016-07-18 11:59:41 -07002233 if not hasattr(options, 'head'):
2234 options.head = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00002235 if not hasattr(options, 'nohooks'):
2236 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002237 if not hasattr(options, 'noprehooks'):
2238 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00002239 if not hasattr(options, 'deps_os'):
2240 options.deps_os = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00002241 if not hasattr(options, 'force'):
2242 options.force = None
2243 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002244
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002245
2246def disable_buffering():
2247 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
2248 # operations. Python as a strong tendency to buffer sys.stdout.
2249 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
2250 # Make stdout annotated with the thread ids.
2251 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00002252
2253
sbc@chromium.org013731e2015-02-26 18:28:43 +00002254def main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002255 """Doesn't parse the arguments here, just find the right subcommand to
2256 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002257 if sys.hexversion < 0x02060000:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002258 print(
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002259 '\nYour python version %s is unsupported, please upgrade.\n' %
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002260 sys.version.split(' ', 1)[0],
2261 file=sys.stderr)
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002262 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002263 if not sys.executable:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002264 print(
2265 '\nPython cannot find the location of it\'s own executable.\n',
2266 file=sys.stderr)
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002267 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002268 fix_encoding.fix_encoding()
2269 disable_buffering()
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00002270 setup_color.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002271 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002272 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002273 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00002274 except KeyboardInterrupt:
2275 gclient_utils.GClientChildren.KillAllRemainingChildren()
2276 raise
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00002277 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002278 print('Error: %s' % str(e), file=sys.stderr)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002279 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00002280 finally:
2281 gclient_utils.PrintWarnings()
sbc@chromium.org013731e2015-02-26 18:28:43 +00002282 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002283
2284
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00002285if '__main__' == __name__:
sbc@chromium.org013731e2015-02-26 18:28:43 +00002286 try:
2287 sys.exit(main(sys.argv[1:]))
2288 except KeyboardInterrupt:
2289 sys.stderr.write('interrupted\n')
2290 sys.exit(1)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002291
2292# vim: ts=2:sw=2:tw=80:et: