blob: 389275afbf8e6555d61d1004119d962c6da27183 [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
Tom Andersonc31ae0b2018-02-06 14:48:56 -080078#
79# Specifying a target CPU
80# To specify a target CPU, the variables target_cpu and target_cpu_only
81# are available and are analagous to target_os and target_os_only.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000082
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +000083from __future__ import print_function
84
maruel@chromium.org39c0b222013-08-17 16:57:01 +000085__version__ = '0.7'
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000086
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +020087import collections
maruel@chromium.org9e5317a2010-08-13 20:35:11 +000088import copy
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +000089import json
maruel@chromium.org754960e2009-09-21 12:31:05 +000090import logging
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000091import optparse
92import os
bradnelson@google.com4949dab2012-04-19 16:41:07 +000093import platform
maruel@chromium.org621939b2010-08-10 20:12:00 +000094import posixpath
msb@chromium.org2e38de72009-09-28 17:04:47 +000095import pprint
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000096import re
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000097import sys
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +000098import time
bradnelson@google.com4949dab2012-04-19 16:41:07 +000099import urlparse
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000100
Tom Andersonc31ae0b2018-02-06 14:48:56 -0800101import detect_host_arch
maruel@chromium.org35625c72011-03-23 17:34:02 +0000102import fix_encoding
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +0200103import gclient_eval
maruel@chromium.org5f3eee32009-09-17 00:34:30 +0000104import gclient_scm
105import gclient_utils
szager@chromium.org848fd492014-04-09 19:06:44 +0000106import git_cache
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000107from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000108import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000109import subprocess2
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +0000110import setup_color
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000111
112
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200113class GNException(Exception):
114 pass
115
116
117def ToGNString(value, allow_dicts = True):
118 """Returns a stringified GN equivalent of the Python value.
119
120 allow_dicts indicates if this function will allow converting dictionaries
121 to GN scopes. This is only possible at the top level, you can't nest a
122 GN scope in a list, so this should be set to False for recursive calls."""
123 if isinstance(value, basestring):
124 if value.find('\n') >= 0:
125 raise GNException("Trying to print a string with a newline in it.")
126 return '"' + \
127 value.replace('\\', '\\\\').replace('"', '\\"').replace('$', '\\$') + \
128 '"'
129
130 if isinstance(value, unicode):
131 return ToGNString(value.encode('utf-8'))
132
133 if isinstance(value, bool):
134 if value:
135 return "true"
136 return "false"
137
138 # NOTE: some type handling removed compared to chromium/src copy.
139
140 raise GNException("Unsupported type when printing to GN.")
141
142
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200143class Hook(object):
144 """Descriptor of command ran before/after sync or on demand."""
145
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200146 def __init__(self, action, pattern=None, name=None, cwd=None, condition=None,
Daniel Chenga0c5f082017-10-19 13:35:19 -0700147 variables=None, verbose=False):
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200148 """Constructor.
149
150 Arguments:
151 action (list of basestring): argv of the command to run
152 pattern (basestring regex): noop with git; deprecated
153 name (basestring): optional name; no effect on operation
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +0200154 cwd (basestring): working directory to use
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200155 condition (basestring): condition when to run the hook
156 variables (dict): variables for evaluating the condition
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200157 """
158 self._action = gclient_utils.freeze(action)
159 self._pattern = pattern
160 self._name = name
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +0200161 self._cwd = cwd
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200162 self._condition = condition
163 self._variables = variables
Daniel Chenga0c5f082017-10-19 13:35:19 -0700164 self._verbose = verbose
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200165
166 @staticmethod
Michael Moss42d02c22018-02-05 10:32:24 -0800167 def from_dict(d, variables=None, verbose=False, conditions=None):
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200168 """Creates a Hook instance from a dict like in the DEPS file."""
Michael Moss42d02c22018-02-05 10:32:24 -0800169 # Merge any local and inherited conditions.
170 if conditions and d.get('condition'):
171 condition = '(%s) and (%s)' % (conditions, d['condition'])
172 else:
173 condition = conditions or d.get('condition')
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200174 return Hook(
175 d['action'],
176 d.get('pattern'),
177 d.get('name'),
178 d.get('cwd'),
Michael Moss42d02c22018-02-05 10:32:24 -0800179 condition,
Daniel Chenga0c5f082017-10-19 13:35:19 -0700180 variables=variables,
181 # Always print the header if not printing to a TTY.
182 verbose=verbose or not setup_color.IS_TTY)
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200183
184 @property
185 def action(self):
186 return self._action
187
188 @property
189 def pattern(self):
190 return self._pattern
191
192 @property
193 def name(self):
194 return self._name
195
Paweł Hajdan, Jrecf53fe2017-09-29 18:28:49 +0200196 @property
197 def condition(self):
198 return self._condition
199
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200200 def matches(self, file_list):
201 """Returns true if the pattern matches any of files in the list."""
202 if not self._pattern:
203 return True
204 pattern = re.compile(self._pattern)
205 return bool([f for f in file_list if pattern.search(f)])
206
207 def run(self, root):
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200208 """Executes the hook's command (provided the condition is met)."""
209 if (self._condition and
210 not gclient_eval.EvaluateCondition(self._condition, self._variables)):
211 return
212
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200213 cmd = [arg.format(**self._variables) for arg in self._action]
214
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200215 if cmd[0] == 'python':
216 # If the hook specified "python" as the first item, the action is a
217 # Python script. Run it by starting a new copy of the same
218 # interpreter.
219 cmd[0] = sys.executable
Nodir Turakulov0ffcc872017-11-09 16:44:58 -0800220 elif cmd[0] == 'vpython' and _detect_host_os() == 'win':
221 cmd[0] += '.bat'
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +0200222
223 cwd = root
224 if self._cwd:
225 cwd = os.path.join(cwd, self._cwd)
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200226 try:
227 start_time = time.time()
228 gclient_utils.CheckCallAndFilterAndHeader(
Daniel Chenga0c5f082017-10-19 13:35:19 -0700229 cmd, cwd=cwd, always=self._verbose)
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +0200230 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
231 # Use a discrete exit status code of 2 to indicate that a hook action
232 # failed. Users of this script may wish to treat hook action failures
233 # differently from VC failures.
234 print('Error: %s' % str(e), file=sys.stderr)
235 sys.exit(2)
236 finally:
237 elapsed_time = time.time() - start_time
238 if elapsed_time > 10:
239 print("Hook '%s' took %.2f secs" % (
240 gclient_utils.CommandToStr(cmd), elapsed_time))
241
242
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200243class DependencySettings(object):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000244 """Immutable configuration settings."""
245 def __init__(
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200246 self, parent, raw_url, url, managed, custom_deps, custom_vars,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200247 custom_hooks, deps_file, should_process, relative,
248 condition, condition_value):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000249 # These are not mutable:
250 self._parent = parent
mmoss@chromium.org8f93f792014-08-26 23:24:09 +0000251 self._deps_file = deps_file
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200252 self._raw_url = raw_url
maruel@chromium.org064186c2011-09-27 23:53:33 +0000253 self._url = url
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200254 # The condition as string (or None). Useful to keep e.g. for flatten.
255 self._condition = condition
256 # Boolean value of the condition. If there's no condition, just True.
257 self._condition_value = condition_value
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000258 # 'managed' determines whether or not this dependency is synced/updated by
259 # gclient after gclient checks it out initially. The difference between
260 # 'managed' and 'should_process' is that the user specifies 'managed' via
smutae7ea312016-07-18 11:59:41 -0700261 # the --unmanaged command-line flag or a .gclient config, where
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000262 # 'should_process' is dynamically set by gclient if it goes over its
263 # recursion limit and controls gclient's behavior so it does not misbehave.
264 self._managed = managed
265 self._should_process = should_process
agabledce6ddc2016-09-08 10:02:16 -0700266 # If this is a recursed-upon sub-dependency, and the parent has
267 # use_relative_paths set, then this dependency should check out its own
268 # dependencies relative to that parent's path for this, rather than
269 # relative to the .gclient file.
270 self._relative = relative
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000271 # This is a mutable value which has the list of 'target_os' OSes listed in
272 # the current deps file.
273 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000274
275 # These are only set in .gclient and not in DEPS files.
276 self._custom_vars = custom_vars or {}
277 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000278 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000279
maruel@chromium.org064186c2011-09-27 23:53:33 +0000280 # Post process the url to remove trailing slashes.
Edward Lemure7273d22018-05-10 19:13:51 -0400281 if isinstance(self.url, basestring):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000282 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
283 # it to proto://host/path@rev.
Edward Lemure7273d22018-05-10 19:13:51 -0400284 self.set_url(self.url.replace('/@', '@'))
285 elif not isinstance(self.url, (None.__class__)):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000286 raise gclient_utils.Error(
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200287 ('dependency url must be either string or None, '
Edward Lemure7273d22018-05-10 19:13:51 -0400288 'instead of %s') % self.url.__class__.__name__)
289
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000290 # Make any deps_file path platform-appropriate.
John Budorick0f7b2002018-01-19 15:46:17 -0800291 if self._deps_file:
292 for sep in ['/', '\\']:
293 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000294
295 @property
296 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000297 return self._deps_file
298
299 @property
300 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000301 return self._managed
302
303 @property
304 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000305 return self._parent
306
307 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000308 def root(self):
309 """Returns the root node, a GClient object."""
310 if not self.parent:
311 # This line is to signal pylint that it could be a GClient instance.
312 return self or GClient(None, None)
313 return self.parent.root
314
315 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000316 def should_process(self):
317 """True if this dependency should be processed, i.e. checked out."""
318 return self._should_process
319
320 @property
321 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000322 return self._custom_vars.copy()
323
324 @property
325 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000326 return self._custom_deps.copy()
327
maruel@chromium.org064186c2011-09-27 23:53:33 +0000328 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000329 def custom_hooks(self):
330 return self._custom_hooks[:]
331
332 @property
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200333 def raw_url(self):
334 """URL before variable expansion."""
335 return self._raw_url
336
337 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000338 def url(self):
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200339 """URL after variable expansion."""
maruel@chromium.org064186c2011-09-27 23:53:33 +0000340 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000341
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000342 @property
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200343 def condition(self):
344 return self._condition
345
346 @property
347 def condition_value(self):
348 return self._condition_value
349
350 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000351 def target_os(self):
352 if self.local_target_os is not None:
353 return tuple(set(self.local_target_os).union(self.parent.target_os))
354 else:
355 return self.parent.target_os
356
Tom Andersonc31ae0b2018-02-06 14:48:56 -0800357 @property
358 def target_cpu(self):
359 return self.parent.target_cpu
360
Edward Lemure7273d22018-05-10 19:13:51 -0400361 def set_url(self, url):
362 self._url = url
363
364 def set_raw_url(self, url):
365 self._raw_url = url
366
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000367 def get_custom_deps(self, name, url):
368 """Returns a custom deps if applicable."""
369 if self.parent:
370 url = self.parent.get_custom_deps(name, url)
371 # None is a valid return value to disable a dependency.
372 return self.custom_deps.get(name, url)
373
maruel@chromium.org064186c2011-09-27 23:53:33 +0000374
375class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000376 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000377
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200378 def __init__(self, parent, name, raw_url, url, managed, custom_deps,
agabledce6ddc2016-09-08 10:02:16 -0700379 custom_vars, custom_hooks, deps_file, should_process,
Edward Lemur231f5ea2018-01-31 19:02:36 +0100380 relative, condition, condition_value, print_outbuf=False):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000381 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000382 DependencySettings.__init__(
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200383 self, parent, raw_url, url, managed, custom_deps, custom_vars,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200384 custom_hooks, deps_file, should_process, relative,
385 condition, condition_value)
maruel@chromium.org68988972011-09-20 14:11:42 +0000386
387 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000388 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000389
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000390 self._pre_deps_hooks = []
391
maruel@chromium.org68988972011-09-20 14:11:42 +0000392 # Calculates properties:
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000393 self._dependencies = []
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200394 self._vars = {}
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +0200395 self._os_dependencies = {}
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +0200396 self._os_deps_hooks = {}
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200397
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000398 # A cache of the files affected by the current operation, necessary for
399 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000400 self._file_list = []
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000401 # List of host names from which dependencies are allowed.
402 # Default is an empty set, meaning unspecified in DEPS file, and hence all
403 # hosts will be allowed. Non-empty set means whitelist of hosts.
404 # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
405 self._allowed_hosts = frozenset()
Michael Moss848c86e2018-05-03 16:05:50 -0700406 self._gn_args_from = None
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200407 # Spec for .gni output to write (if any).
408 self._gn_args_file = None
409 self._gn_args = []
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000410 # If it is not set to True, the dependency wasn't processed for its child
411 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000412 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000413 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000414 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000415 # This dependency had its pre-DEPS hooks run
416 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000417 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000418 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000419 # This is the scm used to checkout self.url. It may be used by dependencies
420 # to get the datetime of the revision we checked out.
421 self._used_scm = None
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000422 self._used_revision = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000423 # The actual revision we ended up getting, or None if that information is
424 # unavailable
425 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000426
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000427 # This is a mutable value that overrides the normal recursion limit for this
428 # dependency. It is read from the actual DEPS file so cannot be set on
429 # class instantiation.
430 self.recursion_override = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000431 # recursedeps is a mutable value that selectively overrides the default
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000432 # 'no recursion' setting on a dep-by-dep basis. It will replace
433 # recursion_override.
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000434 #
435 # It will be a dictionary of {deps_name: {"deps_file": depfile_name}} or
436 # None.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000437 self.recursedeps = None
Edward Lemure7273d22018-05-10 19:13:51 -0400438
439 self._OverrideUrl()
hinoka885e5b12016-06-08 14:40:09 -0700440 # This is inherited from WorkItem. We want the URL to be a resource.
Edward Lemure7273d22018-05-10 19:13:51 -0400441 if self.url and isinstance(self.url, basestring):
hinoka885e5b12016-06-08 14:40:09 -0700442 # The url is usually given to gclient either as https://blah@123
qyearsley12fa6ff2016-08-24 09:18:40 -0700443 # or just https://blah. The @123 portion is irrelevant.
Edward Lemure7273d22018-05-10 19:13:51 -0400444 self.resources.append(self.url.split('@')[0])
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000445
Edward Lemur231f5ea2018-01-31 19:02:36 +0100446 # Controls whether we want to print git's output when we first clone the
447 # dependency
448 self.print_outbuf = print_outbuf
449
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000450 if not self.name and self.parent:
451 raise gclient_utils.Error('Dependency without name')
452
Edward Lemure7273d22018-05-10 19:13:51 -0400453 def _OverrideUrl(self):
454 """Resolves the parsed url from the parent hierarchy."""
455 parsed_url = self.get_custom_deps(self._name, self.url)
456 if parsed_url != self.url:
457 logging.info('Dependency(%s)._OverrideUrl(%s) -> %s', self._name,
458 self.url, parsed_url)
459 self.set_url(parsed_url)
460
461 elif isinstance(self.url, basestring):
462 parsed_url = urlparse.urlparse(self.url)
463 if (not parsed_url[0] and
464 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
465 path = parsed_url[2]
466 if not path.startswith('/'):
467 raise gclient_utils.Error(
468 'relative DEPS entry \'%s\' must begin with a slash' % self.url)
469 # A relative url. Get the parent url, strip from the last '/'
470 # (equivalent to unix basename), and append the relative url.
471 parent_url = self.parent.url
472 parsed_url = parent_url[:parent_url.rfind('/')] + self.url
473 logging.info('Dependency(%s)._OverrideUrl(%s) -> %s', self.name,
474 self.url, parsed_url)
475 self.set_url(parsed_url)
476
477 elif self.url is None:
478 logging.info('Dependency(%s)._OverrideUrl(None) -> None', self._name)
479
480 else:
481 raise gclient_utils.Error('Unknown url type')
482
483 def PinToActualRevision(self):
484 """Updates self.url and self.raw_url to the revision checked out on disk."""
485 if self.url is None:
486 return
487 url = raw_url = None
488 scm = self.CreateSCM(self.url, self.root.root_dir, self.name, self.outbuf)
489 if os.path.isdir(scm.checkout_path):
490 revision = scm.revinfo(None, None, None)
491 url = '%s@%s' % (gclient_utils.SplitUrlRevision(self.url)[0], revision)
492 raw_url = '%s@%s' % (
493 gclient_utils.SplitUrlRevision(self.raw_url)[0], revision)
494 self.set_url(url)
495 self.set_raw_url(raw_url)
496
John Budorick0f7b2002018-01-19 15:46:17 -0800497 def ToLines(self):
498 s = []
499 condition_part = ([' "condition": %r,' % self.condition]
500 if self.condition else [])
501 s.extend([
502 ' # %s' % self.hierarchy(include_url=False),
503 ' "%s": {' % (self.name,),
504 ' "url": "%s",' % (self.raw_url,),
505 ] + condition_part + [
506 ' },',
507 '',
508 ])
509 return s
510
maruel@chromium.org470b5432011-10-11 18:18:19 +0000511 @property
512 def requirements(self):
513 """Calculate the list of requirements."""
514 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000515 # self.parent is implicitly a requirement. This will be recursive by
516 # definition.
517 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000518 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000519
520 # For a tree with at least 2 levels*, the leaf node needs to depend
521 # on the level higher up in an orderly way.
522 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
523 # thus unsorted, while the .gclient format is a list thus sorted.
524 #
525 # * _recursion_limit is hard coded 2 and there is no hope to change this
526 # value.
527 #
528 # Interestingly enough, the following condition only works in the case we
529 # want: self is a 2nd level node. 3nd level node wouldn't need this since
530 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000531 if self.parent and self.parent.parent and not self.parent.parent.parent:
532 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000533
maruel@chromium.org470b5432011-10-11 18:18:19 +0000534 if self.name:
535 requirements |= set(
536 obj.name for obj in self.root.subtree(False)
537 if (obj is not self
538 and obj.name and
539 self.name.startswith(posixpath.join(obj.name, ''))))
540 requirements = tuple(sorted(requirements))
541 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
542 return requirements
543
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000544 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000545 def try_recursedeps(self):
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000546 """Returns False if recursion_override is ever specified."""
547 if self.recursion_override is not None:
548 return False
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000549 return self.parent.try_recursedeps
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000550
551 @property
552 def recursion_limit(self):
553 """Returns > 0 if this dependency is not too recursed to be processed."""
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000554 # We continue to support the absence of recursedeps until tools and DEPS
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000555 # using recursion_override are updated.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000556 if self.try_recursedeps and self.parent.recursedeps != None:
557 if self.name in self.parent.recursedeps:
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000558 return 1
559
560 if self.recursion_override is not None:
561 return self.recursion_override
562 return max(self.parent.recursion_limit - 1, 0)
563
maruel@chromium.org470b5432011-10-11 18:18:19 +0000564 def verify_validity(self):
565 """Verifies that this Dependency is fine to add as a child of another one.
566
567 Returns True if this entry should be added, False if it is a duplicate of
568 another entry.
569 """
570 logging.info('Dependency(%s).verify_validity()' % self.name)
571 if self.name in [s.name for s in self.parent.dependencies]:
572 raise gclient_utils.Error(
573 'The same name "%s" appears multiple times in the deps section' %
574 self.name)
575 if not self.should_process:
576 # Return early, no need to set requirements.
577 return True
578
579 # This require a full tree traversal with locks.
580 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
581 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000582 # Allow to have only one to be None or ''.
Edward Lemure7273d22018-05-10 19:13:51 -0400583 if self.url != sibling.url and bool(self.url) == bool(sibling.url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000584 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000585 ('Dependency %s specified more than once:\n'
586 ' %s [%s]\n'
587 'vs\n'
588 ' %s [%s]') % (
589 self.name,
590 sibling.hierarchy(),
Edward Lemure7273d22018-05-10 19:13:51 -0400591 sibling.url,
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000592 self.hierarchy(),
Edward Lemure7273d22018-05-10 19:13:51 -0400593 self.url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000594 # In theory we could keep it as a shadow of the other one. In
595 # practice, simply ignore it.
596 logging.warn('Won\'t process duplicate dependency %s' % sibling)
597 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000598 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000599
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000600 @staticmethod
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +0200601 def MergeWithOsDeps(deps, deps_os, target_os_list, process_all_deps):
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000602 """Returns a new "deps" structure that is the deps sent in updated
603 with information from deps_os (the deps_os section of the DEPS
604 file) that matches the list of target os."""
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000605 new_deps = deps.copy()
Paweł Hajdan, Jrfd0057e2017-06-21 14:20:21 +0200606 for dep_os, os_deps in deps_os.iteritems():
607 for key, value in os_deps.iteritems():
608 if value is None:
609 # Make this condition very visible, so it's not a silent failure.
610 # It's unclear how to support None override in deps_os.
611 logging.error('Ignoring %r:%r in %r deps_os', key, value, dep_os)
612 continue
613
614 # Normalize value to be a dict which contains |should_process| metadata.
615 if isinstance(value, basestring):
616 value = {'url': value}
617 assert isinstance(value, collections.Mapping), (key, value)
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +0200618 value['should_process'] = dep_os in target_os_list or process_all_deps
Paweł Hajdan, Jrfd0057e2017-06-21 14:20:21 +0200619
620 # Handle collisions/overrides.
621 if key in new_deps and new_deps[key] != value:
622 # Normalize the existing new_deps entry.
623 if isinstance(new_deps[key], basestring):
624 new_deps[key] = {'url': new_deps[key]}
625 assert isinstance(new_deps[key],
626 collections.Mapping), (key, new_deps[key])
627
628 # It's OK if the "override" sets the key to the same value.
629 # This is mostly for legacy reasons to keep existing DEPS files
630 # working. Often mac/ios and unix/android will do this.
631 if value['url'] != new_deps[key]['url']:
632 raise gclient_utils.Error(
633 ('Value from deps_os (%r; %r: %r) conflicts with existing deps '
634 'entry (%r).') % (dep_os, key, value, new_deps[key]))
635
636 # We'd otherwise overwrite |should_process| metadata, but a dep should
637 # be processed if _any_ of its references call for that.
638 value['should_process'] = (
639 value['should_process'] or
640 new_deps[key].get('should_process', True))
641
642 new_deps[key] = value
643
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000644 return new_deps
645
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200646 def _postprocess_deps(self, deps, rel_prefix):
647 """Performs post-processing of deps compared to what's in the DEPS file."""
Paweł Hajdan, Jr4426eaf2017-06-13 12:25:47 +0200648 # Make sure the dict is mutable, e.g. in case it's frozen.
649 deps = dict(deps)
650
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200651 # If a line is in custom_deps, but not in the solution, we want to append
652 # this line to the solution.
653 for d in self.custom_deps:
654 if d not in deps:
655 deps[d] = self.custom_deps[d]
Michael Moss42d02c22018-02-05 10:32:24 -0800656 # Make child deps conditional on any parent conditions. This ensures that,
657 # when flattened, recursed entries have the correct restrictions, even if
658 # not explicitly set in the recursed DEPS file. For instance, if
659 # "src/ios_foo" is conditional on "checkout_ios=True", then anything
660 # recursively included by "src/ios_foo/DEPS" should also require
661 # "checkout_ios=True".
662 if self.condition:
663 for dname, dval in deps.iteritems():
664 if isinstance(dval, basestring):
665 dval = {'url': dval}
666 deps[dname] = dval
667 else:
668 assert isinstance(dval, collections.Mapping)
669 if dval.get('condition'):
670 dval['condition'] = '(%s) and (%s)' % (
671 dval['condition'], self.condition)
672 else:
673 dval['condition'] = self.condition
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200674
675 if rel_prefix:
676 logging.warning('use_relative_paths enabled.')
677 rel_deps = {}
678 for d, url in deps.items():
679 # normpath is required to allow DEPS to use .. in their
680 # dependency local path.
681 rel_deps[os.path.normpath(os.path.join(rel_prefix, d))] = url
682 logging.warning('Updating deps by prepending %s.', rel_prefix)
683 deps = rel_deps
684
685 return deps
686
687 def _deps_to_objects(self, deps, use_relative_paths):
688 """Convert a deps dict to a dict of Dependency objects."""
689 deps_to_add = []
690 for name, dep_value in deps.iteritems():
691 should_process = self.recursion_limit and self.should_process
692 deps_file = self.deps_file
693 if self.recursedeps is not None:
694 ent = self.recursedeps.get(name)
695 if ent is not None:
696 deps_file = ent['deps_file']
697 if dep_value is None:
698 continue
John Budorick0f7b2002018-01-19 15:46:17 -0800699
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200700 condition = None
701 condition_value = True
702 if isinstance(dep_value, basestring):
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200703 raw_url = dep_value
John Budorick0f7b2002018-01-19 15:46:17 -0800704 dep_type = None
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200705 else:
706 # This should be guaranteed by schema checking in gclient_eval.
707 assert isinstance(dep_value, collections.Mapping)
John Budorick0f7b2002018-01-19 15:46:17 -0800708 raw_url = dep_value.get('url')
Paweł Hajdan, Jrfd0057e2017-06-21 14:20:21 +0200709 # Take into account should_process metadata set by MergeWithOsDeps.
710 should_process = (should_process and
711 dep_value.get('should_process', True))
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200712 condition = dep_value.get('condition')
John Budorick0f7b2002018-01-19 15:46:17 -0800713 dep_type = dep_value.get('dep_type')
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200714
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200715 if condition:
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +0200716 condition_value = gclient_eval.EvaluateCondition(
717 condition, self.get_vars())
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +0200718 if not self._get_option('process_all_deps', False):
719 should_process = should_process and condition_value
John Budorick0f7b2002018-01-19 15:46:17 -0800720
721 if dep_type == 'cipd':
John Budorickd3ba72b2018-03-20 12:27:42 -0700722 cipd_root = self.GetCipdRoot()
John Budorick0f7b2002018-01-19 15:46:17 -0800723 for package in dep_value.get('packages', []):
Shenghua Zhangf438ff72018-02-23 14:20:19 -0800724 if 'version' in package:
725 # Matches version to vars value.
726 raw_version = package['version']
727 version = raw_version.format(**self.get_vars())
728 package['version'] = version
John Budorick0f7b2002018-01-19 15:46:17 -0800729 deps_to_add.append(
730 CipdDependency(
731 self, name, package, cipd_root,
732 self.custom_vars, should_process, use_relative_paths,
733 condition, condition_value))
John Budorick0f7b2002018-01-19 15:46:17 -0800734 else:
Michael Moss012013e2018-03-30 17:03:19 -0700735 url = raw_url.format(**self.get_vars()) if raw_url else None
John Budorick0f7b2002018-01-19 15:46:17 -0800736 deps_to_add.append(
Edward Lemurb61d3872018-05-09 18:42:47 -0400737 GitDependency(
John Budorick0f7b2002018-01-19 15:46:17 -0800738 self, name, raw_url, url, None, None, self.custom_vars, None,
739 deps_file, should_process, use_relative_paths, condition,
740 condition_value))
741
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200742 deps_to_add.sort(key=lambda x: x.name)
743 return deps_to_add
744
Edward Lesmes6c24d372018-03-28 12:52:29 -0400745 def ParseDepsFile(self, expand_vars=True):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000746 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000747 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000748 assert not self.dependencies
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000749
750 deps_content = None
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000751
752 # First try to locate the configured deps file. If it's missing, fallback
753 # to DEPS.
754 deps_files = [self.deps_file]
755 if 'DEPS' not in deps_files:
756 deps_files.append('DEPS')
757 for deps_file in deps_files:
758 filepath = os.path.join(self.root.root_dir, self.name, deps_file)
759 if os.path.isfile(filepath):
760 logging.info(
761 'ParseDepsFile(%s): %s file found at %s', self.name, deps_file,
762 filepath)
763 break
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000764 logging.info(
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000765 'ParseDepsFile(%s): No %s file found at %s', self.name, deps_file,
766 filepath)
767
768 if os.path.isfile(filepath):
maruel@chromium.org46304292010-10-28 11:42:00 +0000769 deps_content = gclient_utils.FileRead(filepath)
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000770 logging.debug('ParseDepsFile(%s) read:\n%s', self.name, deps_content)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000771
772 local_scope = {}
773 if deps_content:
maruel@chromium.org46304292010-10-28 11:42:00 +0000774 try:
Edward Lesmes6c24d372018-03-28 12:52:29 -0400775 local_scope = gclient_eval.Parse(
776 deps_content, expand_vars,
777 self._get_option('validate_syntax', False),
Michael Mossda55cdc2018-04-06 18:37:19 -0700778 filepath, self.get_vars())
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000779 except SyntaxError as e:
maruel@chromium.org46304292010-10-28 11:42:00 +0000780 gclient_utils.SyntaxErrorToError(filepath, e)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000781
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000782 if 'allowed_hosts' in local_scope:
783 try:
784 self._allowed_hosts = frozenset(local_scope.get('allowed_hosts'))
785 except TypeError: # raised if non-iterable
786 pass
787 if not self._allowed_hosts:
788 logging.warning("allowed_hosts is specified but empty %s",
789 self._allowed_hosts)
790 raise gclient_utils.Error(
791 'ParseDepsFile(%s): allowed_hosts must be absent '
792 'or a non-empty iterable' % self.name)
793
Michael Moss848c86e2018-05-03 16:05:50 -0700794 self._gn_args_from = local_scope.get('gclient_gn_args_from')
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200795 self._gn_args_file = local_scope.get('gclient_gn_args_file')
796 self._gn_args = local_scope.get('gclient_gn_args', [])
Michael Moss848c86e2018-05-03 16:05:50 -0700797 # It doesn't make sense to set all of these, since setting gn_args_from to
798 # another DEPS will make gclient ignore any other local gn_args* settings.
799 assert not (self._gn_args_from and self._gn_args_file), \
800 'Only specify one of "gclient_gn_args_from" or ' \
801 '"gclient_gn_args_file + gclient_gn_args".'
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +0200802
Edward Lesmes0b899352018-03-19 21:59:55 +0000803 self._vars = local_scope.get('vars', {})
Paweł Hajdan, Jr1407d002017-08-01 20:01:01 +0200804 if self.parent:
805 for key, value in self.parent.get_vars().iteritems():
806 if key in self._vars:
807 self._vars[key] = value
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200808 # Since we heavily post-process things, freeze ones which should
809 # reflect original state of DEPS.
Paweł Hajdan, Jr1407d002017-08-01 20:01:01 +0200810 self._vars = gclient_utils.freeze(self._vars)
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200811
812 # If use_relative_paths is set in the DEPS file, regenerate
813 # the dictionary using paths relative to the directory containing
814 # the DEPS file. Also update recursedeps if use_relative_paths is
815 # enabled.
816 # If the deps file doesn't set use_relative_paths, but the parent did
817 # (and therefore set self.relative on this Dependency object), then we
818 # want to modify the deps and recursedeps by prepending the parent
819 # directory of this dependency.
820 use_relative_paths = local_scope.get('use_relative_paths', False)
821 rel_prefix = None
822 if use_relative_paths:
823 rel_prefix = self.name
824 elif self._relative:
825 rel_prefix = os.path.dirname(self.name)
826
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +0200827 deps = {}
828 for key, value in local_scope.get('deps', {}).iteritems():
829 deps[key.format(**self.get_vars())] = value
830
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200831 if 'recursion' in local_scope:
832 self.recursion_override = local_scope.get('recursion')
833 logging.warning(
834 'Setting %s recursion to %d.', self.name, self.recursion_limit)
835 self.recursedeps = None
836 if 'recursedeps' in local_scope:
837 self.recursedeps = {}
838 for ent in local_scope['recursedeps']:
839 if isinstance(ent, basestring):
840 self.recursedeps[ent] = {"deps_file": self.deps_file}
841 else: # (depname, depsfilename)
842 self.recursedeps[ent[0]] = {"deps_file": ent[1]}
843 logging.warning('Found recursedeps %r.', repr(self.recursedeps))
844
845 if rel_prefix:
846 logging.warning('Updating recursedeps by prepending %s.', rel_prefix)
847 rel_deps = {}
848 for depname, options in self.recursedeps.iteritems():
849 rel_deps[
850 os.path.normpath(os.path.join(rel_prefix, depname))] = options
851 self.recursedeps = rel_deps
Michael Moss848c86e2018-05-03 16:05:50 -0700852 # To get gn_args from another DEPS, that DEPS must be recursed into.
853 if self._gn_args_from:
854 assert self.recursedeps and self._gn_args_from in self.recursedeps, \
855 'The "gclient_gn_args_from" value must be in recursedeps.'
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200856
857 # If present, save 'target_os' in the local_target_os property.
858 if 'target_os' in local_scope:
859 self.local_target_os = local_scope['target_os']
860 # load os specific dependencies if defined. these dependencies may
861 # override or extend the values defined by the 'deps' member.
862 target_os_list = self.target_os
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +0200863 if 'deps_os' in local_scope:
864 for dep_os, os_deps in local_scope['deps_os'].iteritems():
865 self._os_dependencies[dep_os] = self._deps_to_objects(
866 self._postprocess_deps(os_deps, rel_prefix), use_relative_paths)
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200867 if target_os_list and not self._get_option(
868 'do_not_merge_os_specific_entries', False):
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +0200869 deps = self.MergeWithOsDeps(
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +0200870 deps, local_scope['deps_os'], target_os_list,
871 self._get_option('process_all_deps', False))
Paweł Hajdan, Jrcd788e32017-06-12 18:42:22 +0200872
873 deps_to_add = self._deps_to_objects(
874 self._postprocess_deps(deps, rel_prefix), use_relative_paths)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000875
876 # override named sets of hooks by the custom hooks
877 hooks_to_run = []
878 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
879 for hook in local_scope.get('hooks', []):
880 if hook.get('name', '') not in hook_names_to_suppress:
881 hooks_to_run.append(hook)
Scott Grahamc4826742017-05-11 16:59:23 -0700882 if 'hooks_os' in local_scope and target_os_list:
883 hooks_os = local_scope['hooks_os']
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +0200884
885 # Keep original contents of hooks_os for flatten.
886 for hook_os, os_hooks in hooks_os.iteritems():
887 self._os_deps_hooks[hook_os] = [
Michael Moss42d02c22018-02-05 10:32:24 -0800888 Hook.from_dict(hook, variables=self.get_vars(), verbose=True,
889 conditions=self.condition)
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +0200890 for hook in os_hooks]
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +0200891
Scott Grahamc4826742017-05-11 16:59:23 -0700892 # Specifically append these to ensure that hooks_os run after hooks.
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200893 if not self._get_option('do_not_merge_os_specific_entries', False):
894 for the_target_os in target_os_list:
895 the_target_os_hooks = hooks_os.get(the_target_os, [])
896 hooks_to_run.extend(the_target_os_hooks)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000897
898 # add the replacements and any additions
899 for hook in self.custom_hooks:
900 if 'action' in hook:
901 hooks_to_run.append(hook)
902
Dirk Prankeda3a29e2017-02-27 15:29:36 -0800903 if self.recursion_limit:
Paweł Hajdan, Jr032d5452017-06-22 20:43:53 +0200904 self._pre_deps_hooks = [
Michael Moss42d02c22018-02-05 10:32:24 -0800905 Hook.from_dict(hook, variables=self.get_vars(), verbose=True,
906 conditions=self.condition)
Daniel Chenga0c5f082017-10-19 13:35:19 -0700907 for hook in local_scope.get('pre_deps_hooks', [])
908 ]
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000909
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200910 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000911 logging.info('ParseDepsFile(%s) done' % self.name)
912
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +0200913 def _get_option(self, attr, default):
914 obj = self
915 while not hasattr(obj, '_options'):
916 obj = obj.parent
917 return getattr(obj._options, attr, default)
918
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +0200919 def add_dependencies_and_close(self, deps_to_add, hooks):
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000920 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000921 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000922 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000923 self.add_dependency(dep)
Daniel Chenga0c5f082017-10-19 13:35:19 -0700924 self._mark_as_parsed([
925 Hook.from_dict(
Michael Moss42d02c22018-02-05 10:32:24 -0800926 h, variables=self.get_vars(), verbose=self.root._options.verbose,
927 conditions=self.condition)
Daniel Chenga0c5f082017-10-19 13:35:19 -0700928 for h in hooks
929 ])
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000930
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000931 def findDepsFromNotAllowedHosts(self):
932 """Returns a list of depenecies from not allowed hosts.
933
934 If allowed_hosts is not set, allows all hosts and returns empty list.
935 """
936 if not self._allowed_hosts:
937 return []
938 bad_deps = []
939 for dep in self._dependencies:
szager@chromium.orgbd772dd2014-11-05 18:43:08 +0000940 # Don't enforce this for custom_deps.
941 if dep.name in self._custom_deps:
942 continue
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000943 if isinstance(dep.url, basestring):
944 parsed_url = urlparse.urlparse(dep.url)
945 if parsed_url.netloc and parsed_url.netloc not in self._allowed_hosts:
946 bad_deps.append(dep)
947 return bad_deps
948
Edward Lemure7273d22018-05-10 19:13:51 -0400949 def FuzzyMatchUrl(self, candidates):
Edward Lesmesbb16e332018-03-30 17:54:51 -0400950 """Attempts to find this dependency in the list of candidates.
951
Edward Lemure7273d22018-05-10 19:13:51 -0400952 It looks first for the URL of this dependency in the list of
Edward Lesmesbb16e332018-03-30 17:54:51 -0400953 candidates. If it doesn't succeed, and the URL ends in '.git', it will try
954 looking for the URL minus '.git'. Finally it will try to look for the name
955 of the dependency.
956
957 Args:
Edward Lesmesbb16e332018-03-30 17:54:51 -0400958 candidates: list, dict. The list of candidates in which to look for this
959 dependency. It can contain URLs as above, or dependency names like
960 "src/some/dep".
961
962 Returns:
963 If this dependency is not found in the list of candidates, returns None.
964 Otherwise, it returns under which name did we find this dependency:
965 - Its parsed url: "https://example.com/src.git'
966 - Its parsed url minus '.git': "https://example.com/src"
967 - Its name: "src"
968 """
Edward Lemure7273d22018-05-10 19:13:51 -0400969 if self.url:
970 origin, _ = gclient_utils.SplitUrlRevision(self.url)
Edward Lesmesbb16e332018-03-30 17:54:51 -0400971 if origin in candidates:
972 return origin
973 if origin.endswith('.git') and origin[:-len('.git')] in candidates:
974 return origin[:-len('.git')]
Edward Lesmes990148e2018-04-26 14:56:55 -0400975 if origin + '.git' in candidates:
976 return origin + '.git'
Edward Lesmesbb16e332018-03-30 17:54:51 -0400977 if self.name in candidates:
978 return self.name
979 return None
980
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000981 # Arguments number differs from overridden method
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800982 # pylint: disable=arguments-differ
Edward Lesmesc621b212018-03-21 20:26:56 -0400983 def run(self, revision_overrides, command, args, work_queue, options,
984 patch_refs):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000985 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000986 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000987 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000988 if not self.should_process:
989 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000990 # When running runhooks, there's no need to consult the SCM.
991 # All known hooks are expected to run unconditionally regardless of working
992 # copy state, so skip the SCM status check.
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +0200993 run_scm = command not in (
994 'flatten', 'runhooks', 'recurse', 'validate', None)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000995 file_list = [] if not options.nohooks else None
Edward Lesmesbb16e332018-03-30 17:54:51 -0400996 revision_override = revision_overrides.pop(
Edward Lemure7273d22018-05-10 19:13:51 -0400997 self.FuzzyMatchUrl(revision_overrides), None)
998 if run_scm and self.url:
agabled437d762016-10-17 09:35:11 -0700999 # Create a shallow copy to mutate revision.
1000 options = copy.copy(options)
1001 options.revision = revision_override
1002 self._used_revision = options.revision
John Budorick0f7b2002018-01-19 15:46:17 -08001003 self._used_scm = self.CreateSCM(
Edward Lemure7273d22018-05-10 19:13:51 -04001004 self.url, self.root.root_dir, self.name, self.outbuf,
agabled437d762016-10-17 09:35:11 -07001005 out_cb=work_queue.out_cb)
1006 self._got_revision = self._used_scm.RunCommand(command, options, args,
1007 file_list)
Edward Lesmesc621b212018-03-21 20:26:56 -04001008
Edward Lemure7273d22018-05-10 19:13:51 -04001009 patch_repo = self.url.split('@')[0]
1010 patch_ref = patch_refs.pop(self.FuzzyMatchUrl(patch_refs), None)
Edward Lesmesc621b212018-03-21 20:26:56 -04001011 if command == 'update' and patch_ref is not None:
1012 self._used_scm.apply_patch_ref(patch_repo, patch_ref, options,
Edward Lesmesbb16e332018-03-30 17:54:51 -04001013 file_list)
Edward Lesmesc621b212018-03-21 20:26:56 -04001014
agabled437d762016-10-17 09:35:11 -07001015 if file_list:
1016 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +00001017
1018 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
1019 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +00001020 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +00001021 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001022 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +00001023 continue
1024 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001025 [self.root.root_dir.lower(), file_list[i].lower()])
1026 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +00001027 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001028 while file_list[i].startswith(('\\', '/')):
1029 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001030
Edward Lesmes5d6cde32018-04-12 18:32:46 -04001031 if self.recursion_limit:
1032 self.ParseDepsFile(expand_vars=(command != 'flatten'))
1033
Edward Lemure7273d22018-05-10 19:13:51 -04001034 self._run_is_done(file_list or [])
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001035
1036 if self.recursion_limit:
Edward Lesmes5d6cde32018-04-12 18:32:46 -04001037 if command in ('update', 'revert') and not options.noprehooks:
1038 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001039 # Parse the dependencies of this dependency.
1040 for s in self.dependencies:
Paweł Hajdan, Jr4baaa112017-07-04 19:09:32 +02001041 if s.should_process:
1042 work_queue.enqueue(s)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001043
1044 if command == 'recurse':
agabled437d762016-10-17 09:35:11 -07001045 # Skip file only checkout.
Edward Lemure7273d22018-05-10 19:13:51 -04001046 scm = self.GetScmName(self.url)
agabled437d762016-10-17 09:35:11 -07001047 if not options.scm or scm in options.scm:
1048 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
1049 # Pass in the SCM type as an env variable. Make sure we don't put
1050 # unicode strings in the environment.
1051 env = os.environ.copy()
1052 if scm:
1053 env['GCLIENT_SCM'] = str(scm)
Edward Lemure7273d22018-05-10 19:13:51 -04001054 if self.url:
1055 env['GCLIENT_URL'] = str(self.url)
agabled437d762016-10-17 09:35:11 -07001056 env['GCLIENT_DEP_PATH'] = str(self.name)
1057 if options.prepend_dir and scm == 'git':
1058 print_stdout = False
1059 def filter_fn(line):
1060 """Git-specific path marshaling. It is optimized for git-grep."""
ilevy@chromium.org0233ac22012-11-28 20:27:02 +00001061
agabled437d762016-10-17 09:35:11 -07001062 def mod_path(git_pathspec):
1063 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
1064 modified_path = os.path.join(self.name, match.group(2))
1065 branch = match.group(1) or ''
1066 return '%s%s' % (branch, modified_path)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +00001067
agabled437d762016-10-17 09:35:11 -07001068 match = re.match('^Binary file ([^\0]+) matches$', line)
1069 if match:
1070 print('Binary file %s matches\n' % mod_path(match.group(1)))
1071 return
ilevy@chromium.org0233ac22012-11-28 20:27:02 +00001072
agabled437d762016-10-17 09:35:11 -07001073 items = line.split('\0')
1074 if len(items) == 2 and items[1]:
1075 print('%s : %s' % (mod_path(items[0]), items[1]))
1076 elif len(items) >= 2:
1077 # Multiple null bytes or a single trailing null byte indicate
1078 # git is likely displaying filenames only (such as with -l)
1079 print('\n'.join(mod_path(path) for path in items if path))
1080 else:
1081 print(line)
1082 else:
1083 print_stdout = True
1084 filter_fn = None
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001085
Edward Lemure7273d22018-05-10 19:13:51 -04001086 if self.url is None:
agabled437d762016-10-17 09:35:11 -07001087 print('Skipped omitted dependency %s' % cwd, file=sys.stderr)
1088 elif os.path.isdir(cwd):
1089 try:
1090 gclient_utils.CheckCallAndFilter(
1091 args, cwd=cwd, env=env, print_stdout=print_stdout,
1092 filter_fn=filter_fn,
1093 )
1094 except subprocess2.CalledProcessError:
1095 if not options.ignore:
1096 raise
1097 else:
1098 print('Skipped missing %s' % cwd, file=sys.stderr)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001099
John Budorick0f7b2002018-01-19 15:46:17 -08001100 def GetScmName(self, url):
Edward Lemurb61d3872018-05-09 18:42:47 -04001101 raise NotImplementedError()
John Budorick0f7b2002018-01-19 15:46:17 -08001102
1103 def CreateSCM(self, url, root_dir=None, relpath=None, out_fh=None,
1104 out_cb=None):
Edward Lemurb61d3872018-05-09 18:42:47 -04001105 raise NotImplementedError()
John Budorick0f7b2002018-01-19 15:46:17 -08001106
Dirk Pranke9f20d022017-10-11 18:36:54 -07001107 def HasGNArgsFile(self):
1108 return self._gn_args_file is not None
1109
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +02001110 def WriteGNArgsFile(self):
1111 lines = ['# Generated from %r' % self.deps_file]
Paweł Hajdan, Jrb495bf52017-09-25 19:33:50 +02001112 variables = self.get_vars()
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +02001113 for arg in self._gn_args:
Paweł Hajdan, Jre0214742017-09-28 12:21:01 +02001114 value = variables[arg]
1115 if isinstance(value, basestring):
1116 value = gclient_eval.EvaluateCondition(value, variables)
Paweł Hajdan, Jrb495bf52017-09-25 19:33:50 +02001117 lines.append('%s = %s' % (arg, ToGNString(value)))
Paweł Hajdan, Jr57253732017-06-06 23:49:11 +02001118 with open(os.path.join(self.root.root_dir, self._gn_args_file), 'w') as f:
1119 f.write('\n'.join(lines))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001120
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001121 @gclient_utils.lockedmethod
Edward Lemure7273d22018-05-10 19:13:51 -04001122 def _run_is_done(self, file_list):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001123 # Both these are kept for hooks that are run as a separate tree traversal.
1124 self._file_list = file_list
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001125 self._processed = True
1126
szager@google.comb9a78d32012-03-13 18:46:21 +00001127 def GetHooks(self, options):
1128 """Evaluates all hooks, and return them in a flat list.
1129
1130 RunOnDeps() must have been called before to load the DEPS.
1131 """
1132 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +00001133 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001134 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +00001135 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +00001136 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001137 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001138 if self.deps_hooks:
agabled437d762016-10-17 09:35:11 -07001139 # TODO(maruel): If the user is using git, then we don't know
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001140 # what files have changed so we always run all hooks. It'd be nice to fix
1141 # that.
1142 if (options.force or
Edward Lemure7273d22018-05-10 19:13:51 -04001143 self.GetScmName(self.url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001144 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02001145 result.extend(self.deps_hooks)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001146 else:
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02001147 for hook in self.deps_hooks:
1148 if hook.matches(self.file_list_and_children):
1149 result.append(hook)
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001150 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +00001151 result.extend(s.GetHooks(options))
1152 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001153
Daniel Chenga0c5f082017-10-19 13:35:19 -07001154 def RunHooksRecursively(self, options, progress):
szager@google.comb9a78d32012-03-13 18:46:21 +00001155 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +00001156 self._hooks_ran = True
Daniel Chenga0c5f082017-10-19 13:35:19 -07001157 hooks = self.GetHooks(options)
1158 if progress:
1159 progress._total = len(hooks)
1160 for hook in hooks:
Daniel Chenga0c5f082017-10-19 13:35:19 -07001161 if progress:
1162 progress.update(extra=hook.name or '')
Daniel Cheng93c5d602017-10-20 11:40:17 -07001163 hook.run(self.root.root_dir)
Daniel Chenga0c5f082017-10-19 13:35:19 -07001164 if progress:
1165 progress.end()
maruel@chromium.orgeaf61062010-07-07 18:42:39 +00001166
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001167 def RunPreDepsHooks(self):
1168 assert self.processed
1169 assert self.deps_parsed
1170 assert not self.pre_deps_hooks_ran
1171 assert not self.hooks_ran
1172 for s in self.dependencies:
1173 assert not s.processed
1174 self._pre_deps_hooks_ran = True
1175 for hook in self.pre_deps_hooks:
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02001176 hook.run(self.root.root_dir)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001177
John Budorickd3ba72b2018-03-20 12:27:42 -07001178 def GetCipdRoot(self):
1179 if self.root is self:
1180 # Let's not infinitely recurse. If this is root and isn't an
1181 # instance of GClient, do nothing.
1182 return None
1183 return self.root.GetCipdRoot()
1184
maruel@chromium.org0d812442010-08-10 12:41:08 +00001185 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001186 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001187 dependencies = self.dependencies
1188 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001189 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001190 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001191 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001192 for i in d.subtree(include_all):
1193 yield i
1194
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001195 @gclient_utils.lockedmethod
1196 def add_dependency(self, new_dep):
1197 self._dependencies.append(new_dep)
1198
1199 @gclient_utils.lockedmethod
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +02001200 def _mark_as_parsed(self, new_hooks):
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001201 self._deps_hooks.extend(new_hooks)
1202 self._deps_parsed = True
1203
maruel@chromium.org68988972011-09-20 14:11:42 +00001204 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001205 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +00001206 def dependencies(self):
1207 return tuple(self._dependencies)
1208
1209 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001210 @gclient_utils.lockedmethod
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +02001211 def os_dependencies(self):
1212 return dict(self._os_dependencies)
1213
1214 @property
1215 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001216 def deps_hooks(self):
1217 return tuple(self._deps_hooks)
1218
1219 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001220 @gclient_utils.lockedmethod
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02001221 def os_deps_hooks(self):
1222 return dict(self._os_deps_hooks)
1223
1224 @property
1225 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001226 def pre_deps_hooks(self):
1227 return tuple(self._pre_deps_hooks)
1228
1229 @property
1230 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001231 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +00001232 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +00001233 return self._deps_parsed
1234
1235 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001236 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001237 def processed(self):
1238 return self._processed
1239
1240 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001241 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001242 def pre_deps_hooks_ran(self):
1243 return self._pre_deps_hooks_ran
1244
1245 @property
1246 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001247 def hooks_ran(self):
1248 return self._hooks_ran
1249
1250 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001251 @gclient_utils.lockedmethod
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001252 def allowed_hosts(self):
1253 return self._allowed_hosts
1254
1255 @property
1256 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001257 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001258 return tuple(self._file_list)
1259
1260 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +00001261 def used_scm(self):
1262 """SCMWrapper instance for this dependency or None if not processed yet."""
1263 return self._used_scm
1264
1265 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001266 @gclient_utils.lockedmethod
1267 def got_revision(self):
1268 return self._got_revision
1269
1270 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001271 def file_list_and_children(self):
1272 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001273 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001274 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +00001275 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001276
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001277 def __str__(self):
1278 out = []
Edward Lemure7273d22018-05-10 19:13:51 -04001279 for i in ('name', 'url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001280 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001281 'processed', 'hooks_ran', 'deps_parsed', 'requirements',
1282 'allowed_hosts'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001283 # First try the native property if it exists.
1284 if hasattr(self, '_' + i):
1285 value = getattr(self, '_' + i, False)
1286 else:
1287 value = getattr(self, i, False)
1288 if value:
1289 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001290
1291 for d in self.dependencies:
1292 out.extend([' ' + x for x in str(d).splitlines()])
1293 out.append('')
1294 return '\n'.join(out)
1295
1296 def __repr__(self):
1297 return '%s: %s' % (self.name, self.url)
1298
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001299 def hierarchy(self, include_url=True):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +00001300 """Returns a human-readable hierarchical reference to a Dependency."""
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001301 def format_name(d):
1302 if include_url:
1303 return '%s(%s)' % (d.name, d.url)
1304 return d.name
1305 out = format_name(self)
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001306 i = self.parent
1307 while i and i.name:
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001308 out = '%s -> %s' % (format_name(i), out)
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001309 i = i.parent
1310 return out
1311
Michael Mossfe68c912018-03-22 19:19:35 -07001312 def hierarchy_data(self):
1313 """Returns a machine-readable hierarchical reference to a Dependency."""
1314 d = self
1315 out = []
1316 while d and d.name:
1317 out.insert(0, (d.name, d.url))
1318 d = d.parent
1319 return tuple(out)
1320
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +02001321 def get_vars(self):
1322 """Returns a dictionary of effective variable values
1323 (DEPS file contents with applied custom_vars overrides)."""
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001324 # Provide some built-in variables.
1325 result = {
Paweł Hajdan, Jrd325eb32017-10-03 17:43:37 +02001326 'checkout_android': 'android' in self.target_os,
Benjamin Pastene6fe29412018-01-23 15:35:58 -08001327 'checkout_chromeos': 'chromeos' in self.target_os,
Paweł Hajdan, Jrd325eb32017-10-03 17:43:37 +02001328 'checkout_fuchsia': 'fuchsia' in self.target_os,
1329 'checkout_ios': 'ios' in self.target_os,
1330 'checkout_linux': 'unix' in self.target_os,
1331 'checkout_mac': 'mac' in self.target_os,
1332 'checkout_win': 'win' in self.target_os,
1333 'host_os': _detect_host_os(),
Tom Andersonc31ae0b2018-02-06 14:48:56 -08001334
1335 'checkout_arm': 'arm' in self.target_cpu,
1336 'checkout_arm64': 'arm64' in self.target_cpu,
1337 'checkout_x86': 'x86' in self.target_cpu,
1338 'checkout_mips': 'mips' in self.target_cpu,
1339 'checkout_ppc': 'ppc' in self.target_cpu,
1340 'checkout_s390': 's390' in self.target_cpu,
1341 'checkout_x64': 'x64' in self.target_cpu,
1342 'host_cpu': detect_host_arch.HostArch(),
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001343 }
Michael Mossda55cdc2018-04-06 18:37:19 -07001344 # Variable precedence:
1345 # - built-in
1346 # - DEPS vars
1347 # - parents, from first to last
1348 # - custom_vars overrides
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001349 result.update(self._vars)
Michael Mossda55cdc2018-04-06 18:37:19 -07001350 if self.parent:
1351 parent_vars = self.parent.get_vars()
1352 result.update(parent_vars)
Paweł Hajdan, Jrd3790252017-07-03 21:06:24 +02001353 result.update(self.custom_vars or {})
1354 return result
1355
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001356
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001357_PLATFORM_MAPPING = {
1358 'cygwin': 'win',
1359 'darwin': 'mac',
1360 'linux2': 'linux',
1361 'win32': 'win',
Jaideep Bajwad05f3582017-09-11 12:31:48 -04001362 'aix6': 'aix',
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02001363}
1364
1365
1366def _detect_host_os():
1367 return _PLATFORM_MAPPING[sys.platform]
1368
1369
Edward Lemurb61d3872018-05-09 18:42:47 -04001370class GitDependency(Dependency):
1371 """A Dependency object that represents a single git checkout."""
1372
1373 #override
1374 def GetScmName(self, url):
1375 """Always 'git'."""
1376 del url
1377 return 'git'
1378
1379 #override
1380 def CreateSCM(self, url, root_dir=None, relpath=None, out_fh=None,
1381 out_cb=None):
1382 """Create a Wrapper instance suitable for handling this git dependency."""
1383 return gclient_scm.GitWrapper(url, root_dir, relpath, out_fh, out_cb,
1384 print_outbuf=self.print_outbuf)
1385
1386
1387class GClient(GitDependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001388 """Object that represent a gclient checkout. A tree of Dependency(), one per
1389 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001390
1391 DEPS_OS_CHOICES = {
Jaideep Bajwad05f3582017-09-11 12:31:48 -04001392 "aix6": "unix",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001393 "win32": "win",
1394 "win": "win",
1395 "cygwin": "win",
1396 "darwin": "mac",
1397 "mac": "mac",
1398 "unix": "unix",
1399 "linux": "unix",
1400 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +00001401 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +00001402 "android": "android",
Michael Mossc54fa812017-08-17 11:27:58 -07001403 "ios": "ios",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001404 }
1405
1406 DEFAULT_CLIENT_FILE_TEXT = ("""\
1407solutions = [
smutae7ea312016-07-18 11:59:41 -07001408 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001409 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001410 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001411 "managed" : %(managed)s,
smutae7ea312016-07-18 11:59:41 -07001412 "custom_deps" : {
1413 },
Paweł Hajdan, Jr3ba2a7c2017-10-04 19:24:46 +02001414 "custom_vars": %(custom_vars)r,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001415 },
1416]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001417cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001418""")
1419
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001420 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1421# Snapshot generated with gclient revinfo --snapshot
Edward Lesmesc2960242018-03-06 20:50:15 -05001422solutions = %(solution_list)s
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001423""")
1424
1425 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001426 # Do not change previous behavior. Only solution level and immediate DEPS
1427 # are processed.
1428 self._recursion_limit = 2
Edward Lemurb61d3872018-05-09 18:42:47 -04001429 GitDependency.__init__(self, None, None, None, None, True, None, None, None,
1430 'unused', True, None, None, True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001431 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001432 if options.deps_os:
1433 enforced_os = options.deps_os.split(',')
1434 else:
1435 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1436 if 'all' in enforced_os:
1437 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001438 self._enforced_os = tuple(set(enforced_os))
Tom Andersonc31ae0b2018-02-06 14:48:56 -08001439 self._enforced_cpu = detect_host_arch.HostArch(),
maruel@chromium.org271375b2010-06-23 19:17:38 +00001440 self._root_dir = root_dir
John Budorickd3ba72b2018-03-20 12:27:42 -07001441 self._cipd_root = None
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001442 self.config_content = None
1443
borenet@google.com88d10082014-03-21 17:24:48 +00001444 def _CheckConfig(self):
1445 """Verify that the config matches the state of the existing checked-out
1446 solutions."""
1447 for dep in self.dependencies:
1448 if dep.managed and dep.url:
Edward Lemurb61d3872018-05-09 18:42:47 -04001449 scm = dep.CreateSCM(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001450 dep.url, self.root_dir, dep.name, self.outbuf)
smut@google.comd33eab32014-07-07 19:35:18 +00001451 actual_url = scm.GetActualRemoteURL(self._options)
borenet@google.com4e9be262014-04-08 19:40:30 +00001452 if actual_url and not scm.DoesRemoteURLMatch(self._options):
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001453 mirror = scm.GetCacheMirror()
1454 if mirror:
1455 mirror_string = '%s (exists=%s)' % (mirror.mirror_path,
1456 mirror.exists())
1457 else:
1458 mirror_string = 'not used'
borenet@google.com0a427372014-04-02 19:12:13 +00001459 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001460Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001461is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001462
borenet@google.com97882362014-04-07 20:06:02 +00001463The .gclient file contains:
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001464URL: %(expected_url)s (%(expected_scm)s)
1465Cache mirror: %(mirror_string)s
borenet@google.com97882362014-04-07 20:06:02 +00001466
1467The local checkout in %(checkout_path)s reports:
1468%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001469
1470You should ensure that the URL listed in .gclient is correct and either change
agabled437d762016-10-17 09:35:11 -07001471it or fix the checkout.
borenet@google.com88d10082014-03-21 17:24:48 +00001472''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1473 'expected_url': dep.url,
John Budorick0f7b2002018-01-19 15:46:17 -08001474 'expected_scm': self.GetScmName(dep.url),
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001475 'mirror_string' : mirror_string,
borenet@google.com88d10082014-03-21 17:24:48 +00001476 'actual_url': actual_url,
John Budorick0f7b2002018-01-19 15:46:17 -08001477 'actual_scm': self.GetScmName(actual_url)})
borenet@google.com88d10082014-03-21 17:24:48 +00001478
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001479 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001480 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001481 config_dict = {}
1482 self.config_content = content
1483 try:
1484 exec(content, config_dict)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001485 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001486 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001487
peter@chromium.org1efccc82012-04-27 16:34:38 +00001488 # Append any target OS that is not already being enforced to the tuple.
1489 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001490 if config_dict.get('target_os_only', False):
1491 self._enforced_os = tuple(set(target_os))
1492 else:
1493 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1494
Tom Andersonc31ae0b2018-02-06 14:48:56 -08001495 # Append any target CPU that is not already being enforced to the tuple.
1496 target_cpu = config_dict.get('target_cpu', [])
1497 if config_dict.get('target_cpu_only', False):
1498 self._enforced_cpu = tuple(set(target_cpu))
1499 else:
1500 self._enforced_cpu = tuple(set(self._enforced_cpu).union(target_cpu))
1501
Aleksandr Derbenev9e8fb0e2017-08-01 20:18:31 +03001502 cache_dir = config_dict.get('cache_dir', self._options.cache_dir)
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001503 if cache_dir:
1504 cache_dir = os.path.join(self.root_dir, cache_dir)
1505 cache_dir = os.path.abspath(cache_dir)
Andrii Shyshkalov77ce4bd2017-11-27 12:38:18 -08001506
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001507 gclient_scm.GitWrapper.cache_dir = cache_dir
1508 git_cache.Mirror.SetCachePath(cache_dir)
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001509
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001510 if not target_os and config_dict.get('target_os_only', False):
1511 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1512 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001513
Tom Andersonc31ae0b2018-02-06 14:48:56 -08001514 if not target_cpu and config_dict.get('target_cpu_only', False):
1515 raise gclient_utils.Error('Can\'t use target_cpu_only if target_cpu is '
1516 'not specified')
1517
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001518 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001519 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001520 try:
Edward Lemurb61d3872018-05-09 18:42:47 -04001521 deps_to_add.append(GitDependency(
Paweł Hajdan, Jrfc6196b2017-07-27 13:15:25 +02001522 self, s['name'], s['url'], s['url'],
smutae7ea312016-07-18 11:59:41 -07001523 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001524 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001525 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001526 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001527 s.get('deps_file', 'DEPS'),
agabledce6ddc2016-09-08 10:02:16 -07001528 True,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001529 None,
1530 None,
Edward Lemur231f5ea2018-01-31 19:02:36 +01001531 True,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001532 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001533 except KeyError:
1534 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1535 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001536 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1537 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001538
1539 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001540 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001541 self._options.config_filename),
1542 self.config_content)
1543
1544 @staticmethod
1545 def LoadCurrentConfig(options):
1546 """Searches for and loads a .gclient file relative to the current working
1547 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001548 if options.spec:
1549 client = GClient('.', options)
1550 client.SetConfig(options.spec)
1551 else:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001552 if options.verbose:
1553 print('Looking for %s starting from %s\n' % (
1554 options.config_filename, os.getcwd()))
szager@chromium.orge2e03202012-07-31 18:05:16 +00001555 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1556 if not path:
Michael Achenbachb3ce73d2017-10-11 16:41:27 +02001557 if options.verbose:
1558 print('Couldn\'t find configuration file.')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001559 return None
1560 client = GClient(path, options)
1561 client.SetConfig(gclient_utils.FileRead(
1562 os.path.join(path, options.config_filename)))
maruel@chromium.org69392e72011-10-13 22:09:00 +00001563
1564 if (options.revisions and
1565 len(client.dependencies) > 1 and
1566 any('@' not in r for r in options.revisions)):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001567 print(
1568 ('You must specify the full solution name like --revision %s@%s\n'
1569 'when you have multiple solutions setup in your .gclient file.\n'
1570 'Other solutions present are: %s.') % (
maruel@chromium.org69392e72011-10-13 22:09:00 +00001571 client.dependencies[0].name,
1572 options.revisions[0],
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001573 ', '.join(s.name for s in client.dependencies[1:])),
1574 file=sys.stderr)
maruel@chromium.org15804092010-09-02 17:07:37 +00001575 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001576
nsylvain@google.comefc80932011-05-31 21:27:56 +00001577 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
Paweł Hajdan, Jr3ba2a7c2017-10-04 19:24:46 +02001578 managed=True, cache_dir=None, custom_vars=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001579 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1580 'solution_name': solution_name,
1581 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001582 'deps_file': deps_file,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001583 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001584 'cache_dir': cache_dir,
Paweł Hajdan, Jr3ba2a7c2017-10-04 19:24:46 +02001585 'custom_vars': custom_vars or {},
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001586 })
1587
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001588 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001589 """Creates a .gclient_entries file to record the list of unique checkouts.
1590
1591 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001592 """
1593 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1594 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001595 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001596 for entry in self.root.subtree(False):
agabled437d762016-10-17 09:35:11 -07001597 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
Edward Lemure7273d22018-05-10 19:13:51 -04001598 pprint.pformat(entry.url))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001599 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001600 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001601 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001602 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001603
1604 def _ReadEntries(self):
1605 """Read the .gclient_entries file for the given client.
1606
1607 Returns:
1608 A sequence of solution names, which will be empty if there is the
1609 entries file hasn't been created yet.
1610 """
1611 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001612 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001613 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001614 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001615 try:
1616 exec(gclient_utils.FileRead(filename), scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001617 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001618 gclient_utils.SyntaxErrorToError(filename, e)
Aaron Gable3721ee92017-04-03 14:53:14 -07001619 return scope.get('entries', {})
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001620
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001621 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001622 """Checks for revision overrides."""
1623 revision_overrides = {}
smutae7ea312016-07-18 11:59:41 -07001624 if self._options.head:
1625 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001626 if not self._options.revisions:
1627 for s in self.dependencies:
smutae7ea312016-07-18 11:59:41 -07001628 if not s.managed:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001629 self._options.revisions.append('%s@unmanaged' % s.name)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001630 if not self._options.revisions:
1631 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001632 solutions_names = [s.name for s in self.dependencies]
smutae7ea312016-07-18 11:59:41 -07001633 index = 0
1634 for revision in self._options.revisions:
1635 if not '@' in revision:
maruel@chromium.org307d1792010-05-31 20:03:13 +00001636 # Support for --revision 123
smutae7ea312016-07-18 11:59:41 -07001637 revision = '%s@%s' % (solutions_names[index], revision)
1638 name, rev = revision.split('@', 1)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001639 revision_overrides[name] = rev
smutae7ea312016-07-18 11:59:41 -07001640 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001641 return revision_overrides
1642
Edward Lesmesc621b212018-03-21 20:26:56 -04001643 def _EnforcePatchRefs(self):
1644 """Checks for patch refs."""
1645 patch_refs = {}
1646 if not self._options.patch_refs:
1647 return patch_refs
1648 for given_patch_ref in self._options.patch_refs:
1649 patch_repo, _, patch_ref = given_patch_ref.partition('@')
1650 if not patch_repo or not patch_ref:
1651 raise gclient_utils.Error(
1652 'Wrong revision format: %s should be of the form '
1653 'patch_repo@patch_ref.' % given_patch_ref)
1654 patch_refs[patch_repo] = patch_ref
1655 return patch_refs
1656
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001657 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001658 """Runs a command on each dependency in a client and its dependencies.
1659
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001660 Args:
1661 command: The command to use (e.g., 'status' or 'diff')
1662 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001663 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001664 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001665 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001666
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001667 revision_overrides = {}
Edward Lesmesc621b212018-03-21 20:26:56 -04001668 patch_refs = {}
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001669 # It's unnecessary to check for revision overrides for 'recurse'.
1670 # Save a few seconds by not calling _EnforceRevisions() in that case.
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02001671 if command not in ('diff', 'recurse', 'runhooks', 'status', 'revert',
1672 'validate'):
szager@chromium.org5273b8a2014-08-21 15:10:10 +00001673 self._CheckConfig()
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001674 revision_overrides = self._EnforceRevisions()
Edward Lesmesc621b212018-03-21 20:26:56 -04001675
1676 if command == 'update':
1677 patch_refs = self._EnforcePatchRefs()
Daniel Chenga21b5b32017-10-19 20:07:48 +00001678 # Disable progress for non-tty stdout.
Daniel Chenga0c5f082017-10-19 13:35:19 -07001679 should_show_progress = (
1680 setup_color.IS_TTY and not self._options.verbose and progress)
1681 pm = None
1682 if should_show_progress:
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001683 if command in ('update', 'revert'):
1684 pm = Progress('Syncing projects', 1)
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02001685 elif command in ('recurse', 'validate'):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001686 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001687 work_queue = gclient_utils.ExecutionQueue(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001688 self._options.jobs, pm, ignore_requirements=ignore_requirements,
1689 verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001690 for s in self.dependencies:
Paweł Hajdan, Jr4baaa112017-07-04 19:09:32 +02001691 if s.should_process:
1692 work_queue.enqueue(s)
Edward Lesmesc621b212018-03-21 20:26:56 -04001693 work_queue.flush(revision_overrides, command, args, options=self._options,
1694 patch_refs=patch_refs)
1695
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001696 if revision_overrides:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001697 print('Please fix your script, having invalid --revision flags will soon '
Edward Lesmesc621b212018-03-21 20:26:56 -04001698 'be considered an error.', file=sys.stderr)
1699
1700 if patch_refs:
1701 raise gclient_utils.Error(
1702 'The following --patch-ref flags were not used. Please fix it:\n%s' %
1703 ('\n'.join(
1704 patch_repo + '@' + patch_ref
1705 for patch_repo, patch_ref in patch_refs.iteritems())))
piman@chromium.org6f363722010-04-27 00:41:09 +00001706
John Budorickd3ba72b2018-03-20 12:27:42 -07001707 if self._cipd_root:
1708 self._cipd_root.run(command)
1709
Dirk Pranke9f20d022017-10-11 18:36:54 -07001710 # Once all the dependencies have been processed, it's now safe to write
Michael Moss848c86e2018-05-03 16:05:50 -07001711 # out the gn_args_file and run the hooks.
Dirk Pranke9f20d022017-10-11 18:36:54 -07001712 if command == 'update':
Michael Moss848c86e2018-05-03 16:05:50 -07001713 gn_args_dep = self.dependencies[0]
1714 if gn_args_dep._gn_args_from:
1715 deps_map = dict([(dep.name, dep) for dep in gn_args_dep.dependencies])
1716 gn_args_dep = deps_map.get(gn_args_dep._gn_args_from)
1717 if gn_args_dep and gn_args_dep.HasGNArgsFile():
1718 gn_args_dep.WriteGNArgsFile()
Dirk Pranke9f20d022017-10-11 18:36:54 -07001719
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001720 if not self._options.nohooks:
Daniel Chenga0c5f082017-10-19 13:35:19 -07001721 if should_show_progress:
1722 pm = Progress('Running hooks', 1)
1723 self.RunHooksRecursively(self._options, pm)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001724
1725 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001726 # Notify the user if there is an orphaned entry in their working copy.
1727 # Only delete the directory if there are no changes in it, and
1728 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001729 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001730 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1731 for e in entries]
1732
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001733 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001734 if not prev_url:
1735 # entry must have been overridden via .gclient custom_deps
1736 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001737 # Fix path separator on Windows.
1738 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001739 e_dir = os.path.join(self.root_dir, entry_fixed)
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001740 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001741 if (entry not in entries and
1742 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001743 os.path.exists(e_dir)):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001744 # The entry has been removed from DEPS.
Edward Lemurb61d3872018-05-09 18:42:47 -04001745 scm = GitDependency.CreateSCM(
1746 self, prev_url, self.root_dir, entry_fixed, self.outbuf)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001747
1748 # Check to see if this directory is now part of a higher-up checkout.
borenet@google.com359bb642014-05-13 17:28:19 +00001749 scm_root = None
agabled437d762016-10-17 09:35:11 -07001750 try:
1751 scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(scm.checkout_path)
1752 except subprocess2.CalledProcessError:
1753 pass
1754 if not scm_root:
borenet@google.com359bb642014-05-13 17:28:19 +00001755 logging.warning('Could not find checkout root for %s. Unable to '
1756 'determine whether it is part of a higher-level '
1757 'checkout, so not removing.' % entry)
1758 continue
primiano@chromium.org1c127382015-02-17 11:15:40 +00001759
1760 # This is to handle the case of third_party/WebKit migrating from
1761 # being a DEPS entry to being part of the main project.
1762 # If the subproject is a Git project, we need to remove its .git
1763 # folder. Otherwise git operations on that folder will have different
1764 # effects depending on the current working directory.
agabled437d762016-10-17 09:35:11 -07001765 if os.path.abspath(scm_root) == os.path.abspath(e_dir):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001766 e_par_dir = os.path.join(e_dir, os.pardir)
agabled437d762016-10-17 09:35:11 -07001767 if gclient_scm.scm.GIT.IsInsideWorkTree(e_par_dir):
1768 par_scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(e_par_dir)
primiano@chromium.org1c127382015-02-17 11:15:40 +00001769 # rel_e_dir : relative path of entry w.r.t. its parent repo.
1770 rel_e_dir = os.path.relpath(e_dir, par_scm_root)
agabled437d762016-10-17 09:35:11 -07001771 if gclient_scm.scm.GIT.IsDirectoryVersioned(
1772 par_scm_root, rel_e_dir):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001773 save_dir = scm.GetGitBackupDirPath()
1774 # Remove any eventual stale backup dir for the same project.
1775 if os.path.exists(save_dir):
1776 gclient_utils.rmtree(save_dir)
1777 os.rename(os.path.join(e_dir, '.git'), save_dir)
1778 # When switching between the two states (entry/ is a subproject
1779 # -> entry/ is part of the outer project), it is very likely
1780 # that some files are changed in the checkout, unless we are
1781 # jumping *exactly* across the commit which changed just DEPS.
1782 # In such case we want to cleanup any eventual stale files
1783 # (coming from the old subproject) in order to end up with a
1784 # clean checkout.
agabled437d762016-10-17 09:35:11 -07001785 gclient_scm.scm.GIT.CleanupDir(par_scm_root, rel_e_dir)
primiano@chromium.org1c127382015-02-17 11:15:40 +00001786 assert not os.path.exists(os.path.join(e_dir, '.git'))
1787 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher '
1788 'level checkout. The git folder containing all the local'
1789 ' branches has been saved to %s.\n'
1790 'If you don\'t care about its state you can safely '
1791 'remove that folder to free up space.') %
1792 (entry, save_dir))
1793 continue
1794
borenet@google.com359bb642014-05-13 17:28:19 +00001795 if scm_root in full_entries:
primiano@chromium.org1c127382015-02-17 11:15:40 +00001796 logging.info('%s is part of a higher level checkout, not removing',
1797 scm.GetCheckoutRoot())
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001798 continue
1799
1800 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001801 scm.status(self._options, [], file_list)
1802 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001803 if (not self._options.delete_unversioned_trees or
1804 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001805 # There are modified files in this entry. Keep warning until
1806 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001807 print(('\nWARNING: \'%s\' is no longer part of this client. '
1808 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001809 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001810 else:
1811 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001812 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001813 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001814 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001815 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001816 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001817 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001818
1819 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001820 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001821 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001822 # Load all the settings.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001823 work_queue = gclient_utils.ExecutionQueue(
1824 self._options.jobs, None, False, verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001825 for s in self.dependencies:
Paweł Hajdan, Jr4baaa112017-07-04 19:09:32 +02001826 if s.should_process:
1827 work_queue.enqueue(s)
Edward Lesmesc621b212018-03-21 20:26:56 -04001828 work_queue.flush({}, None, [], options=self._options, patch_refs=None)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001829
Edward Lemure7273d22018-05-10 19:13:51 -04001830 def ShouldPrintRevision(dep):
Edward Lesmesbb16e332018-03-30 17:54:51 -04001831 return (not self._options.filter
Edward Lemure7273d22018-05-10 19:13:51 -04001832 or dep.FuzzyMatchUrl(self._options.filter))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001833
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001834 if self._options.snapshot:
Edward Lesmesc2960242018-03-06 20:50:15 -05001835 json_output = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001836 # First level at .gclient
1837 for d in self.dependencies:
1838 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001839 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001840 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001841 for d in dep.dependencies:
Edward Lemure7273d22018-05-10 19:13:51 -04001842 d.PinToActualRevision()
1843 if ShouldPrintRevision(d):
1844 entries[d.name] = d.url
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001845 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001846 GrabDeps(d)
Edward Lesmesc2960242018-03-06 20:50:15 -05001847 json_output.append({
1848 'name': d.name,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001849 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001850 'deps_file': d.deps_file,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001851 'managed': d.managed,
Edward Lesmesc2960242018-03-06 20:50:15 -05001852 'custom_deps': entries,
1853 })
1854 if self._options.output_json == '-':
1855 print(json.dumps(json_output, indent=2, separators=(',', ': ')))
1856 elif self._options.output_json:
1857 with open(self._options.output_json, 'w') as f:
1858 json.dump(json_output, f)
1859 else:
1860 # Print the snapshot configuration file
1861 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {
1862 'solution_list': pprint.pformat(json_output, indent=2),
1863 })
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001864 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001865 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001866 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001867 if self._options.actual:
Edward Lemure7273d22018-05-10 19:13:51 -04001868 d.PinToActualRevision()
1869 if ShouldPrintRevision(d):
1870 entries[d.name] = d.url
Edward Lesmesc2960242018-03-06 20:50:15 -05001871 if self._options.output_json:
1872 json_output = {
1873 name: {
Michael Moss012013e2018-03-30 17:03:19 -07001874 'url': rev.split('@')[0] if rev else None,
1875 'rev': rev.split('@')[1] if rev and '@' in rev else None,
Edward Lesmesc2960242018-03-06 20:50:15 -05001876 }
1877 for name, rev in entries.iteritems()
1878 }
1879 if self._options.output_json == '-':
1880 print(json.dumps(json_output, indent=2, separators=(',', ': ')))
1881 else:
1882 with open(self._options.output_json, 'w') as f:
1883 json.dump(json_output, f)
1884 else:
1885 keys = sorted(entries.keys())
1886 for x in keys:
1887 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001888 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001889
Edward Lesmes6c24d372018-03-28 12:52:29 -04001890 def ParseDepsFile(self, expand_vars=None):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001891 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001892 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001893
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001894 def PrintLocationAndContents(self):
1895 # Print out the .gclient file. This is longer than if we just printed the
1896 # client dict, but more legible, and it might contain helpful comments.
1897 print('Loaded .gclient config in %s:\n%s' % (
1898 self.root_dir, self.config_content))
1899
John Budorickd3ba72b2018-03-20 12:27:42 -07001900 def GetCipdRoot(self):
1901 if not self._cipd_root:
1902 self._cipd_root = gclient_scm.CipdRoot(
1903 self.root_dir,
1904 # TODO(jbudorick): Support other service URLs as necessary.
1905 # Service URLs should be constant over the scope of a cipd
1906 # root, so a var per DEPS file specifying the service URL
1907 # should suffice.
1908 'https://chrome-infra-packages.appspot.com')
1909 return self._cipd_root
1910
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001911 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001912 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001913 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001914 return self._root_dir
1915
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001916 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001917 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001918 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001919 return self._enforced_os
1920
maruel@chromium.org68988972011-09-20 14:11:42 +00001921 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001922 def recursion_limit(self):
1923 """How recursive can each dependencies in DEPS file can load DEPS file."""
1924 return self._recursion_limit
1925
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001926 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +00001927 def try_recursedeps(self):
1928 """Whether to attempt using recursedeps-style recursion processing."""
cmp@chromium.orge84ac912014-06-30 23:14:35 +00001929 return True
1930
1931 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001932 def target_os(self):
1933 return self._enforced_os
1934
Tom Andersonc31ae0b2018-02-06 14:48:56 -08001935 @property
1936 def target_cpu(self):
1937 return self._enforced_cpu
1938
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001939
John Budorick0f7b2002018-01-19 15:46:17 -08001940class CipdDependency(Dependency):
1941 """A Dependency object that represents a single CIPD package."""
1942
1943 def __init__(
1944 self, parent, name, dep_value, cipd_root,
1945 custom_vars, should_process, relative, condition, condition_value):
1946 package = dep_value['package']
1947 version = dep_value['version']
1948 url = urlparse.urljoin(
1949 cipd_root.service_url, '%s@%s' % (package, version))
1950 super(CipdDependency, self).__init__(
Shenghua Zhang6f830312018-02-26 11:45:07 -08001951 parent, name + ':' + package, url, url, None, None, custom_vars,
John Budorick0f7b2002018-01-19 15:46:17 -08001952 None, None, should_process, relative, condition, condition_value)
1953 if relative:
1954 # TODO(jbudorick): Implement relative if necessary.
1955 raise gclient_utils.Error(
1956 'Relative CIPD dependencies are not currently supported.')
John Budorickd3ba72b2018-03-20 12:27:42 -07001957 self._cipd_package = None
John Budorick0f7b2002018-01-19 15:46:17 -08001958 self._cipd_root = cipd_root
John Budorick0f7b2002018-01-19 15:46:17 -08001959 self._cipd_subdir = os.path.relpath(
Shenghua Zhang6f830312018-02-26 11:45:07 -08001960 os.path.join(self.root.root_dir, name), cipd_root.root_dir)
John Budorickd3ba72b2018-03-20 12:27:42 -07001961 self._package_name = package
1962 self._package_version = version
1963
1964 #override
Edward Lesmesc621b212018-03-21 20:26:56 -04001965 def run(self, revision_overrides, command, args, work_queue, options,
1966 patch_refs):
John Budorickd3ba72b2018-03-20 12:27:42 -07001967 """Runs |command| then parse the DEPS file."""
1968 logging.info('CipdDependency(%s).run()' % self.name)
1969 if not self.should_process:
1970 return
1971 self._CreatePackageIfNecessary()
1972 super(CipdDependency, self).run(revision_overrides, command, args,
Edward Lesmesc621b212018-03-21 20:26:56 -04001973 work_queue, options, patch_refs)
John Budorickd3ba72b2018-03-20 12:27:42 -07001974
1975 def _CreatePackageIfNecessary(self):
1976 # We lazily create the CIPD package to make sure that only packages
1977 # that we want (as opposed to all packages defined in all DEPS files
1978 # we parse) get added to the root and subsequently ensured.
1979 if not self._cipd_package:
1980 self._cipd_package = self._cipd_root.add_package(
1981 self._cipd_subdir, self._package_name, self._package_version)
John Budorick0f7b2002018-01-19 15:46:17 -08001982
Edward Lesmes6c24d372018-03-28 12:52:29 -04001983 def ParseDepsFile(self, expand_vars=None):
John Budorick0f7b2002018-01-19 15:46:17 -08001984 """CIPD dependencies are not currently allowed to have nested deps."""
1985 self.add_dependencies_and_close([], [])
1986
1987 #override
Shenghua Zhang6f830312018-02-26 11:45:07 -08001988 def verify_validity(self):
1989 """CIPD dependencies allow duplicate name for packages in same directory."""
1990 logging.info('Dependency(%s).verify_validity()' % self.name)
1991 return True
1992
1993 #override
John Budorick0f7b2002018-01-19 15:46:17 -08001994 def GetScmName(self, url):
1995 """Always 'cipd'."""
1996 del url
1997 return 'cipd'
1998
1999 #override
2000 def CreateSCM(self, url, root_dir=None, relpath=None, out_fh=None,
2001 out_cb=None):
2002 """Create a Wrapper instance suitable for handling this CIPD dependency."""
John Budorickd3ba72b2018-03-20 12:27:42 -07002003 self._CreatePackageIfNecessary()
John Budorick0f7b2002018-01-19 15:46:17 -08002004 return gclient_scm.CipdWrapper(
2005 url, root_dir, relpath, out_fh, out_cb,
2006 root=self._cipd_root,
2007 package=self._cipd_package)
2008
2009 def ToLines(self):
2010 """Return a list of lines representing this in a DEPS file."""
2011 s = []
John Budorickd3ba72b2018-03-20 12:27:42 -07002012 self._CreatePackageIfNecessary()
John Budorick0f7b2002018-01-19 15:46:17 -08002013 if self._cipd_package.authority_for_subdir:
2014 condition_part = ([' "condition": %r,' % self.condition]
2015 if self.condition else [])
2016 s.extend([
2017 ' # %s' % self.hierarchy(include_url=False),
John Budorickd3ba72b2018-03-20 12:27:42 -07002018 ' "%s": {' % (self.name.split(':')[0],),
John Budorick0f7b2002018-01-19 15:46:17 -08002019 ' "packages": [',
2020 ])
2021 for p in self._cipd_root.packages(self._cipd_subdir):
2022 s.extend([
John Budorick64e33cb2018-02-20 09:40:30 -08002023 ' {',
2024 ' "package": "%s",' % p.name,
2025 ' "version": "%s",' % p.version,
2026 ' },',
John Budorick0f7b2002018-01-19 15:46:17 -08002027 ])
John Budorickd3ba72b2018-03-20 12:27:42 -07002028
John Budorick0f7b2002018-01-19 15:46:17 -08002029 s.extend([
2030 ' ],',
2031 ' "dep_type": "cipd",',
2032 ] + condition_part + [
2033 ' },',
2034 '',
2035 ])
2036 return s
2037
2038
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002039#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002040
2041
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002042@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00002043def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002044 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00002045
2046 Runs a shell command on all entries.
qyearsley12fa6ff2016-08-24 09:18:40 -07002047 Sets GCLIENT_DEP_PATH environment variable as the dep's relative location to
ilevy@chromium.org37116242012-11-28 01:32:48 +00002048 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00002049 """
2050 # Stop parsing at the first non-arg so that these go through to the command
2051 parser.disable_interspersed_args()
2052 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002053 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00002054 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002055 help='Ignore non-zero return codes from subcommands.')
2056 parser.add_option('--prepend-dir', action='store_true',
2057 help='Prepend relative dir for use with git <cmd> --null.')
2058 parser.add_option('--no-progress', action='store_true',
2059 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00002060 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00002061 if not args:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002062 print('Need to supply a command!', file=sys.stderr)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00002063 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00002064 root_and_entries = gclient_utils.GetGClientRootAndEntries()
2065 if not root_and_entries:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002066 print(
maruel@chromium.org78cba522010-10-18 13:32:05 +00002067 'You need to run gclient sync at least once to use \'recurse\'.\n'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002068 'This is because .gclient_entries needs to exist and be up to date.',
2069 file=sys.stderr)
maruel@chromium.org78cba522010-10-18 13:32:05 +00002070 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00002071
2072 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00002073 scm_set = set()
2074 for scm in options.scm:
2075 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00002076 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00002077
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00002078 options.nohooks = True
2079 client = GClient.LoadCurrentConfig(options)
Marc-Antoine Ruele6e06412017-10-18 13:47:02 -04002080 if not client:
2081 raise gclient_utils.Error('client not configured; see \'gclient config\'')
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002082 return client.RunOnDeps('recurse', args, ignore_requirements=True,
2083 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00002084
2085
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002086@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00002087def CMDfetch(parser, args):
2088 """Fetches upstream commits for all modules.
2089
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002090 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
2091 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00002092 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002093 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002094 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
2095
2096
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002097class Flattener(object):
2098 """Flattens a gclient solution."""
2099
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002100 def __init__(self, client, pin_all_deps=False):
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002101 """Constructor.
2102
2103 Arguments:
2104 client (GClient): client to flatten
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002105 pin_all_deps (bool): whether to pin all deps, even if they're not pinned
2106 in DEPS
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002107 """
2108 self._client = client
2109
2110 self._deps_string = None
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02002111 self._deps_files = set()
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002112
2113 self._allowed_hosts = set()
2114 self._deps = {}
2115 self._deps_os = {}
2116 self._hooks = []
2117 self._hooks_os = {}
2118 self._pre_deps_hooks = []
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02002119 self._vars = {}
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002120
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002121 self._flatten(pin_all_deps=pin_all_deps)
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002122
2123 @property
2124 def deps_string(self):
2125 assert self._deps_string is not None
2126 return self._deps_string
2127
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02002128 @property
2129 def deps_files(self):
2130 return self._deps_files
2131
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002132 def _pin_dep(self, dep):
2133 """Pins a dependency to specific full revision sha.
2134
2135 Arguments:
2136 dep (Dependency): dependency to process
2137 """
Edward Lemure7273d22018-05-10 19:13:51 -04002138 if dep.url is None:
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002139 return
2140
2141 # Make sure the revision is always fully specified (a hash),
2142 # as opposed to refs or tags which might change. Similarly,
2143 # shortened shas might become ambiguous; make sure to always
2144 # use full one for pinning.
Edward Lemure7273d22018-05-10 19:13:51 -04002145 revision = gclient_utils.SplitUrlRevision(dep.url)[1]
2146 if not revision or not gclient_utils.IsFullGitSha(revision):
2147 dep.PinToActualRevision()
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002148
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002149 def _flatten(self, pin_all_deps=False):
2150 """Runs the flattener. Saves resulting DEPS string.
2151
2152 Arguments:
2153 pin_all_deps (bool): whether to pin all deps, even if they're not pinned
2154 in DEPS
2155 """
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002156 for solution in self._client.dependencies:
Paweł Hajdan, Jr11eb7152017-08-10 12:50:11 +02002157 self._add_dep(solution)
Paweł Hajdan, Jrb0ad16e2017-08-03 15:33:21 +02002158 self._flatten_dep(solution)
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002159
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002160 if pin_all_deps:
2161 for dep in self._deps.itervalues():
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002162 self._pin_dep(dep)
Paweł Hajdan, Jr39300ba2017-08-11 16:52:38 +02002163
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002164 for os_deps in self._deps_os.itervalues():
2165 for dep in os_deps.itervalues():
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002166 self._pin_dep(dep)
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002167
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02002168 def add_deps_file(dep):
Paweł Hajdan, Jr0870df22017-08-23 17:59:29 +02002169 # Only include DEPS files referenced by recursedeps.
2170 if not (dep.parent is None or
2171 (dep.name in (dep.parent.recursedeps or {}))):
2172 return
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02002173 deps_file = dep.deps_file
2174 deps_path = os.path.join(self._client.root_dir, dep.name, deps_file)
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02002175 if not os.path.exists(deps_path):
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02002176 # gclient has a fallback that if deps_file doesn't exist, it'll try
2177 # DEPS. Do the same here.
2178 deps_file = 'DEPS'
2179 deps_path = os.path.join(self._client.root_dir, dep.name, deps_file)
2180 if not os.path.exists(deps_path):
2181 return
Edward Lemure7273d22018-05-10 19:13:51 -04002182 assert dep.url
2183 self._deps_files.add((dep.url, deps_file, dep.hierarchy_data()))
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02002184 for dep in self._deps.itervalues():
2185 add_deps_file(dep)
2186 for os_deps in self._deps_os.itervalues():
2187 for dep in os_deps.itervalues():
2188 add_deps_file(dep)
2189
Michael Moss848c86e2018-05-03 16:05:50 -07002190 gn_args_dep = self._deps.get(self._client.dependencies[0]._gn_args_from,
2191 self._client.dependencies[0])
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002192 self._deps_string = '\n'.join(
Michael Moss848c86e2018-05-03 16:05:50 -07002193 _GNSettingsToLines(gn_args_dep._gn_args_file, gn_args_dep._gn_args) +
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002194 _AllowedHostsToLines(self._allowed_hosts) +
2195 _DepsToLines(self._deps) +
2196 _DepsOsToLines(self._deps_os) +
2197 _HooksToLines('hooks', self._hooks) +
2198 _HooksToLines('pre_deps_hooks', self._pre_deps_hooks) +
2199 _HooksOsToLines(self._hooks_os) +
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02002200 _VarsToLines(self._vars) +
Paweł Hajdan, Jr76a9d042017-08-18 20:05:41 +02002201 ['# %s, %s' % (url, deps_file)
Michael Mossfe68c912018-03-22 19:19:35 -07002202 for url, deps_file, _ in sorted(self._deps_files)] +
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002203 ['']) # Ensure newline at end of file.
2204
Paweł Hajdan, Jr11eb7152017-08-10 12:50:11 +02002205 def _add_dep(self, dep):
2206 """Helper to add a dependency to flattened DEPS.
2207
2208 Arguments:
2209 dep (Dependency): dependency to add
2210 """
2211 assert dep.name not in self._deps or self._deps.get(dep.name) == dep, (
2212 dep.name, self._deps.get(dep.name))
Paweł Hajdan, Jr9a289022017-08-10 16:04:24 +02002213 if dep.url:
2214 self._deps[dep.name] = dep
Paweł Hajdan, Jr11eb7152017-08-10 12:50:11 +02002215
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002216 def _add_os_dep(self, os_dep, dep_os):
2217 """Helper to add an OS-specific dependency to flattened DEPS.
2218
2219 Arguments:
2220 os_dep (Dependency): dependency to add
2221 dep_os (str): name of the OS
2222 """
2223 assert (
2224 os_dep.name not in self._deps_os.get(dep_os, {}) or
2225 self._deps_os.get(dep_os, {}).get(os_dep.name) == os_dep), (
2226 os_dep.name, self._deps_os.get(dep_os, {}).get(os_dep.name))
2227 if os_dep.url:
2228 self._deps_os.setdefault(dep_os, {})[os_dep.name] = os_dep
2229
2230 def _flatten_dep(self, dep, dep_os=None):
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002231 """Visits a dependency in order to flatten it (see CMDflatten).
2232
2233 Arguments:
2234 dep (Dependency): dependency to process
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002235 dep_os (str or None): name of the OS |dep| is specific to
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002236 """
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002237 logging.debug('_flatten_dep(%s, %s)', dep.name, dep_os)
2238
Paweł Hajdan, Jrc69b32e2017-08-17 18:47:48 +02002239 if not dep.deps_parsed:
2240 dep.ParseDepsFile()
2241
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002242 self._allowed_hosts.update(dep.allowed_hosts)
2243
Michael Mossce9f17f2018-01-31 13:16:35 -08002244 # Only include vars explicitly listed in the DEPS files or gclient solution,
2245 # not automatic, local overrides (i.e. not all of dep.get_vars()).
2246 hierarchy = dep.hierarchy(include_url=False)
Paweł Hajdan, Jra3b67ae2017-08-30 15:18:21 +02002247 for key, value in dep._vars.iteritems():
Paweł Hajdan, Jrc9353602017-08-02 17:52:08 +02002248 # Make sure there are no conflicting variables. It is fine however
2249 # to use same variable name, as long as the value is consistent.
2250 assert key not in self._vars or self._vars[key][1] == value
Michael Mossce9f17f2018-01-31 13:16:35 -08002251 self._vars[key] = (hierarchy, value)
2252 # Override explicit custom variables.
2253 for key, value in dep.custom_vars.iteritems():
2254 # Do custom_vars that don't correspond to DEPS vars ever make sense? DEPS
2255 # conditionals shouldn't be using vars that aren't also defined in the
2256 # DEPS (presubmit actually disallows this), so any new custom_var must be
2257 # unused in the DEPS, so no need to add it to the flattened output either.
2258 if key not in self._vars:
2259 continue
2260 # Don't "override" existing vars if it's actually the same value.
2261 elif self._vars[key][1] == value:
2262 continue
2263 # Anything else is overriding a default value from the DEPS.
2264 self._vars[key] = (hierarchy + ' [custom_var override]', value)
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02002265
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002266 self._pre_deps_hooks.extend([(dep, hook) for hook in dep.pre_deps_hooks])
2267
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002268 if dep_os:
2269 if dep.deps_hooks:
2270 self._hooks_os.setdefault(dep_os, []).extend(
2271 [(dep, hook) for hook in dep.deps_hooks])
2272 else:
2273 self._hooks.extend([(dep, hook) for hook in dep.deps_hooks])
2274
Paweł Hajdan, Jrb0ad16e2017-08-03 15:33:21 +02002275 for sub_dep in dep.dependencies:
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002276 if dep_os:
2277 self._add_os_dep(sub_dep, dep_os)
2278 else:
2279 self._add_dep(sub_dep)
Paweł Hajdan, Jrb0ad16e2017-08-03 15:33:21 +02002280
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002281 for hook_os, os_hooks in dep.os_deps_hooks.iteritems():
2282 self._hooks_os.setdefault(hook_os, []).extend(
2283 [(dep, hook) for hook in os_hooks])
2284
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002285 for sub_dep_os, os_deps in dep.os_dependencies.iteritems():
Paweł Hajdan, Jre2deb1e2017-08-09 17:29:21 +02002286 for os_dep in os_deps:
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002287 self._add_os_dep(os_dep, sub_dep_os)
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002288
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002289 # Process recursedeps. |deps_by_name| is a map where keys are dependency
2290 # names, and values are maps of OS names to |Dependency| instances.
2291 # |None| in place of OS name means the dependency is not OS-specific.
2292 deps_by_name = dict((d.name, {None: d}) for d in dep.dependencies)
2293 for sub_dep_os, os_deps in dep.os_dependencies.iteritems():
Paweł Hajdan, Jrc9353602017-08-02 17:52:08 +02002294 for os_dep in os_deps:
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002295 assert sub_dep_os not in deps_by_name.get(os_dep.name, {}), (
2296 os_dep.name, sub_dep_os)
2297 deps_by_name.setdefault(os_dep.name, {})[sub_dep_os] = os_dep
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002298 for recurse_dep_name in (dep.recursedeps or []):
Paweł Hajdan, Jradae2a62017-08-18 16:49:57 +02002299 dep_info = deps_by_name[recurse_dep_name]
2300 for sub_dep_os, os_dep in dep_info.iteritems():
2301 self._flatten_dep(os_dep, dep_os=(sub_dep_os or dep_os))
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002302
2303
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002304def CMDflatten(parser, args):
2305 """Flattens the solutions into a single DEPS file."""
2306 parser.add_option('--output-deps', help='Path to the output DEPS file')
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002307 parser.add_option(
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02002308 '--output-deps-files',
2309 help=('Path to the output metadata about DEPS files referenced by '
2310 'recursedeps.'))
2311 parser.add_option(
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002312 '--pin-all-deps', action='store_true',
2313 help=('Pin all deps, even if not pinned in DEPS. CAVEAT: only does so '
2314 'for checked out deps, NOT deps_os.'))
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002315 options, args = parser.parse_args(args)
2316
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +02002317 options.do_not_merge_os_specific_entries = True
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002318 options.nohooks = True
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002319 options.process_all_deps = True
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002320 client = GClient.LoadCurrentConfig(options)
2321
2322 # Only print progress if we're writing to a file. Otherwise, progress updates
2323 # could obscure intended output.
2324 code = client.RunOnDeps('flatten', args, progress=options.output_deps)
2325 if code != 0:
2326 return code
2327
Paweł Hajdan, Jr271a1682017-07-06 20:54:30 +02002328 flattener = Flattener(client, pin_all_deps=options.pin_all_deps)
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002329
2330 if options.output_deps:
2331 with open(options.output_deps, 'w') as f:
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002332 f.write(flattener.deps_string)
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002333 else:
Paweł Hajdan, Jraaf93f42017-07-06 17:37:46 +02002334 print(flattener.deps_string)
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002335
Michael Mossfe68c912018-03-22 19:19:35 -07002336 deps_files = [{'url': d[0], 'deps_file': d[1], 'hierarchy': d[2]}
Paweł Hajdan, Jrfeb01642017-09-12 15:50:46 +02002337 for d in sorted(flattener.deps_files)]
2338 if options.output_deps_files:
2339 with open(options.output_deps_files, 'w') as f:
2340 json.dump(deps_files, f)
2341
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002342 return 0
2343
2344
Paweł Hajdan, Jr3c2aa832017-06-07 20:22:16 +02002345def _GNSettingsToLines(gn_args_file, gn_args):
2346 s = []
2347 if gn_args_file:
2348 s.extend([
2349 'gclient_gn_args_file = "%s"' % gn_args_file,
2350 'gclient_gn_args = %r' % gn_args,
2351 ])
2352 return s
2353
2354
Paweł Hajdan, Jr6014b562017-06-30 17:43:42 +02002355def _AllowedHostsToLines(allowed_hosts):
2356 """Converts |allowed_hosts| set to list of lines for output."""
2357 if not allowed_hosts:
2358 return []
2359 s = ['allowed_hosts = [']
2360 for h in sorted(allowed_hosts):
2361 s.append(' "%s",' % h)
2362 s.extend([']', ''])
2363 return s
2364
2365
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002366def _DepsToLines(deps):
2367 """Converts |deps| dict to list of lines for output."""
Paweł Hajdan, Jr5b593352017-06-29 18:37:45 +02002368 if not deps:
2369 return []
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002370 s = ['deps = {']
John Budorick0f7b2002018-01-19 15:46:17 -08002371 for _, dep in sorted(deps.iteritems()):
2372 s.extend(dep.ToLines())
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002373 s.extend(['}', ''])
2374 return s
2375
2376
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +02002377def _DepsOsToLines(deps_os):
2378 """Converts |deps_os| dict to list of lines for output."""
Paweł Hajdan, Jr5b593352017-06-29 18:37:45 +02002379 if not deps_os:
2380 return []
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +02002381 s = ['deps_os = {']
2382 for dep_os, os_deps in sorted(deps_os.iteritems()):
2383 s.append(' "%s": {' % dep_os)
Paweł Hajdan, Jrad30de62017-06-26 18:51:58 +02002384 for name, dep in sorted(os_deps.iteritems()):
Paweł Hajdan, Jr78ce24e2017-10-03 17:09:13 +02002385 condition_part = ([' "condition": %r,' % dep.condition]
Paweł Hajdan, Jrad30de62017-06-26 18:51:58 +02002386 if dep.condition else [])
2387 s.extend([
2388 ' # %s' % dep.hierarchy(include_url=False),
2389 ' "%s": {' % (name,),
Paweł Hajdan, Jrde86ab32017-08-10 13:55:16 +02002390 ' "url": "%s",' % (dep.raw_url,),
Paweł Hajdan, Jrad30de62017-06-26 18:51:58 +02002391 ] + condition_part + [
2392 ' },',
2393 '',
2394 ])
Paweł Hajdan, Jrc9603f52017-06-13 22:14:24 +02002395 s.extend([' },', ''])
2396 s.extend(['}', ''])
2397 return s
2398
2399
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002400def _HooksToLines(name, hooks):
2401 """Converts |hooks| list to list of lines for output."""
Paweł Hajdan, Jr5b593352017-06-29 18:37:45 +02002402 if not hooks:
2403 return []
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002404 s = ['%s = [' % name]
2405 for dep, hook in hooks:
2406 s.extend([
2407 ' # %s' % dep.hierarchy(include_url=False),
2408 ' {',
2409 ])
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02002410 if hook.name is not None:
2411 s.append(' "name": "%s",' % hook.name)
2412 if hook.pattern is not None:
2413 s.append(' "pattern": "%s",' % hook.pattern)
Paweł Hajdan, Jrecf53fe2017-09-29 18:28:49 +02002414 if hook.condition is not None:
Paweł Hajdan, Jr78ce24e2017-10-03 17:09:13 +02002415 s.append(' "condition": %r,' % hook.condition)
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002416 s.extend(
Paweł Hajdan, Jrc9364392017-06-14 17:11:56 +02002417 # Hooks run in the parent directory of their dep.
Paweł Hajdan, Jrad30de62017-06-26 18:51:58 +02002418 [' "cwd": "%s",' % os.path.normpath(os.path.dirname(dep.name))] +
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002419 [' "action": ['] +
Paweł Hajdan, Jrc10a4d82017-06-14 14:06:50 +02002420 [' "%s",' % arg for arg in hook.action] +
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02002421 [' ]', ' },', '']
2422 )
2423 s.extend([']', ''])
2424 return s
2425
2426
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002427def _HooksOsToLines(hooks_os):
2428 """Converts |hooks| list to list of lines for output."""
Paweł Hajdan, Jr5b593352017-06-29 18:37:45 +02002429 if not hooks_os:
2430 return []
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002431 s = ['hooks_os = {']
2432 for hook_os, os_hooks in hooks_os.iteritems():
Michael Moss017bcf62017-06-28 15:26:38 -07002433 s.append(' "%s": [' % hook_os)
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002434 for dep, hook in os_hooks:
2435 s.extend([
2436 ' # %s' % dep.hierarchy(include_url=False),
2437 ' {',
2438 ])
2439 if hook.name is not None:
2440 s.append(' "name": "%s",' % hook.name)
2441 if hook.pattern is not None:
2442 s.append(' "pattern": "%s",' % hook.pattern)
Paweł Hajdan, Jrecf53fe2017-09-29 18:28:49 +02002443 if hook.condition is not None:
Paweł Hajdan, Jr78ce24e2017-10-03 17:09:13 +02002444 s.append(' "condition": %r,' % hook.condition)
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002445 s.extend(
2446 # Hooks run in the parent directory of their dep.
2447 [' "cwd": "%s",' % os.path.normpath(os.path.dirname(dep.name))] +
2448 [' "action": ['] +
2449 [' "%s",' % arg for arg in hook.action] +
2450 [' ]', ' },', '']
2451 )
Michael Moss017bcf62017-06-28 15:26:38 -07002452 s.extend([' ],', ''])
Paweł Hajdan, Jr96e1d782017-06-27 11:12:25 +02002453 s.extend(['}', ''])
2454 return s
2455
2456
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02002457def _VarsToLines(variables):
2458 """Converts |variables| dict to list of lines for output."""
2459 if not variables:
2460 return []
2461 s = ['vars = {']
2462 for key, tup in sorted(variables.iteritems()):
Michael Mossce9f17f2018-01-31 13:16:35 -08002463 hierarchy, value = tup
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02002464 s.extend([
Michael Mossce9f17f2018-01-31 13:16:35 -08002465 ' # %s' % hierarchy,
Paweł Hajdan, Jrfb022012017-07-06 18:00:08 +02002466 ' "%s": %r,' % (key, value),
2467 '',
2468 ])
2469 s.extend(['}', ''])
2470 return s
2471
2472
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002473def CMDgrep(parser, args):
2474 """Greps through git repos managed by gclient.
2475
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002476 Runs 'git grep [args...]' for each module.
2477 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002478 # We can't use optparse because it will try to parse arguments sent
2479 # to git grep and throw an error. :-(
2480 if not args or re.match('(-h|--help)$', args[0]):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002481 print(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002482 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
2483 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
2484 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
2485 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002486 ' end of your query.',
2487 file=sys.stderr)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00002488 return 1
2489
2490 jobs_arg = ['--jobs=1']
2491 if re.match(r'(-j|--jobs=)\d+$', args[0]):
2492 jobs_arg, args = args[:1], args[1:]
2493 elif re.match(r'(-j|--jobs)$', args[0]):
2494 jobs_arg, args = args[:2], args[2:]
2495
2496 return CMDrecurse(
2497 parser,
2498 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
2499 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00002500
2501
stip@chromium.orga735da22015-04-29 23:18:20 +00002502def CMDroot(parser, args):
2503 """Outputs the solution root (or current dir if there isn't one)."""
2504 (options, args) = parser.parse_args(args)
2505 client = GClient.LoadCurrentConfig(options)
2506 if client:
2507 print(os.path.abspath(client.root_dir))
2508 else:
2509 print(os.path.abspath('.'))
2510
2511
agablea98a6cd2016-11-15 14:30:10 -08002512@subcommand.usage('[url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002513def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002514 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00002515
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002516 This specifies the configuration for further commands. After update/sync,
2517 top-level DEPS files in each module are read to determine dependent
2518 modules to operate on as well. If optional [url] parameter is
2519 provided, then configuration is read from a specified Subversion server
2520 URL.
2521 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00002522 # We do a little dance with the --gclientfile option. 'gclient config' is the
2523 # only command where it's acceptable to have both '--gclientfile' and '--spec'
2524 # arguments. So, we temporarily stash any --gclientfile parameter into
2525 # options.output_config_file until after the (gclientfile xor spec) error
2526 # check.
2527 parser.remove_option('--gclientfile')
2528 parser.add_option('--gclientfile', dest='output_config_file',
2529 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002530 parser.add_option('--name',
2531 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00002532 parser.add_option('--deps-file', default='DEPS',
David Benjamin105e11e2017-10-16 10:39:35 -04002533 help='overrides the default name for the DEPS file for the '
nsylvain@google.comefc80932011-05-31 21:27:56 +00002534 'main solutions and all sub-dependencies')
smutae7ea312016-07-18 11:59:41 -07002535 parser.add_option('--unmanaged', action='store_true', default=False,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00002536 help='overrides the default behavior to make it possible '
smutae7ea312016-07-18 11:59:41 -07002537 'to have the main solution untouched by gclient '
2538 '(gclient will check out unmanaged dependencies but '
2539 'will never sync them)')
Paweł Hajdan, Jr3ba2a7c2017-10-04 19:24:46 +02002540 parser.add_option('--custom-var', action='append', dest='custom_vars',
2541 default=[],
2542 help='overrides variables; key=value syntax')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002543 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002544 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00002545 if options.output_config_file:
2546 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00002547 if ((options.spec and args) or len(args) > 2 or
2548 (not options.spec and not args)):
2549 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
2550
Paweł Hajdan, Jr3ba2a7c2017-10-04 19:24:46 +02002551 custom_vars = {}
2552 for arg in options.custom_vars:
2553 kv = arg.split('=', 1)
2554 if len(kv) != 2:
2555 parser.error('Invalid --custom-var argument: %r' % arg)
2556 custom_vars[kv[0]] = gclient_eval.EvaluateCondition(kv[1], {})
2557
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002558 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002559 if options.spec:
2560 client.SetConfig(options.spec)
2561 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00002562 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00002563 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002564 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00002565 if name.endswith('.git'):
2566 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00002567 else:
2568 # specify an alternate relpath for the given URL.
2569 name = options.name
agable@chromium.orgf2214672015-10-27 21:02:48 +00002570 if not os.path.abspath(os.path.join(os.getcwd(), name)).startswith(
2571 os.getcwd()):
2572 parser.error('Do not pass a relative path for --name.')
2573 if any(x in ('..', '.', '/', '\\') for x in name.split(os.sep)):
2574 parser.error('Do not include relative path components in --name.')
2575
nsylvain@google.comefc80932011-05-31 21:27:56 +00002576 deps_file = options.deps_file
agablea98a6cd2016-11-15 14:30:10 -08002577 client.SetDefaultConfig(name, deps_file, base_url,
smutae7ea312016-07-18 11:59:41 -07002578 managed=not options.unmanaged,
Paweł Hajdan, Jr3ba2a7c2017-10-04 19:24:46 +02002579 cache_dir=options.cache_dir,
2580 custom_vars=custom_vars)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002581 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002582 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002583
2584
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002585@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002586 gclient pack > patch.txt
2587 generate simple patch for configured client and dependences
2588""")
2589def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002590 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00002591
agabled437d762016-10-17 09:35:11 -07002592 Internally, runs 'git diff' on each checked out module and
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002593 dependencies, and performs minimal postprocessing of the output. The
2594 resulting patch is printed to stdout and can be applied to a freshly
2595 checked out tree via 'patch -p0 < patchfile'.
2596 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002597 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2598 help='override deps for the specified (comma-separated) '
2599 'platform(s); \'all\' will process all deps_os '
2600 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00002601 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002602 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00002603 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00002604 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00002605 client = GClient.LoadCurrentConfig(options)
2606 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002607 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00002608 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002609 client.PrintLocationAndContents()
kbr@google.comab318592009-09-04 00:54:55 +00002610 return client.RunOnDeps('pack', args)
2611
2612
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002613def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002614 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002615 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2616 help='override deps for the specified (comma-separated) '
2617 'platform(s); \'all\' will process all deps_os '
2618 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002619 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002620 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002621 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002622 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002623 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002624 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002625 return client.RunOnDeps('status', args)
2626
2627
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002628@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00002629 gclient sync
2630 update files from SCM according to current configuration,
2631 *for modules which have changed since last update or sync*
2632 gclient sync --force
2633 update files from SCM according to current configuration, for
2634 all modules (useful for recovering files deleted from local copy)
2635 gclient sync --revision src@31000
2636 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002637
2638JSON output format:
2639If the --output-json option is specified, the following document structure will
2640be emitted to the provided file. 'null' entries may occur for subprojects which
2641are present in the gclient solution, but were not processed (due to custom_deps,
2642os_deps, etc.)
2643
2644{
2645 "solutions" : {
2646 "<name>": { # <name> is the posix-normalized path to the solution.
agabled437d762016-10-17 09:35:11 -07002647 "revision": [<git id hex string>|null],
2648 "scm": ["git"|null],
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002649 }
2650 }
2651}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002652""")
2653def CMDsync(parser, args):
2654 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002655 parser.add_option('-f', '--force', action='store_true',
2656 help='force update even for unchanged modules')
2657 parser.add_option('-n', '--nohooks', action='store_true',
2658 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002659 parser.add_option('-p', '--noprehooks', action='store_true',
2660 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002661 parser.add_option('-r', '--revision', action='append',
2662 dest='revisions', metavar='REV', default=[],
2663 help='Enforces revision/hash for the solutions with the '
2664 'format src@rev. The src@ part is optional and can be '
Edward Lesmes53014652018-03-07 18:01:40 -05002665 'skipped. You can also specify URLs instead of paths '
2666 'and gclient will find the solution corresponding to '
2667 'the given URL. If a path is also specified, the URL '
2668 'takes precedence. -r can be used multiple times when '
2669 '.gclient has multiple solutions configured, and will '
2670 'work even if the src@ part is skipped.')
Edward Lesmesc621b212018-03-21 20:26:56 -04002671 parser.add_option('--patch-ref', action='append',
2672 dest='patch_refs', metavar='GERRIT_REF', default=[],
2673 help='Patches the given reference with the format dep@ref. '
2674 'For dep, you can specify URLs as well as paths, with '
2675 'URLs taking preference. The reference will be '
2676 'applied to the necessary path, will be rebased on '
2677 'top what the dep was synced to, and then will do a '
2678 'soft reset. Use --no-rebase-patch-ref and '
2679 '--reset-patch-ref to disable this behavior.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00002680 parser.add_option('--with_branch_heads', action='store_true',
2681 help='Clone git "branch_heads" refspecs in addition to '
2682 'the default refspecs. This adds about 1/2GB to a '
2683 'full checkout. (git only)')
szager@chromium.org8d3348f2014-08-19 22:49:16 +00002684 parser.add_option('--with_tags', action='store_true',
2685 help='Clone git tags in addition to the default refspecs.')
agable2697cd12016-06-28 10:23:53 -07002686 parser.add_option('-H', '--head', action='store_true',
agablea98a6cd2016-11-15 14:30:10 -08002687 help='DEPRECATED: only made sense with safesync urls.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002688 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002689 help='Deletes from the working copy any dependencies that '
2690 'have been removed since the last sync, as long as '
2691 'there are no local modifications. When used with '
2692 '--force, such dependencies are removed even if they '
2693 'have local modifications. When used with --reset, '
2694 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002695 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002696 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002697 parser.add_option('-R', '--reset', action='store_true',
2698 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00002699 parser.add_option('-M', '--merge', action='store_true',
2700 help='merge upstream changes instead of trying to '
2701 'fast-forward or rebase')
dnj@chromium.org5b23e872015-02-20 21:25:57 +00002702 parser.add_option('-A', '--auto_rebase', action='store_true',
2703 help='Automatically rebase repositories against local '
2704 'checkout during update (git only).')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002705 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2706 help='override deps for the specified (comma-separated) '
2707 'platform(s); \'all\' will process all deps_os '
2708 'references')
Paweł Hajdan, Jr357415c2017-07-24 14:35:28 +02002709 # TODO(phajdan.jr): use argparse.SUPPRESS to hide internal flags.
2710 parser.add_option('--do-not-merge-os-specific-entries', action='store_true',
2711 help='INTERNAL ONLY - disables merging of deps_os and '
2712 'hooks_os to dependencies and hooks')
Paweł Hajdan, Jr5ec77132017-08-16 19:21:06 +02002713 parser.add_option('--process-all-deps', action='store_true',
2714 help='Check out all deps, even for different OS-es, '
2715 'or with conditions evaluating to false')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002716 parser.add_option('--upstream', action='store_true',
2717 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002718 parser.add_option('--output-json',
2719 help='Output a json document to this path containing '
2720 'summary information about the sync.')
primiano@chromium.org5439ea52014-08-06 17:18:18 +00002721 parser.add_option('--no-history', action='store_true',
2722 help='GIT ONLY - Reduces the size/time of the checkout at '
2723 'the cost of no history. Requires Git 1.9+')
hinoka@chromium.org46b87412014-05-15 00:42:05 +00002724 parser.add_option('--shallow', action='store_true',
2725 help='GIT ONLY - Do a shallow clone into the cache dir. '
2726 'Requires Git 1.9+')
e.hakkinen@samsung.come8bc1aa2015-04-08 08:00:37 +00002727 parser.add_option('--no_bootstrap', '--no-bootstrap',
2728 action='store_true',
2729 help='Don\'t bootstrap from Google Storage.')
Vadim Shtayura08049e22017-10-11 00:14:52 +00002730 parser.add_option('--ignore_locks', action='store_true',
2731 help='GIT ONLY - Ignore cache locks.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +00002732 parser.add_option('--break_repo_locks', action='store_true',
2733 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2734 'index.lock). This should only be used if you know for '
2735 'certain that this invocation of gclient is the only '
2736 'thing operating on the git repos (e.g. on a bot).')
Vadim Shtayura08049e22017-10-11 00:14:52 +00002737 parser.add_option('--lock_timeout', type='int', default=5000,
2738 help='GIT ONLY - Deadline (in seconds) to wait for git '
2739 'cache lock to become available. Default is %default.')
agabled437d762016-10-17 09:35:11 -07002740 # TODO(agable): Remove these when the oldest CrOS release milestone is M56.
2741 parser.add_option('-t', '--transitive', action='store_true',
2742 help='DEPRECATED: This is a no-op.')
sdefresne69b1be12016-10-18 05:48:02 -07002743 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
agabled437d762016-10-17 09:35:11 -07002744 help='DEPRECATED: This is a no-op.')
Paweł Hajdan, Jr7c7b5592017-05-23 15:06:05 +02002745 # TODO(phajdan.jr): Remove validation options once default (crbug/570091).
Paweł Hajdan, Jr694773d2017-05-29 16:06:23 +02002746 parser.add_option('--validate-syntax', action='store_true', default=True,
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02002747 help='Validate the .gclient and DEPS syntax')
Paweł Hajdan, Jr7c7b5592017-05-23 15:06:05 +02002748 parser.add_option('--disable-syntax-validation', action='store_false',
2749 dest='validate_syntax',
2750 help='Disable validation of .gclient and DEPS syntax.')
Edward Lesmesc621b212018-03-21 20:26:56 -04002751 parser.add_option('--no-rebase-patch-ref', action='store_false',
2752 dest='rebase_patch_ref', default=True,
2753 help='Bypass rebase of the patch ref after checkout.')
2754 parser.add_option('--no-reset-patch-ref', action='store_false',
2755 dest='reset_patch_ref', default=True,
2756 help='Bypass calling reset after patching the ref.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002757 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002758 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002759
2760 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002761 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002762
smutae7ea312016-07-18 11:59:41 -07002763 if options.revisions and options.head:
2764 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
2765 print('Warning: you cannot use both --head and --revision')
2766
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002767 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002768 client.PrintLocationAndContents()
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002769 ret = client.RunOnDeps('update', args)
2770 if options.output_json:
2771 slns = {}
2772 for d in client.subtree(True):
2773 normed = d.name.replace('\\', '/').rstrip('/') + '/'
2774 slns[normed] = {
2775 'revision': d.got_revision,
2776 'scm': d.used_scm.name if d.used_scm else None,
hinoka@chromium.org17db9052014-05-10 01:11:29 +00002777 'url': str(d.url) if d.url else None,
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002778 }
2779 with open(options.output_json, 'wb') as f:
2780 json.dump({'solutions': slns}, f)
2781 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002782
2783
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002784CMDupdate = CMDsync
2785
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002786
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02002787def CMDvalidate(parser, args):
2788 """Validates the .gclient and DEPS syntax."""
2789 options, args = parser.parse_args(args)
2790 options.validate_syntax = True
2791 client = GClient.LoadCurrentConfig(options)
2792 rv = client.RunOnDeps('validate', args)
2793 if rv == 0:
2794 print('validate: SUCCESS')
2795 else:
2796 print('validate: FAILURE')
2797 return rv
2798
2799
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002800def CMDdiff(parser, args):
2801 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002802 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2803 help='override deps for the specified (comma-separated) '
2804 'platform(s); \'all\' will process all deps_os '
2805 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002806 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002807 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002808 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002809 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002810 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002811 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002812 return client.RunOnDeps('diff', args)
2813
2814
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002815def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002816 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00002817
2818 That's the nuclear option to get back to a 'clean' state. It removes anything
agabled437d762016-10-17 09:35:11 -07002819 that shows up in git status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002820 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2821 help='override deps for the specified (comma-separated) '
2822 'platform(s); \'all\' will process all deps_os '
2823 'references')
2824 parser.add_option('-n', '--nohooks', action='store_true',
2825 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002826 parser.add_option('-p', '--noprehooks', action='store_true',
2827 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002828 parser.add_option('--upstream', action='store_true',
2829 help='Make repo state match upstream branch.')
iannucci@chromium.orgbf525dc2016-04-07 22:00:28 +00002830 parser.add_option('--break_repo_locks', action='store_true',
2831 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2832 'index.lock). This should only be used if you know for '
2833 'certain that this invocation of gclient is the only '
2834 'thing operating on the git repos (e.g. on a bot).')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002835 (options, args) = parser.parse_args(args)
2836 # --force is implied.
2837 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002838 options.reset = False
2839 options.delete_unversioned_trees = False
agablec903d732016-07-26 09:07:24 -07002840 options.merge = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002841 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002842 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002843 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002844 return client.RunOnDeps('revert', args)
2845
2846
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002847def CMDrunhooks(parser, args):
2848 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002849 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2850 help='override deps for the specified (comma-separated) '
2851 'platform(s); \'all\' will process all deps_os '
2852 'references')
2853 parser.add_option('-f', '--force', action='store_true', default=True,
2854 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002855 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002856 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002857 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002858 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002859 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002860 client.PrintLocationAndContents()
maruel@chromium.org5df6a462009-08-28 18:52:26 +00002861 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002862 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002863 return client.RunOnDeps('runhooks', args)
2864
2865
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002866def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002867 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002868
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002869 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002870 can be used to reproduce the same tree in the future. It is only useful for
agabled437d762016-10-17 09:35:11 -07002871 'unpinned dependencies', i.e. DEPS/deps references without a git hash.
2872 A git branch name isn't 'pinned' since the actual commit can change.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002873 """
2874 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2875 help='override deps for the specified (comma-separated) '
2876 'platform(s); \'all\' will process all deps_os '
2877 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002878 parser.add_option('-a', '--actual', action='store_true',
2879 help='gets the actual checked out revisions instead of the '
2880 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002881 parser.add_option('-s', '--snapshot', action='store_true',
2882 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002883 'version of all repositories to reproduce the tree, '
2884 'implies -a')
Edward Lesmesbb16e332018-03-30 17:54:51 -04002885 parser.add_option('--filter', action='append', dest='filter',
Edward Lesmesdaa76d22018-03-06 14:56:57 -05002886 help='Display revision information only for the specified '
Edward Lesmesbb16e332018-03-30 17:54:51 -04002887 'dependencies (filtered by URL or path).')
Edward Lesmesc2960242018-03-06 20:50:15 -05002888 parser.add_option('--output-json',
2889 help='Output a json document to this path containing '
2890 'information about the revisions.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002891 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002892 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002893 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002894 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002895 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002896 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002897
2898
Edward Lesmes411041f2018-04-05 20:12:55 -04002899def CMDgetdep(parser, args):
2900 """Gets revision information and variable values from a DEPS file."""
2901 parser.add_option('--var', action='append',
2902 dest='vars', metavar='VAR', default=[],
2903 help='Gets the value of a given variable.')
2904 parser.add_option('-r', '--revision', action='append',
2905 dest='revisions', metavar='DEP', default=[],
2906 help='Gets the revision/version for the given dependency. '
2907 'If it is a git dependency, dep must be a path. If it '
2908 'is a CIPD dependency, dep must be of the form '
2909 'path:package.')
2910 parser.add_option('--deps-file', default='DEPS',
2911 # TODO(ehmaldonado): Try to find the DEPS file pointed by
2912 # .gclient first.
2913 help='The DEPS file to be edited. Defaults to the DEPS '
2914 'file in the current directory.')
2915 (options, args) = parser.parse_args(args)
2916
2917 if not os.path.isfile(options.deps_file):
2918 raise gclient_utils.Error(
2919 'DEPS file %s does not exist.' % options.deps_file)
2920 with open(options.deps_file) as f:
2921 contents = f.read()
2922 local_scope = gclient_eval.Parse(
2923 contents, expand_vars=True, validate_syntax=True,
2924 filename=options.deps_file)
2925
2926 for var in options.vars:
2927 print(gclient_eval.GetVar(local_scope, var))
2928
2929 for name in options.revisions:
2930 if ':' in name:
2931 name, _, package = name.partition(':')
2932 if not name or not package:
2933 parser.error(
2934 'Wrong CIPD format: %s:%s should be of the form path:pkg.'
2935 % (name, package))
2936 print(gclient_eval.GetCIPD(local_scope, name, package))
2937 else:
2938 print(gclient_eval.GetRevision(local_scope, name))
2939
2940
Edward Lesmes6f64a052018-03-20 17:35:49 -04002941def CMDsetdep(parser, args):
Edward Lesmes0ecf6d62018-04-05 18:28:55 -04002942 """Modifies dependency revisions and variable values in a DEPS file"""
Edward Lesmes6f64a052018-03-20 17:35:49 -04002943 parser.add_option('--var', action='append',
2944 dest='vars', metavar='VAR=VAL', default=[],
2945 help='Sets a variable to the given value with the format '
2946 'name=value.')
2947 parser.add_option('-r', '--revision', action='append',
2948 dest='revisions', metavar='DEP@REV', default=[],
2949 help='Sets the revision/version for the dependency with '
2950 'the format dep@rev. If it is a git dependency, dep '
2951 'must be a path and rev must be a git hash or '
2952 'reference (e.g. src/dep@deadbeef). If it is a CIPD '
2953 'dependency, dep must be of the form path:package and '
2954 'rev must be the package version '
2955 '(e.g. src/pkg:chromium/pkg@2.1-cr0).')
2956 parser.add_option('--deps-file', default='DEPS',
2957 # TODO(ehmaldonado): Try to find the DEPS file pointed by
2958 # .gclient first.
2959 help='The DEPS file to be edited. Defaults to the DEPS '
2960 'file in the current directory.')
2961 (options, args) = parser.parse_args(args)
Edward Lesmes0ecf6d62018-04-05 18:28:55 -04002962 if args:
2963 parser.error('Unused arguments: "%s"' % '" "'.join(args))
2964 if not options.revisions and not options.vars:
2965 parser.error(
2966 'You must specify at least one variable or revision to modify.')
Edward Lesmes6f64a052018-03-20 17:35:49 -04002967
Edward Lesmes6f64a052018-03-20 17:35:49 -04002968 if not os.path.isfile(options.deps_file):
2969 raise gclient_utils.Error(
2970 'DEPS file %s does not exist.' % options.deps_file)
2971 with open(options.deps_file) as f:
2972 contents = f.read()
Edward Lesmes6c24d372018-03-28 12:52:29 -04002973 local_scope = gclient_eval.Parse(
2974 contents, expand_vars=True, validate_syntax=True,
2975 filename=options.deps_file)
Edward Lesmes6f64a052018-03-20 17:35:49 -04002976
2977 for var in options.vars:
2978 name, _, value = var.partition('=')
2979 if not name or not value:
Edward Lesmes0ecf6d62018-04-05 18:28:55 -04002980 parser.error(
Edward Lesmes6f64a052018-03-20 17:35:49 -04002981 'Wrong var format: %s should be of the form name=value.' % var)
Edward Lesmes3d993812018-04-02 12:52:49 -04002982 if name in local_scope['vars']:
2983 gclient_eval.SetVar(local_scope, name, value)
2984 else:
2985 gclient_eval.AddVar(local_scope, name, value)
Edward Lesmes6f64a052018-03-20 17:35:49 -04002986
2987 for revision in options.revisions:
2988 name, _, value = revision.partition('@')
2989 if not name or not value:
Edward Lesmes0ecf6d62018-04-05 18:28:55 -04002990 parser.error(
Edward Lesmes6f64a052018-03-20 17:35:49 -04002991 'Wrong dep format: %s should be of the form dep@rev.' % revision)
2992 if ':' in name:
2993 name, _, package = name.partition(':')
2994 if not name or not package:
Edward Lesmes0ecf6d62018-04-05 18:28:55 -04002995 parser.error(
Edward Lesmes6f64a052018-03-20 17:35:49 -04002996 'Wrong CIPD format: %s:%s should be of the form path:pkg@version.'
2997 % (name, package))
2998 gclient_eval.SetCIPD(local_scope, name, package, value)
2999 else:
Edward Lesmes9f531292018-03-20 21:27:15 -04003000 gclient_eval.SetRevision(local_scope, name, value)
Edward Lesmes6f64a052018-03-20 17:35:49 -04003001
3002 with open(options.deps_file, 'w') as f:
3003 f.write(gclient_eval.RenderDEPSFile(local_scope))
3004
3005
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00003006def CMDverify(parser, args):
3007 """Verifies the DEPS file deps are only from allowed_hosts."""
3008 (options, args) = parser.parse_args(args)
3009 client = GClient.LoadCurrentConfig(options)
3010 if not client:
3011 raise gclient_utils.Error('client not configured; see \'gclient config\'')
3012 client.RunOnDeps(None, [])
3013 # Look at each first-level dependency of this gclient only.
3014 for dep in client.dependencies:
3015 bad_deps = dep.findDepsFromNotAllowedHosts()
3016 if not bad_deps:
3017 continue
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00003018 print("There are deps from not allowed hosts in file %s" % dep.deps_file)
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00003019 for bad_dep in bad_deps:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00003020 print("\t%s at %s" % (bad_dep.name, bad_dep.url))
3021 print("allowed_hosts:", ', '.join(dep.allowed_hosts))
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00003022 sys.stdout.flush()
3023 raise gclient_utils.Error(
3024 'dependencies from disallowed hosts; check your DEPS file.')
3025 return 0
3026
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003027class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00003028 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003029
3030 def __init__(self, **kwargs):
3031 optparse.OptionParser.__init__(
3032 self, version='%prog ' + __version__, **kwargs)
3033
3034 # Some arm boards have issues with parallel sync.
3035 if platform.machine().startswith('arm'):
3036 jobs = 1
3037 else:
3038 jobs = max(8, gclient_utils.NumLocalCpus())
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003039
3040 self.add_option(
3041 '-j', '--jobs', default=jobs, type='int',
3042 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00003043 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003044 self.add_option(
3045 '-v', '--verbose', action='count', default=0,
3046 help='Produces additional output for diagnostics. Can be used up to '
3047 'three times for more logging info.')
3048 self.add_option(
3049 '--gclientfile', dest='config_filename',
3050 help='Specify an alternate %s file' % self.gclientfile_default)
3051 self.add_option(
3052 '--spec',
3053 help='create a gclient file containing the provided string. Due to '
3054 'Cygwin/Python brokenness, it can\'t contain any newlines.')
3055 self.add_option(
Aleksandr Derbenev9e8fb0e2017-08-01 20:18:31 +03003056 '--cache-dir',
3057 help='(git only) Cache all git repos into this dir and do '
3058 'shared clones from the cache, instead of cloning '
3059 'directly from the remote. (experimental)',
3060 default=os.environ.get('GCLIENT_CACHE_DIR'))
3061 self.add_option(
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003062 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00003063 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003064
3065 def parse_args(self, args=None, values=None):
3066 """Integrates standard options processing."""
3067 options, args = optparse.OptionParser.parse_args(self, args, values)
3068 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
3069 logging.basicConfig(
3070 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00003071 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00003072 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003073 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00003074 if (options.config_filename and
3075 options.config_filename != os.path.basename(options.config_filename)):
3076 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00003077 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003078 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00003079 options.entries_filename = options.config_filename + '_entries'
3080 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003081 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00003082
3083 # These hacks need to die.
3084 if not hasattr(options, 'revisions'):
3085 # GClient.RunOnDeps expects it even if not applicable.
3086 options.revisions = []
smutae7ea312016-07-18 11:59:41 -07003087 if not hasattr(options, 'head'):
3088 options.head = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00003089 if not hasattr(options, 'nohooks'):
3090 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00003091 if not hasattr(options, 'noprehooks'):
3092 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00003093 if not hasattr(options, 'deps_os'):
3094 options.deps_os = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00003095 if not hasattr(options, 'force'):
3096 options.force = None
3097 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00003098
maruel@chromium.org39c0b222013-08-17 16:57:01 +00003099
3100def disable_buffering():
3101 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
3102 # operations. Python as a strong tendency to buffer sys.stdout.
3103 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
3104 # Make stdout annotated with the thread ids.
3105 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00003106
3107
sbc@chromium.org013731e2015-02-26 18:28:43 +00003108def main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00003109 """Doesn't parse the arguments here, just find the right subcommand to
3110 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00003111 if sys.hexversion < 0x02060000:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00003112 print(
maruel@chromium.org82798cb2012-02-23 18:16:12 +00003113 '\nYour python version %s is unsupported, please upgrade.\n' %
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00003114 sys.version.split(' ', 1)[0],
3115 file=sys.stderr)
maruel@chromium.org82798cb2012-02-23 18:16:12 +00003116 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00003117 if not sys.executable:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00003118 print(
3119 '\nPython cannot find the location of it\'s own executable.\n',
3120 file=sys.stderr)
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00003121 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00003122 fix_encoding.fix_encoding()
3123 disable_buffering()
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00003124 setup_color.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00003125 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00003126 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00003127 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00003128 except KeyboardInterrupt:
3129 gclient_utils.GClientChildren.KillAllRemainingChildren()
3130 raise
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00003131 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00003132 print('Error: %s' % str(e), file=sys.stderr)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00003133 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00003134 finally:
3135 gclient_utils.PrintWarnings()
sbc@chromium.org013731e2015-02-26 18:28:43 +00003136 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00003137
3138
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00003139if '__main__' == __name__:
sbc@chromium.org013731e2015-02-26 18:28:43 +00003140 try:
3141 sys.exit(main(sys.argv[1:]))
3142 except KeyboardInterrupt:
3143 sys.stderr.write('interrupted\n')
3144 sys.exit(1)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00003145
3146# vim: ts=2:sw=2:tw=80:et: