blob: 1654750984f0d6afb0be5b1dcb9b738733dda2b9 [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
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +020083import collections
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
108
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200109class GNException(Exception):
110 pass
111
112
113def ToGNString(value, allow_dicts = True):
114 """Returns a stringified GN equivalent of the Python value.
115
116 allow_dicts indicates if this function will allow converting dictionaries
117 to GN scopes. This is only possible at the top level, you can't nest a
118 GN scope in a list, so this should be set to False for recursive calls."""
119 if isinstance(value, basestring):
120 if value.find('\n') >= 0:
121 raise GNException("Trying to print a string with a newline in it.")
122 return '"' + \
123 value.replace('\\', '\\\\').replace('"', '\\"').replace('$', '\\$') + \
124 '"'
125
126 if isinstance(value, unicode):
127 return ToGNString(value.encode('utf-8'))
128
129 if isinstance(value, bool):
130 if value:
131 return "true"
132 return "false"
133
134 # NOTE: some type handling removed compared to chromium/src copy.
135
136 raise GNException("Unsupported type when printing to GN.")
137
138
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200139class Hook(object):
140 """Descriptor of command ran before/after sync or on demand."""
141
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200142 def __init__(self, action, pattern=None, name=None, cwd=None, condition=None,
143 variables=None):
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200144 """Constructor.
145
146 Arguments:
147 action (list of basestring): argv of the command to run
148 pattern (basestring regex): noop with git; deprecated
149 name (basestring): optional name; no effect on operation
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +0200150 cwd (basestring): working directory to use
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200151 condition (basestring): condition when to run the hook
152 variables (dict): variables for evaluating the condition
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200153 """
154 self._action = gclient_utils.freeze(action)
155 self._pattern = pattern
156 self._name = name
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +0200157 self._cwd = cwd
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200158 self._condition = condition
159 self._variables = variables
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200160
161 @staticmethod
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200162 def from_dict(d, variables=None):
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200163 """Creates a Hook instance from a dict like in the DEPS file."""
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200164 return Hook(
165 d['action'],
166 d.get('pattern'),
167 d.get('name'),
168 d.get('cwd'),
169 d.get('condition'),
170 variables=variables)
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200171
172 @property
173 def action(self):
174 return self._action
175
176 @property
177 def pattern(self):
178 return self._pattern
179
180 @property
181 def name(self):
182 return self._name
183
184 def matches(self, file_list):
185 """Returns true if the pattern matches any of files in the list."""
186 if not self._pattern:
187 return True
188 pattern = re.compile(self._pattern)
189 return bool([f for f in file_list if pattern.search(f)])
190
191 def run(self, root):
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200192 """Executes the hook's command (provided the condition is met)."""
193 if (self._condition and
194 not gclient_eval.EvaluateCondition(self._condition, self._variables)):
195 return
196
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200197 cmd = [arg.format(**self._variables) for arg in self._action]
198
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200199 if cmd[0] == 'python':
200 # If the hook specified "python" as the first item, the action is a
201 # Python script. Run it by starting a new copy of the same
202 # interpreter.
203 cmd[0] = sys.executable
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +0200204
205 cwd = root
206 if self._cwd:
207 cwd = os.path.join(cwd, self._cwd)
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200208 try:
209 start_time = time.time()
210 gclient_utils.CheckCallAndFilterAndHeader(
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +0200211 cmd, cwd=cwd, always=True)
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200212 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
213 # Use a discrete exit status code of 2 to indicate that a hook action
214 # failed. Users of this script may wish to treat hook action failures
215 # differently from VC failures.
216 print('Error: %s' % str(e), file=sys.stderr)
217 sys.exit(2)
218 finally:
219 elapsed_time = time.time() - start_time
220 if elapsed_time > 10:
221 print("Hook '%s' took %.2f secs" % (
222 gclient_utils.CommandToStr(cmd), elapsed_time))
223
224
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200225class DependencySettings(object):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000226 """Immutable configuration settings."""
227 def __init__(
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200228 self, parent, raw_url, url, managed, custom_deps, custom_vars,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200229 custom_hooks, deps_file, should_process, relative,
230 condition, condition_value):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000231 # These are not mutable:
232 self._parent = parent
mmoss@chromium.org8f93f792014-08-26 23:24:09 +0000233 self._deps_file = deps_file
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200234 self._raw_url = raw_url
maruel@chromium.org064186c2011-09-27 23:53:33 +0000235 self._url = url
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200236 # The condition as string (or None). Useful to keep e.g. for flatten.
237 self._condition = condition
238 # Boolean value of the condition. If there's no condition, just True.
239 self._condition_value = condition_value
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000240 # 'managed' determines whether or not this dependency is synced/updated by
241 # gclient after gclient checks it out initially. The difference between
242 # 'managed' and 'should_process' is that the user specifies 'managed' via
smutae7ea312016-07-18 11:59:41 -0700243 # the --unmanaged command-line flag or a .gclient config, where
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000244 # 'should_process' is dynamically set by gclient if it goes over its
245 # recursion limit and controls gclient's behavior so it does not misbehave.
246 self._managed = managed
247 self._should_process = should_process
agabledce6ddc2016-09-08 10:02:16 -0700248 # If this is a recursed-upon sub-dependency, and the parent has
249 # use_relative_paths set, then this dependency should check out its own
250 # dependencies relative to that parent's path for this, rather than
251 # relative to the .gclient file.
252 self._relative = relative
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000253 # This is a mutable value which has the list of 'target_os' OSes listed in
254 # the current deps file.
255 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000256
257 # These are only set in .gclient and not in DEPS files.
258 self._custom_vars = custom_vars or {}
259 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000260 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000261
maruel@chromium.org064186c2011-09-27 23:53:33 +0000262 # Post process the url to remove trailing slashes.
263 if isinstance(self._url, basestring):
264 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
265 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000266 self._url = self._url.replace('/@', '@')
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200267 elif not isinstance(self._url, (None.__class__)):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000268 raise gclient_utils.Error(
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200269 ('dependency url must be either string or None, '
270 'instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000271 # Make any deps_file path platform-appropriate.
272 for sep in ['/', '\\']:
273 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000274
275 @property
276 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000277 return self._deps_file
278
279 @property
280 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000281 return self._managed
282
283 @property
284 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000285 return self._parent
286
287 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000288 def root(self):
289 """Returns the root node, a GClient object."""
290 if not self.parent:
291 # This line is to signal pylint that it could be a GClient instance.
292 return self or GClient(None, None)
293 return self.parent.root
294
295 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000296 def should_process(self):
297 """True if this dependency should be processed, i.e. checked out."""
298 return self._should_process
299
300 @property
301 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000302 return self._custom_vars.copy()
303
304 @property
305 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000306 return self._custom_deps.copy()
307
maruel@chromium.org064186c2011-09-27 23:53:33 +0000308 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000309 def custom_hooks(self):
310 return self._custom_hooks[:]
311
312 @property
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200313 def raw_url(self):
314 """URL before variable expansion."""
315 return self._raw_url
316
317 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000318 def url(self):
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200319 """URL after variable expansion."""
maruel@chromium.org064186c2011-09-27 23:53:33 +0000320 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000321
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000322 @property
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200323 def condition(self):
324 return self._condition
325
326 @property
327 def condition_value(self):
328 return self._condition_value
329
330 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000331 def target_os(self):
332 if self.local_target_os is not None:
333 return tuple(set(self.local_target_os).union(self.parent.target_os))
334 else:
335 return self.parent.target_os
336
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000337 def get_custom_deps(self, name, url):
338 """Returns a custom deps if applicable."""
339 if self.parent:
340 url = self.parent.get_custom_deps(name, url)
341 # None is a valid return value to disable a dependency.
342 return self.custom_deps.get(name, url)
343
maruel@chromium.org064186c2011-09-27 23:53:33 +0000344
345class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000346 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000347
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200348 def __init__(self, parent, name, raw_url, url, managed, custom_deps,
agabledce6ddc2016-09-08 10:02:16 -0700349 custom_vars, custom_hooks, deps_file, should_process,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200350 relative, condition, condition_value):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000351 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000352 DependencySettings.__init__(
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200353 self, parent, raw_url, url, managed, custom_deps, custom_vars,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200354 custom_hooks, deps_file, should_process, relative,
355 condition, condition_value)
maruel@chromium.org68988972011-09-20 14:11:42 +0000356
357 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000358 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000359
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000360 self._pre_deps_hooks = []
361
maruel@chromium.org68988972011-09-20 14:11:42 +0000362 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000363 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000364 self._dependencies = []
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200365 self._vars = {}
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +0200366 self._os_dependencies = {}
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +0200367 self._os_deps_hooks = {}
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200368
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000369 # A cache of the files affected by the current operation, necessary for
370 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000371 self._file_list = []
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000372 # List of host names from which dependencies are allowed.
373 # Default is an empty set, meaning unspecified in DEPS file, and hence all
374 # hosts will be allowed. Non-empty set means whitelist of hosts.
375 # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
376 self._allowed_hosts = frozenset()
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200377 # Spec for .gni output to write (if any).
378 self._gn_args_file = None
379 self._gn_args = []
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000380 # If it is not set to True, the dependency wasn't processed for its child
381 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000382 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000383 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000384 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000385 # This dependency had its pre-DEPS hooks run
386 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000387 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000388 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000389 # This is the scm used to checkout self.url. It may be used by dependencies
390 # to get the datetime of the revision we checked out.
391 self._used_scm = None
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000392 self._used_revision = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000393 # The actual revision we ended up getting, or None if that information is
394 # unavailable
395 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000396
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000397 # This is a mutable value that overrides the normal recursion limit for this
398 # dependency. It is read from the actual DEPS file so cannot be set on
399 # class instantiation.
400 self.recursion_override = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000401 # recursedeps is a mutable value that selectively overrides the default
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000402 # 'no recursion' setting on a dep-by-dep basis. It will replace
403 # recursion_override.
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000404 #
405 # It will be a dictionary of {deps_name: {"deps_file": depfile_name}} or
406 # None.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000407 self.recursedeps = None
hinoka885e5b12016-06-08 14:40:09 -0700408 # This is inherited from WorkItem. We want the URL to be a resource.
409 if url and isinstance(url, basestring):
410 # The url is usually given to gclient either as https://blah@123
qyearsley12fa6ff2016-08-24 09:18:40 -0700411 # or just https://blah. The @123 portion is irrelevant.
hinoka885e5b12016-06-08 14:40:09 -0700412 self.resources.append(url.split('@')[0])
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000413
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000414 if not self.name and self.parent:
415 raise gclient_utils.Error('Dependency without name')
416
maruel@chromium.org470b5432011-10-11 18:18:19 +0000417 @property
418 def requirements(self):
419 """Calculate the list of requirements."""
420 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000421 # self.parent is implicitly a requirement. This will be recursive by
422 # definition.
423 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000424 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000425
426 # For a tree with at least 2 levels*, the leaf node needs to depend
427 # on the level higher up in an orderly way.
428 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
429 # thus unsorted, while the .gclient format is a list thus sorted.
430 #
431 # * _recursion_limit is hard coded 2 and there is no hope to change this
432 # value.
433 #
434 # Interestingly enough, the following condition only works in the case we
435 # want: self is a 2nd level node. 3nd level node wouldn't need this since
436 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000437 if self.parent and self.parent.parent and not self.parent.parent.parent:
438 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000439
maruel@chromium.org470b5432011-10-11 18:18:19 +0000440 if self.name:
441 requirements |= set(
442 obj.name for obj in self.root.subtree(False)
443 if (obj is not self
444 and obj.name and
445 self.name.startswith(posixpath.join(obj.name, ''))))
446 requirements = tuple(sorted(requirements))
447 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
448 return requirements
449
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000450 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000451 def try_recursedeps(self):
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000452 """Returns False if recursion_override is ever specified."""
453 if self.recursion_override is not None:
454 return False
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000455 return self.parent.try_recursedeps
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000456
457 @property
458 def recursion_limit(self):
459 """Returns > 0 if this dependency is not too recursed to be processed."""
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000460 # We continue to support the absence of recursedeps until tools and DEPS
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000461 # using recursion_override are updated.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000462 if self.try_recursedeps and self.parent.recursedeps != None:
463 if self.name in self.parent.recursedeps:
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000464 return 1
465
466 if self.recursion_override is not None:
467 return self.recursion_override
468 return max(self.parent.recursion_limit - 1, 0)
469
maruel@chromium.org470b5432011-10-11 18:18:19 +0000470 def verify_validity(self):
471 """Verifies that this Dependency is fine to add as a child of another one.
472
473 Returns True if this entry should be added, False if it is a duplicate of
474 another entry.
475 """
476 logging.info('Dependency(%s).verify_validity()' % self.name)
477 if self.name in [s.name for s in self.parent.dependencies]:
478 raise gclient_utils.Error(
479 'The same name "%s" appears multiple times in the deps section' %
480 self.name)
481 if not self.should_process:
482 # Return early, no need to set requirements.
483 return True
484
485 # This require a full tree traversal with locks.
486 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
487 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000488 self_url = self.LateOverride(self.url)
489 sibling_url = sibling.LateOverride(sibling.url)
490 # Allow to have only one to be None or ''.
491 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000492 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000493 ('Dependency %s specified more than once:\n'
494 ' %s [%s]\n'
495 'vs\n'
496 ' %s [%s]') % (
497 self.name,
498 sibling.hierarchy(),
499 sibling_url,
500 self.hierarchy(),
501 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000502 # In theory we could keep it as a shadow of the other one. In
503 # practice, simply ignore it.
504 logging.warn('Won\'t process duplicate dependency %s' % sibling)
505 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000506 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000507
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000508 def LateOverride(self, url):
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200509 """Resolves the parsed url from url."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000510 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000511 parsed_url = self.get_custom_deps(self.name, url)
512 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000513 logging.info(
514 'Dependency(%s).LateOverride(%s) -> %s' %
515 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000516 return parsed_url
517
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000518 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000519 parsed_url = urlparse.urlparse(url)
scr@chromium.orgf1eccaf2014-04-11 15:51:33 +0000520 if (not parsed_url[0] and
521 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000522 # A relative url. Fetch the real base.
523 path = parsed_url[2]
524 if not path.startswith('/'):
525 raise gclient_utils.Error(
526 'relative DEPS entry \'%s\' must begin with a slash' % url)
527 # Create a scm just to query the full url.
528 parent_url = self.parent.parsed_url
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000529 scm = gclient_scm.CreateSCM(
530 parent_url, self.root.root_dir, None, self.outbuf)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000531 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000532 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000533 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000534 logging.info(
535 'Dependency(%s).LateOverride(%s) -> %s' %
536 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000537 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000538
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000539 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000540 logging.info(
541 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000542 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000543
544 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000545
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000546 @staticmethod
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +0200547 def MergeWithOsDeps(deps, deps_os, target_os_list, process_all_deps):
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000548 """Returns a new "deps" structure that is the deps sent in updated
549 with information from deps_os (the deps_os section of the DEPS
550 file) that matches the list of target os."""
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000551 new_deps = deps.copy()
Paweł Hajdan, Jrfd0057e2017-06-21 14:20:21 +0200552 for dep_os, os_deps in deps_os.iteritems():
553 for key, value in os_deps.iteritems():
554 if value is None:
555 # Make this condition very visible, so it's not a silent failure.
556 # It's unclear how to support None override in deps_os.
557 logging.error('Ignoring %r:%r in %r deps_os', key, value, dep_os)
558 continue
559
560 # Normalize value to be a dict which contains |should_process| metadata.
561 if isinstance(value, basestring):
562 value = {'url': value}
563 assert isinstance(value, collections.Mapping), (key, value)
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +0200564 value['should_process'] = dep_os in target_os_list or process_all_deps
Paweł Hajdan, Jrfd0057e2017-06-21 14:20:21 +0200565
566 # Handle collisions/overrides.
567 if key in new_deps and new_deps[key] != value:
568 # Normalize the existing new_deps entry.
569 if isinstance(new_deps[key], basestring):
570 new_deps[key] = {'url': new_deps[key]}
571 assert isinstance(new_deps[key],
572 collections.Mapping), (key, new_deps[key])
573
574 # It's OK if the "override" sets the key to the same value.
575 # This is mostly for legacy reasons to keep existing DEPS files
576 # working. Often mac/ios and unix/android will do this.
577 if value['url'] != new_deps[key]['url']:
578 raise gclient_utils.Error(
579 ('Value from deps_os (%r; %r: %r) conflicts with existing deps '
580 'entry (%r).') % (dep_os, key, value, new_deps[key]))
581
582 # We'd otherwise overwrite |should_process| metadata, but a dep should
583 # be processed if _any_ of its references call for that.
584 value['should_process'] = (
585 value['should_process'] or
586 new_deps[key].get('should_process', True))
587
588 new_deps[key] = value
589
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000590 return new_deps
591
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200592 def _postprocess_deps(self, deps, rel_prefix):
593 """Performs post-processing of deps compared to what's in the DEPS file."""
Paweł Hajdan, Jr4426eaf2017-06-13 12:25:47 +0200594 # Make sure the dict is mutable, e.g. in case it's frozen.
595 deps = dict(deps)
596
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200597 # If a line is in custom_deps, but not in the solution, we want to append
598 # this line to the solution.
599 for d in self.custom_deps:
600 if d not in deps:
601 deps[d] = self.custom_deps[d]
602
603 if rel_prefix:
604 logging.warning('use_relative_paths enabled.')
605 rel_deps = {}
606 for d, url in deps.items():
607 # normpath is required to allow DEPS to use .. in their
608 # dependency local path.
609 rel_deps[os.path.normpath(os.path.join(rel_prefix, d))] = url
610 logging.warning('Updating deps by prepending %s.', rel_prefix)
611 deps = rel_deps
612
613 return deps
614
615 def _deps_to_objects(self, deps, use_relative_paths):
616 """Convert a deps dict to a dict of Dependency objects."""
617 deps_to_add = []
618 for name, dep_value in deps.iteritems():
619 should_process = self.recursion_limit and self.should_process
620 deps_file = self.deps_file
621 if self.recursedeps is not None:
622 ent = self.recursedeps.get(name)
623 if ent is not None:
624 deps_file = ent['deps_file']
625 if dep_value is None:
626 continue
627 condition = None
628 condition_value = True
629 if isinstance(dep_value, basestring):
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200630 raw_url = dep_value
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200631 else:
632 # This should be guaranteed by schema checking in gclient_eval.
633 assert isinstance(dep_value, collections.Mapping)
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200634 raw_url = dep_value['url']
Paweł Hajdan, Jrfd0057e2017-06-21 14:20:21 +0200635 # Take into account should_process metadata set by MergeWithOsDeps.
636 should_process = (should_process and
637 dep_value.get('should_process', True))
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200638 condition = dep_value.get('condition')
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200639
640 url = raw_url.format(**self.get_vars())
641
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200642 if condition:
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +0200643 condition_value = gclient_eval.EvaluateCondition(
644 condition, self.get_vars())
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +0200645 if not self._get_option('process_all_deps', False):
646 should_process = should_process and condition_value
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200647 deps_to_add.append(Dependency(
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200648 self, name, raw_url, url, None, None, self.custom_vars, None,
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200649 deps_file, should_process, use_relative_paths, condition,
650 condition_value))
651 deps_to_add.sort(key=lambda x: x.name)
652 return deps_to_add
653
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000654 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000655 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000656 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000657 assert not self.dependencies
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000658
659 deps_content = None
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000660
661 # First try to locate the configured deps file. If it's missing, fallback
662 # to DEPS.
663 deps_files = [self.deps_file]
664 if 'DEPS' not in deps_files:
665 deps_files.append('DEPS')
666 for deps_file in deps_files:
667 filepath = os.path.join(self.root.root_dir, self.name, deps_file)
668 if os.path.isfile(filepath):
669 logging.info(
670 'ParseDepsFile(%s): %s file found at %s', self.name, deps_file,
671 filepath)
672 break
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000673 logging.info(
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000674 'ParseDepsFile(%s): No %s file found at %s', self.name, deps_file,
675 filepath)
676
677 if os.path.isfile(filepath):
maruel@chromium.org46304292010-10-28 11:42:00 +0000678 deps_content = gclient_utils.FileRead(filepath)
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000679 logging.debug('ParseDepsFile(%s) read:\n%s', self.name, deps_content)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000680
681 local_scope = {}
682 if deps_content:
Paweł Hajdan, Jrf1587bf2017-06-20 21:19:07 +0200683 global_scope = {
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200684 'Var': lambda var_name: '{%s}' % var_name,
Paweł Hajdan, Jrf1587bf2017-06-20 21:19:07 +0200685 'deps_os': {},
686 }
maruel@chromium.org46304292010-10-28 11:42:00 +0000687 # Eval the content.
688 try:
Paweł Hajdan, Jrc485d5a2017-06-02 12:08:09 +0200689 if self._get_option('validate_syntax', False):
690 gclient_eval.Exec(deps_content, global_scope, local_scope, filepath)
691 else:
692 exec(deps_content, global_scope, local_scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000693 except SyntaxError as e:
maruel@chromium.org46304292010-10-28 11:42:00 +0000694 gclient_utils.SyntaxErrorToError(filepath, e)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000695
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000696 if 'allowed_hosts' in local_scope:
697 try:
698 self._allowed_hosts = frozenset(local_scope.get('allowed_hosts'))
699 except TypeError: # raised if non-iterable
700 pass
701 if not self._allowed_hosts:
702 logging.warning("allowed_hosts is specified but empty %s",
703 self._allowed_hosts)
704 raise gclient_utils.Error(
705 'ParseDepsFile(%s): allowed_hosts must be absent '
706 'or a non-empty iterable' % self.name)
707
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200708 self._gn_args_file = local_scope.get('gclient_gn_args_file')
709 self._gn_args = local_scope.get('gclient_gn_args', [])
710
Paweł Hajdan, Jr1407d002017-08-01 20:01:01 +0200711 self._vars = local_scope.get('vars', {})
712 if self.parent:
713 for key, value in self.parent.get_vars().iteritems():
714 if key in self._vars:
715 self._vars[key] = value
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200716 # Since we heavily post-process things, freeze ones which should
717 # reflect original state of DEPS.
Paweł Hajdan, Jr1407d002017-08-01 20:01:01 +0200718 self._vars = gclient_utils.freeze(self._vars)
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200719
720 # If use_relative_paths is set in the DEPS file, regenerate
721 # the dictionary using paths relative to the directory containing
722 # the DEPS file. Also update recursedeps if use_relative_paths is
723 # enabled.
724 # If the deps file doesn't set use_relative_paths, but the parent did
725 # (and therefore set self.relative on this Dependency object), then we
726 # want to modify the deps and recursedeps by prepending the parent
727 # directory of this dependency.
728 use_relative_paths = local_scope.get('use_relative_paths', False)
729 rel_prefix = None
730 if use_relative_paths:
731 rel_prefix = self.name
732 elif self._relative:
733 rel_prefix = os.path.dirname(self.name)
734
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200735 deps = {}
736 for key, value in local_scope.get('deps', {}).iteritems():
737 deps[key.format(**self.get_vars())] = value
738
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200739 if 'recursion' in local_scope:
740 self.recursion_override = local_scope.get('recursion')
741 logging.warning(
742 'Setting %s recursion to %d.', self.name, self.recursion_limit)
743 self.recursedeps = None
744 if 'recursedeps' in local_scope:
745 self.recursedeps = {}
746 for ent in local_scope['recursedeps']:
747 if isinstance(ent, basestring):
748 self.recursedeps[ent] = {"deps_file": self.deps_file}
749 else: # (depname, depsfilename)
750 self.recursedeps[ent[0]] = {"deps_file": ent[1]}
751 logging.warning('Found recursedeps %r.', repr(self.recursedeps))
752
753 if rel_prefix:
754 logging.warning('Updating recursedeps by prepending %s.', rel_prefix)
755 rel_deps = {}
756 for depname, options in self.recursedeps.iteritems():
757 rel_deps[
758 os.path.normpath(os.path.join(rel_prefix, depname))] = options
759 self.recursedeps = rel_deps
760
761 # If present, save 'target_os' in the local_target_os property.
762 if 'target_os' in local_scope:
763 self.local_target_os = local_scope['target_os']
764 # load os specific dependencies if defined. these dependencies may
765 # override or extend the values defined by the 'deps' member.
766 target_os_list = self.target_os
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +0200767 if 'deps_os' in local_scope:
768 for dep_os, os_deps in local_scope['deps_os'].iteritems():
769 self._os_dependencies[dep_os] = self._deps_to_objects(
770 self._postprocess_deps(os_deps, rel_prefix), use_relative_paths)
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200771 if target_os_list and not self._get_option(
772 'do_not_merge_os_specific_entries', False):
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +0200773 deps = self.MergeWithOsDeps(
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +0200774 deps, local_scope['deps_os'], target_os_list,
775 self._get_option('process_all_deps', False))
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200776
777 deps_to_add = self._deps_to_objects(
778 self._postprocess_deps(deps, rel_prefix), use_relative_paths)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000779
780 # override named sets of hooks by the custom hooks
781 hooks_to_run = []
782 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
783 for hook in local_scope.get('hooks', []):
784 if hook.get('name', '') not in hook_names_to_suppress:
785 hooks_to_run.append(hook)
Scott Grahamc4826742017-05-11 16:59:23 -0700786 if 'hooks_os' in local_scope and target_os_list:
787 hooks_os = local_scope['hooks_os']
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +0200788
789 # Keep original contents of hooks_os for flatten.
790 for hook_os, os_hooks in hooks_os.iteritems():
791 self._os_deps_hooks[hook_os] = [
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +0200792 Hook.from_dict(hook, variables=self.get_vars())
793 for hook in os_hooks]
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +0200794
Scott Grahamc4826742017-05-11 16:59:23 -0700795 # Specifically append these to ensure that hooks_os run after hooks.
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200796 if not self._get_option('do_not_merge_os_specific_entries', False):
797 for the_target_os in target_os_list:
798 the_target_os_hooks = hooks_os.get(the_target_os, [])
799 hooks_to_run.extend(the_target_os_hooks)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000800
801 # add the replacements and any additions
802 for hook in self.custom_hooks:
803 if 'action' in hook:
804 hooks_to_run.append(hook)
805
Dirk Prankeda3a29e2017-02-27 15:29:36 -0800806 if self.recursion_limit:
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200807 self._pre_deps_hooks = [
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +0200808 Hook.from_dict(hook, variables=self.get_vars()) for hook in
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200809 local_scope.get('pre_deps_hooks', [])]
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000810
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200811 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000812 logging.info('ParseDepsFile(%s) done' % self.name)
813
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +0200814 def _get_option(self, attr, default):
815 obj = self
816 while not hasattr(obj, '_options'):
817 obj = obj.parent
818 return getattr(obj._options, attr, default)
819
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200820 def add_dependencies_and_close(self, deps_to_add, hooks):
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000821 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000822 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000823 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000824 self.add_dependency(dep)
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200825 self._mark_as_parsed(
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200826 [Hook.from_dict(h, variables=self.get_vars()) for h in hooks])
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000827
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000828 def findDepsFromNotAllowedHosts(self):
829 """Returns a list of depenecies from not allowed hosts.
830
831 If allowed_hosts is not set, allows all hosts and returns empty list.
832 """
833 if not self._allowed_hosts:
834 return []
835 bad_deps = []
836 for dep in self._dependencies:
szager@chromium.orgbd772dd2014-11-05 18:43:08 +0000837 # Don't enforce this for custom_deps.
838 if dep.name in self._custom_deps:
839 continue
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000840 if isinstance(dep.url, basestring):
841 parsed_url = urlparse.urlparse(dep.url)
842 if parsed_url.netloc and parsed_url.netloc not in self._allowed_hosts:
843 bad_deps.append(dep)
844 return bad_deps
845
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000846 # Arguments number differs from overridden method
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800847 # pylint: disable=arguments-differ
maruel@chromium.org3742c842010-09-09 19:27:14 +0000848 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000849 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000850 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000851 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000852 if not self.should_process:
853 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000854 # When running runhooks, there's no need to consult the SCM.
855 # All known hooks are expected to run unconditionally regardless of working
856 # copy state, so skip the SCM status check.
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +0200857 run_scm = command not in (
858 'flatten', 'runhooks', 'recurse', 'validate', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000859 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000860 file_list = [] if not options.nohooks else None
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000861 revision_override = revision_overrides.pop(self.name, None)
Dave Tubbda9712017-06-01 15:10:53 -0700862 if not revision_override and parsed_url:
863 revision_override = revision_overrides.get(parsed_url.split('@')[0], None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000864 if run_scm and parsed_url:
agabled437d762016-10-17 09:35:11 -0700865 # Create a shallow copy to mutate revision.
866 options = copy.copy(options)
867 options.revision = revision_override
868 self._used_revision = options.revision
869 self._used_scm = gclient_scm.CreateSCM(
870 parsed_url, self.root.root_dir, self.name, self.outbuf,
871 out_cb=work_queue.out_cb)
872 self._got_revision = self._used_scm.RunCommand(command, options, args,
873 file_list)
874 if file_list:
875 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000876
877 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
878 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000879 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000880 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000881 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000882 continue
883 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000884 [self.root.root_dir.lower(), file_list[i].lower()])
885 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000886 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000887 while file_list[i].startswith(('\\', '/')):
888 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000889
890 # Always parse the DEPS file.
891 self.ParseDepsFile()
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200892 if self._gn_args_file and command == 'update':
893 self.WriteGNArgsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000894 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000895 if command in ('update', 'revert') and not options.noprehooks:
896 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000897
898 if self.recursion_limit:
899 # Parse the dependencies of this dependency.
900 for s in self.dependencies:
Paweł Hajdan, Jr4baaa112017-07-04 19:09:32 +0200901 if s.should_process:
902 work_queue.enqueue(s)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000903
904 if command == 'recurse':
agabled437d762016-10-17 09:35:11 -0700905 # Skip file only checkout.
906 scm = gclient_scm.GetScmName(parsed_url)
907 if not options.scm or scm in options.scm:
908 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
909 # Pass in the SCM type as an env variable. Make sure we don't put
910 # unicode strings in the environment.
911 env = os.environ.copy()
912 if scm:
913 env['GCLIENT_SCM'] = str(scm)
914 if parsed_url:
915 env['GCLIENT_URL'] = str(parsed_url)
916 env['GCLIENT_DEP_PATH'] = str(self.name)
917 if options.prepend_dir and scm == 'git':
918 print_stdout = False
919 def filter_fn(line):
920 """Git-specific path marshaling. It is optimized for git-grep."""
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000921
agabled437d762016-10-17 09:35:11 -0700922 def mod_path(git_pathspec):
923 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
924 modified_path = os.path.join(self.name, match.group(2))
925 branch = match.group(1) or ''
926 return '%s%s' % (branch, modified_path)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000927
agabled437d762016-10-17 09:35:11 -0700928 match = re.match('^Binary file ([^\0]+) matches$', line)
929 if match:
930 print('Binary file %s matches\n' % mod_path(match.group(1)))
931 return
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000932
agabled437d762016-10-17 09:35:11 -0700933 items = line.split('\0')
934 if len(items) == 2 and items[1]:
935 print('%s : %s' % (mod_path(items[0]), items[1]))
936 elif len(items) >= 2:
937 # Multiple null bytes or a single trailing null byte indicate
938 # git is likely displaying filenames only (such as with -l)
939 print('\n'.join(mod_path(path) for path in items if path))
940 else:
941 print(line)
942 else:
943 print_stdout = True
944 filter_fn = None
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000945
agabled437d762016-10-17 09:35:11 -0700946 if parsed_url is None:
947 print('Skipped omitted dependency %s' % cwd, file=sys.stderr)
948 elif os.path.isdir(cwd):
949 try:
950 gclient_utils.CheckCallAndFilter(
951 args, cwd=cwd, env=env, print_stdout=print_stdout,
952 filter_fn=filter_fn,
953 )
954 except subprocess2.CalledProcessError:
955 if not options.ignore:
956 raise
957 else:
958 print('Skipped missing %s' % cwd, file=sys.stderr)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000959
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200960 def WriteGNArgsFile(self):
961 lines = ['# Generated from %r' % self.deps_file]
Paweł Hajdan, Jrb495bf52017-09-25 19:33:50 +0200962 variables = self.get_vars()
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200963 for arg in self._gn_args:
Paweł Hajdan, Jrb495bf52017-09-25 19:33:50 +0200964 value = gclient_eval.EvaluateCondition(variables[arg], variables)
965 lines.append('%s = %s' % (arg, ToGNString(value)))
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200966 with open(os.path.join(self.root.root_dir, self._gn_args_file), 'w') as f:
967 f.write('\n'.join(lines))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000968
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000969 @gclient_utils.lockedmethod
970 def _run_is_done(self, file_list, parsed_url):
971 # Both these are kept for hooks that are run as a separate tree traversal.
972 self._file_list = file_list
973 self._parsed_url = parsed_url
974 self._processed = True
975
szager@google.comb9a78d32012-03-13 18:46:21 +0000976 def GetHooks(self, options):
977 """Evaluates all hooks, and return them in a flat list.
978
979 RunOnDeps() must have been called before to load the DEPS.
980 """
981 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000982 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000983 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000984 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000985 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000986 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000987 if self.deps_hooks:
agabled437d762016-10-17 09:35:11 -0700988 # TODO(maruel): If the user is using git, then we don't know
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000989 # what files have changed so we always run all hooks. It'd be nice to fix
990 # that.
991 if (options.force or
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000992 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000993 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200994 result.extend(self.deps_hooks)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000995 else:
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200996 for hook in self.deps_hooks:
997 if hook.matches(self.file_list_and_children):
998 result.append(hook)
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000999 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +00001000 result.extend(s.GetHooks(options))
1001 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001002
szager@google.comb9a78d32012-03-13 18:46:21 +00001003 def RunHooksRecursively(self, options):
1004 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +00001005 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +00001006 for hook in self.GetHooks(options):
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02001007 hook.run(self.root.root_dir)
maruel@chromium.orgeaf61062010-07-07 18:42:39 +00001008
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001009 def RunPreDepsHooks(self):
1010 assert self.processed
1011 assert self.deps_parsed
1012 assert not self.pre_deps_hooks_ran
1013 assert not self.hooks_ran
1014 for s in self.dependencies:
1015 assert not s.processed
1016 self._pre_deps_hooks_ran = True
1017 for hook in self.pre_deps_hooks:
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02001018 hook.run(self.root.root_dir)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001019
maruel@chromium.org0d812442010-08-10 12:41:08 +00001020 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001021 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001022 dependencies = self.dependencies
1023 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001024 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001025 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001026 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001027 for i in d.subtree(include_all):
1028 yield i
1029
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001030 @gclient_utils.lockedmethod
1031 def add_dependency(self, new_dep):
1032 self._dependencies.append(new_dep)
1033
1034 @gclient_utils.lockedmethod
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +02001035 def _mark_as_parsed(self, new_hooks):
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001036 self._deps_hooks.extend(new_hooks)
1037 self._deps_parsed = True
1038
maruel@chromium.org68988972011-09-20 14:11:42 +00001039 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001040 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +00001041 def dependencies(self):
1042 return tuple(self._dependencies)
1043
1044 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001045 @gclient_utils.lockedmethod
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +02001046 def os_dependencies(self):
1047 return dict(self._os_dependencies)
1048
1049 @property
1050 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001051 def deps_hooks(self):
1052 return tuple(self._deps_hooks)
1053
1054 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001055 @gclient_utils.lockedmethod
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02001056 def os_deps_hooks(self):
1057 return dict(self._os_deps_hooks)
1058
1059 @property
1060 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001061 def pre_deps_hooks(self):
1062 return tuple(self._pre_deps_hooks)
1063
1064 @property
1065 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001066 def parsed_url(self):
1067 return self._parsed_url
1068
1069 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001070 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001071 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +00001072 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +00001073 return self._deps_parsed
1074
1075 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001076 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001077 def processed(self):
1078 return self._processed
1079
1080 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001081 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001082 def pre_deps_hooks_ran(self):
1083 return self._pre_deps_hooks_ran
1084
1085 @property
1086 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001087 def hooks_ran(self):
1088 return self._hooks_ran
1089
1090 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001091 @gclient_utils.lockedmethod
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001092 def allowed_hosts(self):
1093 return self._allowed_hosts
1094
1095 @property
1096 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001097 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001098 return tuple(self._file_list)
1099
1100 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +00001101 def used_scm(self):
1102 """SCMWrapper instance for this dependency or None if not processed yet."""
1103 return self._used_scm
1104
1105 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001106 @gclient_utils.lockedmethod
1107 def got_revision(self):
1108 return self._got_revision
1109
1110 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001111 def file_list_and_children(self):
1112 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001113 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001114 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +00001115 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001116
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001117 def __str__(self):
1118 out = []
agablea98a6cd2016-11-15 14:30:10 -08001119 for i in ('name', 'url', 'parsed_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001120 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001121 'processed', 'hooks_ran', 'deps_parsed', 'requirements',
1122 'allowed_hosts'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001123 # First try the native property if it exists.
1124 if hasattr(self, '_' + i):
1125 value = getattr(self, '_' + i, False)
1126 else:
1127 value = getattr(self, i, False)
1128 if value:
1129 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001130
1131 for d in self.dependencies:
1132 out.extend([' ' + x for x in str(d).splitlines()])
1133 out.append('')
1134 return '\n'.join(out)
1135
1136 def __repr__(self):
1137 return '%s: %s' % (self.name, self.url)
1138
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001139 def hierarchy(self, include_url=True):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +00001140 """Returns a human-readable hierarchical reference to a Dependency."""
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001141 def format_name(d):
1142 if include_url:
1143 return '%s(%s)' % (d.name, d.url)
1144 return d.name
1145 out = format_name(self)
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001146 i = self.parent
1147 while i and i.name:
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001148 out = '%s -> %s' % (format_name(i), out)
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001149 i = i.parent
1150 return out
1151
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +02001152 def get_vars(self):
1153 """Returns a dictionary of effective variable values
1154 (DEPS file contents with applied custom_vars overrides)."""
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001155 # Provide some built-in variables.
1156 result = {
Paweł Hajdan, Jrdf64ee12017-09-26 09:32:14 +02001157 'checkout_android': repr('android' in self.target_os),
1158 'checkout_fuchsia': repr('fuchsia' in self.target_os),
1159 'checkout_ios': repr('ios' in self.target_os),
1160 'checkout_linux': repr('unix' in self.target_os),
1161 'checkout_mac': repr('mac' in self.target_os),
1162 'checkout_win': repr('win' in self.target_os),
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001163 'host_os': repr(_detect_host_os()),
1164 }
1165 # Variables defined in DEPS file override built-in ones.
1166 result.update(self._vars)
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +02001167 result.update(self.custom_vars or {})
1168 return result
1169
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001170
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001171_PLATFORM_MAPPING = {
1172 'cygwin': 'win',
1173 'darwin': 'mac',
1174 'linux2': 'linux',
1175 'win32': 'win',
Jaideep Bajwad05f3582017-09-11 12:31:48 -04001176 'aix6': 'aix',
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001177}
1178
1179
1180def _detect_host_os():
1181 return _PLATFORM_MAPPING[sys.platform]
1182
1183
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001184class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001185 """Object that represent a gclient checkout. A tree of Dependency(), one per
1186 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001187
1188 DEPS_OS_CHOICES = {
Jaideep Bajwad05f3582017-09-11 12:31:48 -04001189 "aix6": "unix",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001190 "win32": "win",
1191 "win": "win",
1192 "cygwin": "win",
1193 "darwin": "mac",
1194 "mac": "mac",
1195 "unix": "unix",
1196 "linux": "unix",
1197 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +00001198 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +00001199 "android": "android",
Michael Mossc54fa812017-08-17 11:27:58 -07001200 "ios": "ios",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001201 }
1202
1203 DEFAULT_CLIENT_FILE_TEXT = ("""\
1204solutions = [
smutae7ea312016-07-18 11:59:41 -07001205 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001206 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001207 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001208 "managed" : %(managed)s,
smutae7ea312016-07-18 11:59:41 -07001209 "custom_deps" : {
1210 },
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001211 },
1212]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001213cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001214""")
1215
1216 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
smutae7ea312016-07-18 11:59:41 -07001217 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001218 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001219 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001220 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001221 "custom_deps" : {
smutae7ea312016-07-18 11:59:41 -07001222%(solution_deps)s },
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001223 },
1224""")
1225
1226 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1227# Snapshot generated with gclient revinfo --snapshot
1228solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001229%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001230""")
1231
1232 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001233 # Do not change previous behavior. Only solution level and immediate DEPS
1234 # are processed.
1235 self._recursion_limit = 2
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +02001236 Dependency.__init__(self, None, None, None, None, True, None, None, None,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001237 'unused', True, None, None, True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001238 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001239 if options.deps_os:
1240 enforced_os = options.deps_os.split(',')
1241 else:
1242 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1243 if 'all' in enforced_os:
1244 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001245 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001246 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001247 self.config_content = None
1248
borenet@google.com88d10082014-03-21 17:24:48 +00001249 def _CheckConfig(self):
1250 """Verify that the config matches the state of the existing checked-out
1251 solutions."""
1252 for dep in self.dependencies:
1253 if dep.managed and dep.url:
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001254 scm = gclient_scm.CreateSCM(
1255 dep.url, self.root_dir, dep.name, self.outbuf)
smut@google.comd33eab32014-07-07 19:35:18 +00001256 actual_url = scm.GetActualRemoteURL(self._options)
borenet@google.com4e9be262014-04-08 19:40:30 +00001257 if actual_url and not scm.DoesRemoteURLMatch(self._options):
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001258 mirror = scm.GetCacheMirror()
1259 if mirror:
1260 mirror_string = '%s (exists=%s)' % (mirror.mirror_path,
1261 mirror.exists())
1262 else:
1263 mirror_string = 'not used'
borenet@google.com0a427372014-04-02 19:12:13 +00001264 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001265Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001266is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001267
borenet@google.com97882362014-04-07 20:06:02 +00001268The .gclient file contains:
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001269URL: %(expected_url)s (%(expected_scm)s)
1270Cache mirror: %(mirror_string)s
borenet@google.com97882362014-04-07 20:06:02 +00001271
1272The local checkout in %(checkout_path)s reports:
1273%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001274
1275You should ensure that the URL listed in .gclient is correct and either change
agabled437d762016-10-17 09:35:11 -07001276it or fix the checkout.
borenet@google.com88d10082014-03-21 17:24:48 +00001277''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1278 'expected_url': dep.url,
1279 'expected_scm': gclient_scm.GetScmName(dep.url),
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001280 'mirror_string' : mirror_string,
borenet@google.com88d10082014-03-21 17:24:48 +00001281 'actual_url': actual_url,
1282 'actual_scm': gclient_scm.GetScmName(actual_url)})
1283
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001284 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001285 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001286 config_dict = {}
1287 self.config_content = content
1288 try:
1289 exec(content, config_dict)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001290 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001291 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001292
peter@chromium.org1efccc82012-04-27 16:34:38 +00001293 # Append any target OS that is not already being enforced to the tuple.
1294 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001295 if config_dict.get('target_os_only', False):
1296 self._enforced_os = tuple(set(target_os))
1297 else:
1298 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1299
Aleksandr Derbenev9e8fb0e2017-08-01 20:18:31 +03001300 cache_dir = config_dict.get('cache_dir', self._options.cache_dir)
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001301 if cache_dir:
1302 cache_dir = os.path.join(self.root_dir, cache_dir)
1303 cache_dir = os.path.abspath(cache_dir)
szager@chromium.orgcaf5bef2014-08-24 18:56:32 +00001304 # If running on a bot, force break any stale git cache locks.
dnj@chromium.orgb682b3e2014-08-25 19:17:12 +00001305 if os.path.exists(cache_dir) and os.environ.get('CHROME_HEADLESS'):
szager@chromium.org4848fb62014-08-24 19:16:31 +00001306 subprocess2.check_call(['git', 'cache', 'unlock', '--cache-dir',
1307 cache_dir, '--force', '--all'])
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001308 gclient_scm.GitWrapper.cache_dir = cache_dir
1309 git_cache.Mirror.SetCachePath(cache_dir)
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001310
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001311 if not target_os and config_dict.get('target_os_only', False):
1312 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1313 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001314
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001315 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001316 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001317 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001318 deps_to_add.append(Dependency(
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +02001319 self, s['name'], s['url'], s['url'],
smutae7ea312016-07-18 11:59:41 -07001320 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001321 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001322 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001323 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001324 s.get('deps_file', 'DEPS'),
agabledce6ddc2016-09-08 10:02:16 -07001325 True,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001326 None,
1327 None,
1328 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001329 except KeyError:
1330 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1331 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001332 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1333 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001334
1335 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001336 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001337 self._options.config_filename),
1338 self.config_content)
1339
1340 @staticmethod
1341 def LoadCurrentConfig(options):
1342 """Searches for and loads a .gclient file relative to the current working
1343 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001344 if options.spec:
1345 client = GClient('.', options)
1346 client.SetConfig(options.spec)
1347 else:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001348 if options.verbose:
1349 print('Looking for %s starting from %s\n' % (
1350 options.config_filename, os.getcwd()))
szager@chromium.orge2e03202012-07-31 18:05:16 +00001351 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1352 if not path:
1353 return None
1354 client = GClient(path, options)
1355 client.SetConfig(gclient_utils.FileRead(
1356 os.path.join(path, options.config_filename)))
maruel@chromium.org69392e72011-10-13 22:09:00 +00001357
1358 if (options.revisions and
1359 len(client.dependencies) > 1 and
1360 any('@' not in r for r in options.revisions)):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001361 print(
1362 ('You must specify the full solution name like --revision %s@%s\n'
1363 'when you have multiple solutions setup in your .gclient file.\n'
1364 'Other solutions present are: %s.') % (
maruel@chromium.org69392e72011-10-13 22:09:00 +00001365 client.dependencies[0].name,
1366 options.revisions[0],
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001367 ', '.join(s.name for s in client.dependencies[1:])),
1368 file=sys.stderr)
maruel@chromium.org15804092010-09-02 17:07:37 +00001369 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001370
nsylvain@google.comefc80932011-05-31 21:27:56 +00001371 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
agablea98a6cd2016-11-15 14:30:10 -08001372 managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001373 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1374 'solution_name': solution_name,
1375 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001376 'deps_file': deps_file,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001377 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001378 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001379 })
1380
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001381 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001382 """Creates a .gclient_entries file to record the list of unique checkouts.
1383
1384 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001385 """
1386 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1387 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001388 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001389 for entry in self.root.subtree(False):
agabled437d762016-10-17 09:35:11 -07001390 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1391 pprint.pformat(entry.parsed_url))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001392 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001393 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001394 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001395 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001396
1397 def _ReadEntries(self):
1398 """Read the .gclient_entries file for the given client.
1399
1400 Returns:
1401 A sequence of solution names, which will be empty if there is the
1402 entries file hasn't been created yet.
1403 """
1404 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001405 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001406 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001407 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001408 try:
1409 exec(gclient_utils.FileRead(filename), scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001410 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001411 gclient_utils.SyntaxErrorToError(filename, e)
Aaron Gable3721ee92017-04-03 14:53:14 -07001412 return scope.get('entries', {})
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001413
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001414 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001415 """Checks for revision overrides."""
1416 revision_overrides = {}
smutae7ea312016-07-18 11:59:41 -07001417 if self._options.head:
1418 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001419 if not self._options.revisions:
1420 for s in self.dependencies:
smutae7ea312016-07-18 11:59:41 -07001421 if not s.managed:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001422 self._options.revisions.append('%s@unmanaged' % s.name)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001423 if not self._options.revisions:
1424 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001425 solutions_names = [s.name for s in self.dependencies]
smutae7ea312016-07-18 11:59:41 -07001426 index = 0
1427 for revision in self._options.revisions:
1428 if not '@' in revision:
maruel@chromium.org307d1792010-05-31 20:03:13 +00001429 # Support for --revision 123
smutae7ea312016-07-18 11:59:41 -07001430 revision = '%s@%s' % (solutions_names[index], revision)
1431 name, rev = revision.split('@', 1)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001432 revision_overrides[name] = rev
smutae7ea312016-07-18 11:59:41 -07001433 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001434 return revision_overrides
1435
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001436 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001437 """Runs a command on each dependency in a client and its dependencies.
1438
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001439 Args:
1440 command: The command to use (e.g., 'status' or 'diff')
1441 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001442 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001443 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001444 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001445
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001446 revision_overrides = {}
1447 # It's unnecessary to check for revision overrides for 'recurse'.
1448 # Save a few seconds by not calling _EnforceRevisions() in that case.
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02001449 if command not in ('diff', 'recurse', 'runhooks', 'status', 'revert',
1450 'validate'):
szager@chromium.org5273b8a2014-08-21 15:10:10 +00001451 self._CheckConfig()
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001452 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001453 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001454 # Disable progress for non-tty stdout.
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00001455 if (setup_color.IS_TTY and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001456 if command in ('update', 'revert'):
1457 pm = Progress('Syncing projects', 1)
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02001458 elif command in ('recurse', 'validate'):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001459 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001460 work_queue = gclient_utils.ExecutionQueue(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001461 self._options.jobs, pm, ignore_requirements=ignore_requirements,
1462 verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001463 for s in self.dependencies:
Paweł Hajdan, Jr4baaa112017-07-04 19:09:32 +02001464 if s.should_process:
1465 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001466 work_queue.flush(revision_overrides, command, args, options=self._options)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001467 if revision_overrides:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001468 print('Please fix your script, having invalid --revision flags will soon '
1469 'considered an error.', file=sys.stderr)
piman@chromium.org6f363722010-04-27 00:41:09 +00001470
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001471 # Once all the dependencies have been processed, it's now safe to run the
1472 # hooks.
1473 if not self._options.nohooks:
1474 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001475
1476 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001477 # Notify the user if there is an orphaned entry in their working copy.
1478 # Only delete the directory if there are no changes in it, and
1479 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001480 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001481 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1482 for e in entries]
1483
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001484 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001485 if not prev_url:
1486 # entry must have been overridden via .gclient custom_deps
1487 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001488 # Fix path separator on Windows.
1489 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001490 e_dir = os.path.join(self.root_dir, entry_fixed)
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001491 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001492 if (entry not in entries and
1493 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001494 os.path.exists(e_dir)):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001495 # The entry has been removed from DEPS.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001496 scm = gclient_scm.CreateSCM(
1497 prev_url, self.root_dir, entry_fixed, self.outbuf)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001498
1499 # Check to see if this directory is now part of a higher-up checkout.
borenet@google.com359bb642014-05-13 17:28:19 +00001500 scm_root = None
agabled437d762016-10-17 09:35:11 -07001501 try:
1502 scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(scm.checkout_path)
1503 except subprocess2.CalledProcessError:
1504 pass
1505 if not scm_root:
borenet@google.com359bb642014-05-13 17:28:19 +00001506 logging.warning('Could not find checkout root for %s. Unable to '
1507 'determine whether it is part of a higher-level '
1508 'checkout, so not removing.' % entry)
1509 continue
primiano@chromium.org1c127382015-02-17 11:15:40 +00001510
1511 # This is to handle the case of third_party/WebKit migrating from
1512 # being a DEPS entry to being part of the main project.
1513 # If the subproject is a Git project, we need to remove its .git
1514 # folder. Otherwise git operations on that folder will have different
1515 # effects depending on the current working directory.
agabled437d762016-10-17 09:35:11 -07001516 if os.path.abspath(scm_root) == os.path.abspath(e_dir):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001517 e_par_dir = os.path.join(e_dir, os.pardir)
agabled437d762016-10-17 09:35:11 -07001518 if gclient_scm.scm.GIT.IsInsideWorkTree(e_par_dir):
1519 par_scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(e_par_dir)
primiano@chromium.org1c127382015-02-17 11:15:40 +00001520 # rel_e_dir : relative path of entry w.r.t. its parent repo.
1521 rel_e_dir = os.path.relpath(e_dir, par_scm_root)
agabled437d762016-10-17 09:35:11 -07001522 if gclient_scm.scm.GIT.IsDirectoryVersioned(
1523 par_scm_root, rel_e_dir):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001524 save_dir = scm.GetGitBackupDirPath()
1525 # Remove any eventual stale backup dir for the same project.
1526 if os.path.exists(save_dir):
1527 gclient_utils.rmtree(save_dir)
1528 os.rename(os.path.join(e_dir, '.git'), save_dir)
1529 # When switching between the two states (entry/ is a subproject
1530 # -> entry/ is part of the outer project), it is very likely
1531 # that some files are changed in the checkout, unless we are
1532 # jumping *exactly* across the commit which changed just DEPS.
1533 # In such case we want to cleanup any eventual stale files
1534 # (coming from the old subproject) in order to end up with a
1535 # clean checkout.
agabled437d762016-10-17 09:35:11 -07001536 gclient_scm.scm.GIT.CleanupDir(par_scm_root, rel_e_dir)
primiano@chromium.org1c127382015-02-17 11:15:40 +00001537 assert not os.path.exists(os.path.join(e_dir, '.git'))
1538 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher '
1539 'level checkout. The git folder containing all the local'
1540 ' branches has been saved to %s.\n'
1541 'If you don\'t care about its state you can safely '
1542 'remove that folder to free up space.') %
1543 (entry, save_dir))
1544 continue
1545
borenet@google.com359bb642014-05-13 17:28:19 +00001546 if scm_root in full_entries:
primiano@chromium.org1c127382015-02-17 11:15:40 +00001547 logging.info('%s is part of a higher level checkout, not removing',
1548 scm.GetCheckoutRoot())
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001549 continue
1550
1551 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001552 scm.status(self._options, [], file_list)
1553 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001554 if (not self._options.delete_unversioned_trees or
1555 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001556 # There are modified files in this entry. Keep warning until
1557 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001558 print(('\nWARNING: \'%s\' is no longer part of this client. '
1559 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001560 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001561 else:
1562 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001563 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001564 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001565 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001566 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001567 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001568 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001569
1570 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001571 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001572 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001573 # Load all the settings.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001574 work_queue = gclient_utils.ExecutionQueue(
1575 self._options.jobs, None, False, verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001576 for s in self.dependencies:
Paweł Hajdan, Jr4baaa112017-07-04 19:09:32 +02001577 if s.should_process:
1578 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001579 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001580
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001581 def GetURLAndRev(dep):
1582 """Returns the revision-qualified SCM url for a Dependency."""
1583 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001584 return None
agabled437d762016-10-17 09:35:11 -07001585 url, _ = gclient_utils.SplitUrlRevision(dep.parsed_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001586 scm = gclient_scm.CreateSCM(
agabled437d762016-10-17 09:35:11 -07001587 dep.parsed_url, self.root_dir, dep.name, self.outbuf)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001588 if not os.path.isdir(scm.checkout_path):
1589 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001590 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001591
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001592 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001593 new_gclient = ''
1594 # First level at .gclient
1595 for d in self.dependencies:
1596 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001597 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001598 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001599 for d in dep.dependencies:
1600 entries[d.name] = GetURLAndRev(d)
1601 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001602 GrabDeps(d)
1603 custom_deps = []
1604 for k in sorted(entries.keys()):
1605 if entries[k]:
1606 # Quotes aren't escaped...
1607 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1608 else:
1609 custom_deps.append(' \"%s\": None,\n' % k)
1610 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1611 'solution_name': d.name,
1612 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001613 'deps_file': d.deps_file,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001614 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001615 'solution_deps': ''.join(custom_deps),
1616 }
1617 # Print the snapshot configuration file
1618 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001619 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001620 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001621 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001622 if self._options.actual:
1623 entries[d.name] = GetURLAndRev(d)
1624 else:
1625 entries[d.name] = d.parsed_url
1626 keys = sorted(entries.keys())
1627 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001628 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001629 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001630
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001631 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001632 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001633 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001634
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001635 def PrintLocationAndContents(self):
1636 # Print out the .gclient file. This is longer than if we just printed the
1637 # client dict, but more legible, and it might contain helpful comments.
1638 print('Loaded .gclient config in %s:\n%s' % (
1639 self.root_dir, self.config_content))
1640
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001641 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001642 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001643 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001644 return self._root_dir
1645
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001646 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001647 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001648 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001649 return self._enforced_os
1650
maruel@chromium.org68988972011-09-20 14:11:42 +00001651 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001652 def recursion_limit(self):
1653 """How recursive can each dependencies in DEPS file can load DEPS file."""
1654 return self._recursion_limit
1655
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001656 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +00001657 def try_recursedeps(self):
1658 """Whether to attempt using recursedeps-style recursion processing."""
cmp@chromium.orge84ac912014-06-30 23:14:35 +00001659 return True
1660
1661 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001662 def target_os(self):
1663 return self._enforced_os
1664
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001665
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001666#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001667
1668
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001669@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001670def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001671 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001672
1673 Runs a shell command on all entries.
qyearsley12fa6ff2016-08-24 09:18:40 -07001674 Sets GCLIENT_DEP_PATH environment variable as the dep's relative location to
ilevy@chromium.org37116242012-11-28 01:32:48 +00001675 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001676 """
1677 # Stop parsing at the first non-arg so that these go through to the command
1678 parser.disable_interspersed_args()
1679 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001680 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001681 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001682 help='Ignore non-zero return codes from subcommands.')
1683 parser.add_option('--prepend-dir', action='store_true',
1684 help='Prepend relative dir for use with git <cmd> --null.')
1685 parser.add_option('--no-progress', action='store_true',
1686 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001687 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001688 if not args:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001689 print('Need to supply a command!', file=sys.stderr)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001690 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001691 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1692 if not root_and_entries:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001693 print(
maruel@chromium.org78cba522010-10-18 13:32:05 +00001694 'You need to run gclient sync at least once to use \'recurse\'.\n'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001695 'This is because .gclient_entries needs to exist and be up to date.',
1696 file=sys.stderr)
maruel@chromium.org78cba522010-10-18 13:32:05 +00001697 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001698
1699 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001700 scm_set = set()
1701 for scm in options.scm:
1702 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001703 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001704
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001705 options.nohooks = True
1706 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001707 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1708 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001709
1710
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001711@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001712def CMDfetch(parser, args):
1713 """Fetches upstream commits for all modules.
1714
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001715 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1716 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001717 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001718 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001719 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1720
1721
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001722class Flattener(object):
1723 """Flattens a gclient solution."""
1724
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001725 def __init__(self, client, pin_all_deps=False):
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001726 """Constructor.
1727
1728 Arguments:
1729 client (GClient): client to flatten
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001730 pin_all_deps (bool): whether to pin all deps, even if they're not pinned
1731 in DEPS
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001732 """
1733 self._client = client
1734
1735 self._deps_string = None
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02001736 self._deps_files = set()
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001737
1738 self._allowed_hosts = set()
1739 self._deps = {}
1740 self._deps_os = {}
1741 self._hooks = []
1742 self._hooks_os = {}
1743 self._pre_deps_hooks = []
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02001744 self._vars = {}
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001745
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001746 self._flatten(pin_all_deps=pin_all_deps)
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001747
1748 @property
1749 def deps_string(self):
1750 assert self._deps_string is not None
1751 return self._deps_string
1752
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02001753 @property
1754 def deps_files(self):
1755 return self._deps_files
1756
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02001757 def _pin_dep(self, dep):
1758 """Pins a dependency to specific full revision sha.
1759
1760 Arguments:
1761 dep (Dependency): dependency to process
1762 """
1763 if dep.parsed_url is None:
1764 return
1765
1766 # Make sure the revision is always fully specified (a hash),
1767 # as opposed to refs or tags which might change. Similarly,
1768 # shortened shas might become ambiguous; make sure to always
1769 # use full one for pinning.
1770 url, revision = gclient_utils.SplitUrlRevision(dep.parsed_url)
1771 if revision and gclient_utils.IsFullGitSha(revision):
1772 return
1773
1774 scm = gclient_scm.CreateSCM(
1775 dep.parsed_url, self._client.root_dir, dep.name, dep.outbuf)
1776 revinfo = scm.revinfo(self._client._options, [], None)
1777
1778 dep._parsed_url = dep._url = '%s@%s' % (url, revinfo)
1779 raw_url, _ = gclient_utils.SplitUrlRevision(dep._raw_url)
1780 dep._raw_url = '%s@%s' % (raw_url, revinfo)
1781
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001782 def _flatten(self, pin_all_deps=False):
1783 """Runs the flattener. Saves resulting DEPS string.
1784
1785 Arguments:
1786 pin_all_deps (bool): whether to pin all deps, even if they're not pinned
1787 in DEPS
1788 """
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001789 for solution in self._client.dependencies:
Paweł Hajdan, Jr11eb7152017-08-10 12:50:11 +02001790 self._add_dep(solution)
Paweł Hajdan, Jrb0ad16e2017-08-03 15:33:21 +02001791 self._flatten_dep(solution)
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001792
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001793 if pin_all_deps:
1794 for dep in self._deps.itervalues():
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02001795 self._pin_dep(dep)
Paweł Hajdan, Jr39300ba2017-08-11 16:52:38 +02001796
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02001797 for os_deps in self._deps_os.itervalues():
1798 for dep in os_deps.itervalues():
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02001799 self._pin_dep(dep)
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001800
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02001801 def add_deps_file(dep):
Paweł Hajdan, Jr0870df22017-08-23 17:59:29 +02001802 # Only include DEPS files referenced by recursedeps.
1803 if not (dep.parent is None or
1804 (dep.name in (dep.parent.recursedeps or {}))):
1805 return
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02001806 deps_file = dep.deps_file
1807 deps_path = os.path.join(self._client.root_dir, dep.name, deps_file)
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02001808 if not os.path.exists(deps_path):
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02001809 # gclient has a fallback that if deps_file doesn't exist, it'll try
1810 # DEPS. Do the same here.
1811 deps_file = 'DEPS'
1812 deps_path = os.path.join(self._client.root_dir, dep.name, deps_file)
1813 if not os.path.exists(deps_path):
1814 return
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02001815 assert dep.parsed_url
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02001816 self._deps_files.add((dep.parsed_url, deps_file))
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02001817 for dep in self._deps.itervalues():
1818 add_deps_file(dep)
1819 for os_deps in self._deps_os.itervalues():
1820 for dep in os_deps.itervalues():
1821 add_deps_file(dep)
1822
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001823 self._deps_string = '\n'.join(
1824 _GNSettingsToLines(
1825 self._client.dependencies[0]._gn_args_file,
1826 self._client.dependencies[0]._gn_args) +
1827 _AllowedHostsToLines(self._allowed_hosts) +
1828 _DepsToLines(self._deps) +
1829 _DepsOsToLines(self._deps_os) +
1830 _HooksToLines('hooks', self._hooks) +
1831 _HooksToLines('pre_deps_hooks', self._pre_deps_hooks) +
1832 _HooksOsToLines(self._hooks_os) +
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02001833 _VarsToLines(self._vars) +
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02001834 ['# %s, %s' % (url, deps_file)
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02001835 for url, deps_file in sorted(self._deps_files)] +
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001836 ['']) # Ensure newline at end of file.
1837
Paweł Hajdan, Jr11eb7152017-08-10 12:50:11 +02001838 def _add_dep(self, dep):
1839 """Helper to add a dependency to flattened DEPS.
1840
1841 Arguments:
1842 dep (Dependency): dependency to add
1843 """
1844 assert dep.name not in self._deps or self._deps.get(dep.name) == dep, (
1845 dep.name, self._deps.get(dep.name))
Paweł Hajdan, Jr9a289022017-08-10 16:04:24 +02001846 if dep.url:
1847 self._deps[dep.name] = dep
Paweł Hajdan, Jr11eb7152017-08-10 12:50:11 +02001848
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001849 def _add_os_dep(self, os_dep, dep_os):
1850 """Helper to add an OS-specific dependency to flattened DEPS.
1851
1852 Arguments:
1853 os_dep (Dependency): dependency to add
1854 dep_os (str): name of the OS
1855 """
1856 assert (
1857 os_dep.name not in self._deps_os.get(dep_os, {}) or
1858 self._deps_os.get(dep_os, {}).get(os_dep.name) == os_dep), (
1859 os_dep.name, self._deps_os.get(dep_os, {}).get(os_dep.name))
1860 if os_dep.url:
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02001861 # OS-specific deps need to have their full URL resolved manually.
1862 assert not os_dep.parsed_url, (os_dep, os_dep.parsed_url)
1863 os_dep._parsed_url = os_dep.LateOverride(os_dep.url)
1864
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001865 self._deps_os.setdefault(dep_os, {})[os_dep.name] = os_dep
1866
1867 def _flatten_dep(self, dep, dep_os=None):
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001868 """Visits a dependency in order to flatten it (see CMDflatten).
1869
1870 Arguments:
1871 dep (Dependency): dependency to process
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001872 dep_os (str or None): name of the OS |dep| is specific to
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001873 """
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001874 logging.debug('_flatten_dep(%s, %s)', dep.name, dep_os)
1875
Paweł Hajdan, Jrc69b32e2017-08-17 18:47:48 +02001876 if not dep.deps_parsed:
1877 dep.ParseDepsFile()
1878
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001879 self._allowed_hosts.update(dep.allowed_hosts)
1880
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001881 # Only include vars listed in the DEPS files, not possible local overrides.
1882 for key, value in dep._vars.iteritems():
Paweł Hajdan, Jrc9353602017-08-02 17:52:08 +02001883 # Make sure there are no conflicting variables. It is fine however
1884 # to use same variable name, as long as the value is consistent.
1885 assert key not in self._vars or self._vars[key][1] == value
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02001886 self._vars[key] = (dep, value)
1887
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001888 self._pre_deps_hooks.extend([(dep, hook) for hook in dep.pre_deps_hooks])
1889
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001890 if dep_os:
1891 if dep.deps_hooks:
1892 self._hooks_os.setdefault(dep_os, []).extend(
1893 [(dep, hook) for hook in dep.deps_hooks])
1894 else:
1895 self._hooks.extend([(dep, hook) for hook in dep.deps_hooks])
1896
Paweł Hajdan, Jrb0ad16e2017-08-03 15:33:21 +02001897 for sub_dep in dep.dependencies:
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001898 if dep_os:
1899 self._add_os_dep(sub_dep, dep_os)
1900 else:
1901 self._add_dep(sub_dep)
Paweł Hajdan, Jrb0ad16e2017-08-03 15:33:21 +02001902
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001903 for hook_os, os_hooks in dep.os_deps_hooks.iteritems():
1904 self._hooks_os.setdefault(hook_os, []).extend(
1905 [(dep, hook) for hook in os_hooks])
1906
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001907 for sub_dep_os, os_deps in dep.os_dependencies.iteritems():
Paweł Hajdan, Jre2deb1e2017-08-09 17:29:21 +02001908 for os_dep in os_deps:
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001909 self._add_os_dep(os_dep, sub_dep_os)
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001910
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001911 # Process recursedeps. |deps_by_name| is a map where keys are dependency
1912 # names, and values are maps of OS names to |Dependency| instances.
1913 # |None| in place of OS name means the dependency is not OS-specific.
1914 deps_by_name = dict((d.name, {None: d}) for d in dep.dependencies)
1915 for sub_dep_os, os_deps in dep.os_dependencies.iteritems():
Paweł Hajdan, Jrc9353602017-08-02 17:52:08 +02001916 for os_dep in os_deps:
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001917 assert sub_dep_os not in deps_by_name.get(os_dep.name, {}), (
1918 os_dep.name, sub_dep_os)
1919 deps_by_name.setdefault(os_dep.name, {})[sub_dep_os] = os_dep
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001920 for recurse_dep_name in (dep.recursedeps or []):
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02001921 dep_info = deps_by_name[recurse_dep_name]
1922 for sub_dep_os, os_dep in dep_info.iteritems():
1923 self._flatten_dep(os_dep, dep_os=(sub_dep_os or dep_os))
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001924
1925
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001926def CMDflatten(parser, args):
1927 """Flattens the solutions into a single DEPS file."""
1928 parser.add_option('--output-deps', help='Path to the output DEPS file')
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001929 parser.add_option(
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02001930 '--output-deps-files',
1931 help=('Path to the output metadata about DEPS files referenced by '
1932 'recursedeps.'))
1933 parser.add_option(
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001934 '--pin-all-deps', action='store_true',
1935 help=('Pin all deps, even if not pinned in DEPS. CAVEAT: only does so '
1936 'for checked out deps, NOT deps_os.'))
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001937 options, args = parser.parse_args(args)
1938
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +02001939 options.do_not_merge_os_specific_entries = True
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001940 options.nohooks = True
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02001941 options.process_all_deps = True
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001942 client = GClient.LoadCurrentConfig(options)
1943
1944 # Only print progress if we're writing to a file. Otherwise, progress updates
1945 # could obscure intended output.
1946 code = client.RunOnDeps('flatten', args, progress=options.output_deps)
1947 if code != 0:
1948 return code
1949
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02001950 flattener = Flattener(client, pin_all_deps=options.pin_all_deps)
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001951
1952 if options.output_deps:
1953 with open(options.output_deps, 'w') as f:
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001954 f.write(flattener.deps_string)
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001955 else:
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02001956 print(flattener.deps_string)
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001957
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02001958 deps_files = [{'url': d[0], 'deps_file': d[1]}
1959 for d in sorted(flattener.deps_files)]
1960 if options.output_deps_files:
1961 with open(options.output_deps_files, 'w') as f:
1962 json.dump(deps_files, f)
1963
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001964 return 0
1965
1966
Paweł Hajdan, Jr3c2aa832017-06-07 20:22:16 +02001967def _GNSettingsToLines(gn_args_file, gn_args):
1968 s = []
1969 if gn_args_file:
1970 s.extend([
1971 'gclient_gn_args_file = "%s"' % gn_args_file,
1972 'gclient_gn_args = %r' % gn_args,
1973 ])
1974 return s
1975
1976
Paweł Hajdan, Jr6014b562017-06-30 17:43:42 +02001977def _AllowedHostsToLines(allowed_hosts):
1978 """Converts |allowed_hosts| set to list of lines for output."""
1979 if not allowed_hosts:
1980 return []
1981 s = ['allowed_hosts = [']
1982 for h in sorted(allowed_hosts):
1983 s.append(' "%s",' % h)
1984 s.extend([']', ''])
1985 return s
1986
1987
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001988def _DepsToLines(deps):
1989 """Converts |deps| dict to list of lines for output."""
Paweł Hajdan, Jr5b593352017-06-29 18:37:45 +02001990 if not deps:
1991 return []
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001992 s = ['deps = {']
1993 for name, dep in sorted(deps.iteritems()):
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001994 condition_part = ([' "condition": "%s",' % dep.condition]
1995 if dep.condition else [])
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001996 s.extend([
1997 ' # %s' % dep.hierarchy(include_url=False),
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001998 ' "%s": {' % (name,),
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +02001999 ' "url": "%s",' % (dep.raw_url,),
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02002000 ] + condition_part + [
2001 ' },',
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002002 '',
2003 ])
2004 s.extend(['}', ''])
2005 return s
2006
2007
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +02002008def _DepsOsToLines(deps_os):
2009 """Converts |deps_os| dict to list of lines for output."""
Paweł Hajdan, Jr5b593352017-06-29 18:37:45 +02002010 if not deps_os:
2011 return []
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +02002012 s = ['deps_os = {']
2013 for dep_os, os_deps in sorted(deps_os.iteritems()):
2014 s.append(' "%s": {' % dep_os)
Paweł Hajdan, Jrad30de62017-06-26 18:51:58 +02002015 for name, dep in sorted(os_deps.iteritems()):
2016 condition_part = ([' "condition": "%s",' % dep.condition]
2017 if dep.condition else [])
2018 s.extend([
2019 ' # %s' % dep.hierarchy(include_url=False),
2020 ' "%s": {' % (name,),
Paweł Hajdan, Jrde86ab32017-08-10 13:55:16 +02002021 ' "url": "%s",' % (dep.raw_url,),
Paweł Hajdan, Jrad30de62017-06-26 18:51:58 +02002022 ] + condition_part + [
2023 ' },',
2024 '',
2025 ])
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +02002026 s.extend([' },', ''])
2027 s.extend(['}', ''])
2028 return s
2029
2030
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002031def _HooksToLines(name, hooks):
2032 """Converts |hooks| list to list of lines for output."""
Paweł Hajdan, Jr5b593352017-06-29 18:37:45 +02002033 if not hooks:
2034 return []
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002035 s = ['%s = [' % name]
2036 for dep, hook in hooks:
2037 s.extend([
2038 ' # %s' % dep.hierarchy(include_url=False),
2039 ' {',
2040 ])
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02002041 if hook.name is not None:
2042 s.append(' "name": "%s",' % hook.name)
2043 if hook.pattern is not None:
2044 s.append(' "pattern": "%s",' % hook.pattern)
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002045 s.extend(
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +02002046 # Hooks run in the parent directory of their dep.
Paweł Hajdan, Jrad30de62017-06-26 18:51:58 +02002047 [' "cwd": "%s",' % os.path.normpath(os.path.dirname(dep.name))] +
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002048 [' "action": ['] +
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02002049 [' "%s",' % arg for arg in hook.action] +
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002050 [' ]', ' },', '']
2051 )
2052 s.extend([']', ''])
2053 return s
2054
2055
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002056def _HooksOsToLines(hooks_os):
2057 """Converts |hooks| list to list of lines for output."""
Paweł Hajdan, Jr5b593352017-06-29 18:37:45 +02002058 if not hooks_os:
2059 return []
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002060 s = ['hooks_os = {']
2061 for hook_os, os_hooks in hooks_os.iteritems():
Michael Moss017bcf62017-06-28 15:26:38 -07002062 s.append(' "%s": [' % hook_os)
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002063 for dep, hook in os_hooks:
2064 s.extend([
2065 ' # %s' % dep.hierarchy(include_url=False),
2066 ' {',
2067 ])
2068 if hook.name is not None:
2069 s.append(' "name": "%s",' % hook.name)
2070 if hook.pattern is not None:
2071 s.append(' "pattern": "%s",' % hook.pattern)
2072 s.extend(
2073 # Hooks run in the parent directory of their dep.
2074 [' "cwd": "%s",' % os.path.normpath(os.path.dirname(dep.name))] +
2075 [' "action": ['] +
2076 [' "%s",' % arg for arg in hook.action] +
2077 [' ]', ' },', '']
2078 )
Michael Moss017bcf62017-06-28 15:26:38 -07002079 s.extend([' ],', ''])
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002080 s.extend(['}', ''])
2081 return s
2082
2083
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02002084def _VarsToLines(variables):
2085 """Converts |variables| dict to list of lines for output."""
2086 if not variables:
2087 return []
2088 s = ['vars = {']
2089 for key, tup in sorted(variables.iteritems()):
2090 dep, value = tup
2091 s.extend([
2092 ' # %s' % dep.hierarchy(include_url=False),
2093 ' "%s": %r,' % (key, value),
2094 '',
2095 ])
2096 s.extend(['}', ''])
2097 return s
2098
2099
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002100def CMDgrep(parser, args):
2101 """Greps through git repos managed by gclient.
2102
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002103 Runs 'git grep [args...]' for each module.
2104 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002105 # We can't use optparse because it will try to parse arguments sent
2106 # to git grep and throw an error. :-(
2107 if not args or re.match('(-h|--help)$', args[0]):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002108 print(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002109 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
2110 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
2111 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
2112 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002113 ' end of your query.',
2114 file=sys.stderr)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002115 return 1
2116
2117 jobs_arg = ['--jobs=1']
2118 if re.match(r'(-j|--jobs=)\d+$', args[0]):
2119 jobs_arg, args = args[:1], args[1:]
2120 elif re.match(r'(-j|--jobs)$', args[0]):
2121 jobs_arg, args = args[:2], args[2:]
2122
2123 return CMDrecurse(
2124 parser,
2125 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
2126 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00002127
2128
stip@chromium.orga735da22015-04-29 23:18:20 +00002129def CMDroot(parser, args):
2130 """Outputs the solution root (or current dir if there isn't one)."""
2131 (options, args) = parser.parse_args(args)
2132 client = GClient.LoadCurrentConfig(options)
2133 if client:
2134 print(os.path.abspath(client.root_dir))
2135 else:
2136 print(os.path.abspath('.'))
2137
2138
agablea98a6cd2016-11-15 14:30:10 -08002139@subcommand.usage('[url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002140def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002141 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00002142
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002143 This specifies the configuration for further commands. After update/sync,
2144 top-level DEPS files in each module are read to determine dependent
2145 modules to operate on as well. If optional [url] parameter is
2146 provided, then configuration is read from a specified Subversion server
2147 URL.
2148 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00002149 # We do a little dance with the --gclientfile option. 'gclient config' is the
2150 # only command where it's acceptable to have both '--gclientfile' and '--spec'
2151 # arguments. So, we temporarily stash any --gclientfile parameter into
2152 # options.output_config_file until after the (gclientfile xor spec) error
2153 # check.
2154 parser.remove_option('--gclientfile')
2155 parser.add_option('--gclientfile', dest='output_config_file',
2156 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002157 parser.add_option('--name',
2158 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00002159 parser.add_option('--deps-file', default='DEPS',
2160 help='overrides the default name for the DEPS file for the'
2161 'main solutions and all sub-dependencies')
smutae7ea312016-07-18 11:59:41 -07002162 parser.add_option('--unmanaged', action='store_true', default=False,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00002163 help='overrides the default behavior to make it possible '
smutae7ea312016-07-18 11:59:41 -07002164 'to have the main solution untouched by gclient '
2165 '(gclient will check out unmanaged dependencies but '
2166 'will never sync them)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002167 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002168 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00002169 if options.output_config_file:
2170 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00002171 if ((options.spec and args) or len(args) > 2 or
2172 (not options.spec and not args)):
2173 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
2174
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002175 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002176 if options.spec:
2177 client.SetConfig(options.spec)
2178 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00002179 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00002180 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002181 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00002182 if name.endswith('.git'):
2183 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00002184 else:
2185 # specify an alternate relpath for the given URL.
2186 name = options.name
agable@chromium.orgf2214672015-10-27 21:02:48 +00002187 if not os.path.abspath(os.path.join(os.getcwd(), name)).startswith(
2188 os.getcwd()):
2189 parser.error('Do not pass a relative path for --name.')
2190 if any(x in ('..', '.', '/', '\\') for x in name.split(os.sep)):
2191 parser.error('Do not include relative path components in --name.')
2192
nsylvain@google.comefc80932011-05-31 21:27:56 +00002193 deps_file = options.deps_file
agablea98a6cd2016-11-15 14:30:10 -08002194 client.SetDefaultConfig(name, deps_file, base_url,
smutae7ea312016-07-18 11:59:41 -07002195 managed=not options.unmanaged,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00002196 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002197 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002198 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002199
2200
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002201@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002202 gclient pack > patch.txt
2203 generate simple patch for configured client and dependences
2204""")
2205def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002206 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00002207
agabled437d762016-10-17 09:35:11 -07002208 Internally, runs 'git diff' on each checked out module and
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002209 dependencies, and performs minimal postprocessing of the output. The
2210 resulting patch is printed to stdout and can be applied to a freshly
2211 checked out tree via 'patch -p0 < patchfile'.
2212 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002213 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2214 help='override deps for the specified (comma-separated) '
2215 'platform(s); \'all\' will process all deps_os '
2216 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00002217 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002218 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00002219 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00002220 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00002221 client = GClient.LoadCurrentConfig(options)
2222 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002223 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00002224 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002225 client.PrintLocationAndContents()
kbr@google.comab318592009-09-04 00:54:55 +00002226 return client.RunOnDeps('pack', args)
2227
2228
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002229def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002230 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002231 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2232 help='override deps for the specified (comma-separated) '
2233 'platform(s); \'all\' will process all deps_os '
2234 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002235 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002236 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002237 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002238 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002239 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002240 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002241 return client.RunOnDeps('status', args)
2242
2243
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002244@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00002245 gclient sync
2246 update files from SCM according to current configuration,
2247 *for modules which have changed since last update or sync*
2248 gclient sync --force
2249 update files from SCM according to current configuration, for
2250 all modules (useful for recovering files deleted from local copy)
2251 gclient sync --revision src@31000
2252 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002253
2254JSON output format:
2255If the --output-json option is specified, the following document structure will
2256be emitted to the provided file. 'null' entries may occur for subprojects which
2257are present in the gclient solution, but were not processed (due to custom_deps,
2258os_deps, etc.)
2259
2260{
2261 "solutions" : {
2262 "<name>": { # <name> is the posix-normalized path to the solution.
agabled437d762016-10-17 09:35:11 -07002263 "revision": [<git id hex string>|null],
2264 "scm": ["git"|null],
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002265 }
2266 }
2267}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002268""")
2269def CMDsync(parser, args):
2270 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002271 parser.add_option('-f', '--force', action='store_true',
2272 help='force update even for unchanged modules')
2273 parser.add_option('-n', '--nohooks', action='store_true',
2274 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002275 parser.add_option('-p', '--noprehooks', action='store_true',
2276 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002277 parser.add_option('-r', '--revision', action='append',
2278 dest='revisions', metavar='REV', default=[],
2279 help='Enforces revision/hash for the solutions with the '
2280 'format src@rev. The src@ part is optional and can be '
2281 'skipped. -r can be used multiple times when .gclient '
2282 'has multiple solutions configured and will work even '
agablea98a6cd2016-11-15 14:30:10 -08002283 'if the src@ part is skipped.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00002284 parser.add_option('--with_branch_heads', action='store_true',
2285 help='Clone git "branch_heads" refspecs in addition to '
2286 'the default refspecs. This adds about 1/2GB to a '
2287 'full checkout. (git only)')
szager@chromium.org8d3348f2014-08-19 22:49:16 +00002288 parser.add_option('--with_tags', action='store_true',
2289 help='Clone git tags in addition to the default refspecs.')
agable2697cd12016-06-28 10:23:53 -07002290 parser.add_option('-H', '--head', action='store_true',
agablea98a6cd2016-11-15 14:30:10 -08002291 help='DEPRECATED: only made sense with safesync urls.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002292 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002293 help='Deletes from the working copy any dependencies that '
2294 'have been removed since the last sync, as long as '
2295 'there are no local modifications. When used with '
2296 '--force, such dependencies are removed even if they '
2297 'have local modifications. When used with --reset, '
2298 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002299 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002300 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002301 parser.add_option('-R', '--reset', action='store_true',
2302 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00002303 parser.add_option('-M', '--merge', action='store_true',
2304 help='merge upstream changes instead of trying to '
2305 'fast-forward or rebase')
dnj@chromium.org5b23e872015-02-20 21:25:57 +00002306 parser.add_option('-A', '--auto_rebase', action='store_true',
2307 help='Automatically rebase repositories against local '
2308 'checkout during update (git only).')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002309 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2310 help='override deps for the specified (comma-separated) '
2311 'platform(s); \'all\' will process all deps_os '
2312 'references')
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +02002313 # TODO(phajdan.jr): use argparse.SUPPRESS to hide internal flags.
2314 parser.add_option('--do-not-merge-os-specific-entries', action='store_true',
2315 help='INTERNAL ONLY - disables merging of deps_os and '
2316 'hooks_os to dependencies and hooks')
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002317 parser.add_option('--process-all-deps', action='store_true',
2318 help='Check out all deps, even for different OS-es, '
2319 'or with conditions evaluating to false')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002320 parser.add_option('--upstream', action='store_true',
2321 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002322 parser.add_option('--output-json',
2323 help='Output a json document to this path containing '
2324 'summary information about the sync.')
primiano@chromium.org5439ea52014-08-06 17:18:18 +00002325 parser.add_option('--no-history', action='store_true',
2326 help='GIT ONLY - Reduces the size/time of the checkout at '
2327 'the cost of no history. Requires Git 1.9+')
hinoka@chromium.org46b87412014-05-15 00:42:05 +00002328 parser.add_option('--shallow', action='store_true',
2329 help='GIT ONLY - Do a shallow clone into the cache dir. '
2330 'Requires Git 1.9+')
e.hakkinen@samsung.come8bc1aa2015-04-08 08:00:37 +00002331 parser.add_option('--no_bootstrap', '--no-bootstrap',
2332 action='store_true',
2333 help='Don\'t bootstrap from Google Storage.')
hinoka@chromium.org8a10f6d2014-06-23 18:38:57 +00002334 parser.add_option('--ignore_locks', action='store_true',
2335 help='GIT ONLY - Ignore cache locks.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +00002336 parser.add_option('--break_repo_locks', action='store_true',
2337 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2338 'index.lock). This should only be used if you know for '
2339 'certain that this invocation of gclient is the only '
2340 'thing operating on the git repos (e.g. on a bot).')
nodir@chromium.org5b48e482016-03-18 20:27:54 +00002341 parser.add_option('--lock_timeout', type='int', default=5000,
szager@chromium.orgdbb6f822016-02-02 22:59:30 +00002342 help='GIT ONLY - Deadline (in seconds) to wait for git '
nodir@chromium.org5b48e482016-03-18 20:27:54 +00002343 'cache lock to become available. Default is %default.')
agabled437d762016-10-17 09:35:11 -07002344 # TODO(agable): Remove these when the oldest CrOS release milestone is M56.
2345 parser.add_option('-t', '--transitive', action='store_true',
2346 help='DEPRECATED: This is a no-op.')
sdefresne69b1be12016-10-18 05:48:02 -07002347 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
agabled437d762016-10-17 09:35:11 -07002348 help='DEPRECATED: This is a no-op.')
Paweł Hajdan, Jr7c7b5592017-05-23 15:06:05 +02002349 # TODO(phajdan.jr): Remove validation options once default (crbug/570091).
Paweł Hajdan, Jr694773d2017-05-29 16:06:23 +02002350 parser.add_option('--validate-syntax', action='store_true', default=True,
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02002351 help='Validate the .gclient and DEPS syntax')
Paweł Hajdan, Jr7c7b5592017-05-23 15:06:05 +02002352 parser.add_option('--disable-syntax-validation', action='store_false',
2353 dest='validate_syntax',
2354 help='Disable validation of .gclient and DEPS syntax.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002355 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002356 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002357
2358 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002359 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002360
smutae7ea312016-07-18 11:59:41 -07002361 if options.revisions and options.head:
2362 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
2363 print('Warning: you cannot use both --head and --revision')
2364
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002365 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002366 client.PrintLocationAndContents()
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002367 ret = client.RunOnDeps('update', args)
2368 if options.output_json:
2369 slns = {}
2370 for d in client.subtree(True):
2371 normed = d.name.replace('\\', '/').rstrip('/') + '/'
2372 slns[normed] = {
2373 'revision': d.got_revision,
2374 'scm': d.used_scm.name if d.used_scm else None,
hinoka@chromium.org17db9052014-05-10 01:11:29 +00002375 'url': str(d.url) if d.url else None,
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002376 }
2377 with open(options.output_json, 'wb') as f:
2378 json.dump({'solutions': slns}, f)
2379 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002380
2381
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002382CMDupdate = CMDsync
2383
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002384
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02002385def CMDvalidate(parser, args):
2386 """Validates the .gclient and DEPS syntax."""
2387 options, args = parser.parse_args(args)
2388 options.validate_syntax = True
2389 client = GClient.LoadCurrentConfig(options)
2390 rv = client.RunOnDeps('validate', args)
2391 if rv == 0:
2392 print('validate: SUCCESS')
2393 else:
2394 print('validate: FAILURE')
2395 return rv
2396
2397
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002398def CMDdiff(parser, args):
2399 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002400 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2401 help='override deps for the specified (comma-separated) '
2402 'platform(s); \'all\' will process all deps_os '
2403 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002404 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002405 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002406 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002407 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002408 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002409 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002410 return client.RunOnDeps('diff', args)
2411
2412
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002413def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002414 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00002415
2416 That's the nuclear option to get back to a 'clean' state. It removes anything
agabled437d762016-10-17 09:35:11 -07002417 that shows up in git status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002418 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2419 help='override deps for the specified (comma-separated) '
2420 'platform(s); \'all\' will process all deps_os '
2421 'references')
2422 parser.add_option('-n', '--nohooks', action='store_true',
2423 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002424 parser.add_option('-p', '--noprehooks', action='store_true',
2425 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002426 parser.add_option('--upstream', action='store_true',
2427 help='Make repo state match upstream branch.')
iannucci@chromium.orgbf525dc2016-04-07 22:00:28 +00002428 parser.add_option('--break_repo_locks', action='store_true',
2429 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2430 'index.lock). This should only be used if you know for '
2431 'certain that this invocation of gclient is the only '
2432 'thing operating on the git repos (e.g. on a bot).')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002433 (options, args) = parser.parse_args(args)
2434 # --force is implied.
2435 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002436 options.reset = False
2437 options.delete_unversioned_trees = False
agablec903d732016-07-26 09:07:24 -07002438 options.merge = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002439 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002440 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002441 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002442 return client.RunOnDeps('revert', args)
2443
2444
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002445def CMDrunhooks(parser, args):
2446 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002447 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2448 help='override deps for the specified (comma-separated) '
2449 'platform(s); \'all\' will process all deps_os '
2450 'references')
2451 parser.add_option('-f', '--force', action='store_true', default=True,
2452 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002453 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002454 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002455 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002456 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002457 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002458 client.PrintLocationAndContents()
maruel@chromium.org5df6a462009-08-28 18:52:26 +00002459 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002460 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002461 return client.RunOnDeps('runhooks', args)
2462
2463
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002464def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002465 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002466
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002467 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002468 can be used to reproduce the same tree in the future. It is only useful for
agabled437d762016-10-17 09:35:11 -07002469 'unpinned dependencies', i.e. DEPS/deps references without a git hash.
2470 A git branch name isn't 'pinned' since the actual commit can change.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002471 """
2472 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2473 help='override deps for the specified (comma-separated) '
2474 'platform(s); \'all\' will process all deps_os '
2475 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002476 parser.add_option('-a', '--actual', action='store_true',
2477 help='gets the actual checked out revisions instead of the '
2478 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002479 parser.add_option('-s', '--snapshot', action='store_true',
2480 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002481 'version of all repositories to reproduce the tree, '
2482 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002483 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002484 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002485 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002486 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002487 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002488 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002489
2490
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002491def CMDverify(parser, args):
2492 """Verifies the DEPS file deps are only from allowed_hosts."""
2493 (options, args) = parser.parse_args(args)
2494 client = GClient.LoadCurrentConfig(options)
2495 if not client:
2496 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2497 client.RunOnDeps(None, [])
2498 # Look at each first-level dependency of this gclient only.
2499 for dep in client.dependencies:
2500 bad_deps = dep.findDepsFromNotAllowedHosts()
2501 if not bad_deps:
2502 continue
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002503 print("There are deps from not allowed hosts in file %s" % dep.deps_file)
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002504 for bad_dep in bad_deps:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002505 print("\t%s at %s" % (bad_dep.name, bad_dep.url))
2506 print("allowed_hosts:", ', '.join(dep.allowed_hosts))
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002507 sys.stdout.flush()
2508 raise gclient_utils.Error(
2509 'dependencies from disallowed hosts; check your DEPS file.')
2510 return 0
2511
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002512class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00002513 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002514
2515 def __init__(self, **kwargs):
2516 optparse.OptionParser.__init__(
2517 self, version='%prog ' + __version__, **kwargs)
2518
2519 # Some arm boards have issues with parallel sync.
2520 if platform.machine().startswith('arm'):
2521 jobs = 1
2522 else:
2523 jobs = max(8, gclient_utils.NumLocalCpus())
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002524
2525 self.add_option(
2526 '-j', '--jobs', default=jobs, type='int',
2527 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002528 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002529 self.add_option(
2530 '-v', '--verbose', action='count', default=0,
2531 help='Produces additional output for diagnostics. Can be used up to '
2532 'three times for more logging info.')
2533 self.add_option(
2534 '--gclientfile', dest='config_filename',
2535 help='Specify an alternate %s file' % self.gclientfile_default)
2536 self.add_option(
2537 '--spec',
2538 help='create a gclient file containing the provided string. Due to '
2539 'Cygwin/Python brokenness, it can\'t contain any newlines.')
2540 self.add_option(
Aleksandr Derbenev9e8fb0e2017-08-01 20:18:31 +03002541 '--cache-dir',
2542 help='(git only) Cache all git repos into this dir and do '
2543 'shared clones from the cache, instead of cloning '
2544 'directly from the remote. (experimental)',
2545 default=os.environ.get('GCLIENT_CACHE_DIR'))
2546 self.add_option(
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002547 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00002548 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002549
2550 def parse_args(self, args=None, values=None):
2551 """Integrates standard options processing."""
2552 options, args = optparse.OptionParser.parse_args(self, args, values)
2553 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
2554 logging.basicConfig(
2555 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00002556 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002557 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002558 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00002559 if (options.config_filename and
2560 options.config_filename != os.path.basename(options.config_filename)):
2561 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002562 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002563 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00002564 options.entries_filename = options.config_filename + '_entries'
2565 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002566 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00002567
2568 # These hacks need to die.
2569 if not hasattr(options, 'revisions'):
2570 # GClient.RunOnDeps expects it even if not applicable.
2571 options.revisions = []
smutae7ea312016-07-18 11:59:41 -07002572 if not hasattr(options, 'head'):
2573 options.head = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00002574 if not hasattr(options, 'nohooks'):
2575 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002576 if not hasattr(options, 'noprehooks'):
2577 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00002578 if not hasattr(options, 'deps_os'):
2579 options.deps_os = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00002580 if not hasattr(options, 'force'):
2581 options.force = None
2582 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002583
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002584
2585def disable_buffering():
2586 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
2587 # operations. Python as a strong tendency to buffer sys.stdout.
2588 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
2589 # Make stdout annotated with the thread ids.
2590 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00002591
2592
sbc@chromium.org013731e2015-02-26 18:28:43 +00002593def main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002594 """Doesn't parse the arguments here, just find the right subcommand to
2595 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002596 if sys.hexversion < 0x02060000:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002597 print(
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002598 '\nYour python version %s is unsupported, please upgrade.\n' %
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002599 sys.version.split(' ', 1)[0],
2600 file=sys.stderr)
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002601 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002602 if not sys.executable:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002603 print(
2604 '\nPython cannot find the location of it\'s own executable.\n',
2605 file=sys.stderr)
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002606 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002607 fix_encoding.fix_encoding()
2608 disable_buffering()
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00002609 setup_color.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002610 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002611 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002612 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00002613 except KeyboardInterrupt:
2614 gclient_utils.GClientChildren.KillAllRemainingChildren()
2615 raise
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00002616 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002617 print('Error: %s' % str(e), file=sys.stderr)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002618 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00002619 finally:
2620 gclient_utils.PrintWarnings()
sbc@chromium.org013731e2015-02-26 18:28:43 +00002621 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002622
2623
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00002624if '__main__' == __name__:
sbc@chromium.org013731e2015-02-26 18:28:43 +00002625 try:
2626 sys.exit(main(sys.argv[1:]))
2627 except KeyboardInterrupt:
2628 sys.stderr.write('interrupted\n')
2629 sys.exit(1)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002630
2631# vim: ts=2:sw=2:tw=80:et: