blob: e651c6cb20b88f15bd5a076b2b295d9a4ff4a5c5 [file] [log] [blame]
iannucci@chromium.org405b87e2015-11-12 18:08:34 +00001#!/usr/bin/env python
thakis@chromium.org4f474b62012-01-18 01:31:29 +00002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
maruel@chromium.orgba551772010-02-03 18:21:42 +00003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00005
agabled437d762016-10-17 09:35:11 -07006"""Meta checkout dependency manager for Git."""
maruel@chromium.org39c0b222013-08-17 16:57:01 +00007# Files
8# .gclient : Current client configuration, written by 'config' command.
9# Format is a Python script defining 'solutions', a list whose
10# entries each are maps binding the strings "name" and "url"
11# to strings specifying the name and location of the client
12# module, as well as "custom_deps" to a map similar to the
13# deps section of the DEPS file below, as well as
14# "custom_hooks" to a list similar to the hooks sections of
15# the DEPS file below.
16# .gclient_entries : A cache constructed by 'update' command. Format is a
17# Python script defining 'entries', a list of the names
18# of all modules in the client
19# <module>/DEPS : Python script defining var 'deps' as a map from each
20# requisite submodule name to a URL where it can be found (via
21# one SCM)
22#
23# Hooks
24# .gclient and DEPS files may optionally contain a list named "hooks" to
25# allow custom actions to be performed based on files that have changed in the
26# working copy as a result of a "sync"/"update" or "revert" operation. This
27# can be prevented by using --nohooks (hooks run by default). Hooks can also
28# be forced to run with the "runhooks" operation. If "sync" is run with
29# --force, all known but not suppressed hooks will run regardless of the state
30# of the working copy.
31#
32# Each item in a "hooks" list is a dict, containing these two keys:
33# "pattern" The associated value is a string containing a regular
34# expression. When a file whose pathname matches the expression
35# is checked out, updated, or reverted, the hook's "action" will
36# run.
37# "action" A list describing a command to run along with its arguments, if
38# any. An action command will run at most one time per gclient
39# invocation, regardless of how many files matched the pattern.
40# The action is executed in the same directory as the .gclient
41# file. If the first item in the list is the string "python",
42# the current Python interpreter (sys.executable) will be used
43# to run the command. If the list contains string
44# "$matching_files" it will be removed from the list and the list
45# will be extended by the list of matching files.
46# "name" An optional string specifying the group to which a hook belongs
47# for overriding and organizing.
48#
49# Example:
50# hooks = [
51# { "pattern": "\\.(gif|jpe?g|pr0n|png)$",
52# "action": ["python", "image_indexer.py", "--all"]},
53# { "pattern": ".",
54# "name": "gyp",
55# "action": ["python", "src/build/gyp_chromium"]},
56# ]
57#
borenet@google.com2d1ee9e2013-10-15 08:13:16 +000058# Pre-DEPS Hooks
59# DEPS files may optionally contain a list named "pre_deps_hooks". These are
60# the same as normal hooks, except that they run before the DEPS are
61# processed. Pre-DEPS run with "sync" and "revert" unless the --noprehooks
62# flag is used.
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +000063#
maruel@chromium.org39c0b222013-08-17 16:57:01 +000064# Specifying a target OS
65# An optional key named "target_os" may be added to a gclient file to specify
66# one or more additional operating systems that should be considered when
Scott Grahamc4826742017-05-11 16:59:23 -070067# processing the deps_os/hooks_os dict of a DEPS file.
maruel@chromium.org39c0b222013-08-17 16:57:01 +000068#
69# Example:
70# target_os = [ "android" ]
71#
72# If the "target_os_only" key is also present and true, then *only* the
73# operating systems listed in "target_os" will be used.
74#
75# Example:
76# target_os = [ "ios" ]
77# target_os_only = True
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000078
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +000079from __future__ import print_function
80
maruel@chromium.org39c0b222013-08-17 16:57:01 +000081__version__ = '0.7'
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000082
szager@chromium.org7b8b6de2014-08-23 00:57:31 +000083import ast
maruel@chromium.org9e5317a2010-08-13 20:35:11 +000084import copy
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +000085import json
maruel@chromium.org754960e2009-09-21 12:31:05 +000086import logging
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000087import optparse
88import os
bradnelson@google.com4949dab2012-04-19 16:41:07 +000089import platform
maruel@chromium.org621939b2010-08-10 20:12:00 +000090import posixpath
msb@chromium.org2e38de72009-09-28 17:04:47 +000091import pprint
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000092import re
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000093import sys
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +000094import time
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000095import urllib
bradnelson@google.com4949dab2012-04-19 16:41:07 +000096import urlparse
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000097
maruel@chromium.org35625c72011-03-23 17:34:02 +000098import fix_encoding
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +020099import gclient_eval
maruel@chromium.org5f3eee32009-09-17 00:34:30 +0000100import gclient_scm
101import gclient_utils
szager@chromium.org848fd492014-04-09 19:06:44 +0000102import git_cache
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000103from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000104import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000105import subprocess2
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +0000106import setup_color
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000107
szager@chromium.org7b8b6de2014-08-23 00:57:31 +0000108CHROMIUM_SRC_URL = 'https://chromium.googlesource.com/chromium/src.git'
109
110
111def ast_dict_index(dnode, key):
112 """Search an ast.Dict for the argument key, and return its index."""
113 idx = [i for i in range(len(dnode.keys)) if (
114 type(dnode.keys[i]) is ast.Str and dnode.keys[i].s == key)]
115 if not idx:
116 return -1
117 elif len(idx) > 1:
118 raise gclient_utils.Error('Multiple dict entries with same key in AST')
119 return idx[-1]
120
121def ast2str(node, indent=0):
122 """Return a pretty-printed rendition of an ast.Node."""
123 t = type(node)
124 if t is ast.Module:
125 return '\n'.join([ast2str(x, indent) for x in node.body])
126 elif t is ast.Assign:
127 return ((' ' * indent) +
128 ' = '.join([ast2str(x) for x in node.targets] +
129 [ast2str(node.value, indent)]) + '\n')
130 elif t is ast.Name:
131 return node.id
132 elif t is ast.List:
133 if not node.elts:
134 return '[]'
135 elif len(node.elts) == 1:
136 return '[' + ast2str(node.elts[0], indent) + ']'
137 return ('[\n' + (' ' * (indent + 1)) +
138 (',\n' + (' ' * (indent + 1))).join(
139 [ast2str(x, indent + 1) for x in node.elts]) +
140 '\n' + (' ' * indent) + ']')
141 elif t is ast.Dict:
142 if not node.keys:
143 return '{}'
144 elif len(node.keys) == 1:
145 return '{%s: %s}' % (ast2str(node.keys[0]),
146 ast2str(node.values[0], indent + 1))
147 return ('{\n' + (' ' * (indent + 1)) +
148 (',\n' + (' ' * (indent + 1))).join(
149 ['%s: %s' % (ast2str(node.keys[i]),
150 ast2str(node.values[i], indent + 1))
151 for i in range(len(node.keys))]) +
152 '\n' + (' ' * indent) + '}')
153 elif t is ast.Str:
154 return "'%s'" % node.s
155 else:
156 raise gclient_utils.Error("Unexpected AST node at line %d, column %d: %s"
157 % (node.lineno, node.col_offset, t))
158
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000159
maruel@chromium.org116704f2010-06-11 17:34:38 +0000160class GClientKeywords(object):
maruel@chromium.org116704f2010-06-11 17:34:38 +0000161 class VarImpl(object):
162 def __init__(self, custom_vars, local_scope):
163 self._custom_vars = custom_vars
164 self._local_scope = local_scope
165
166 def Lookup(self, var_name):
167 """Implements the Var syntax."""
168 if var_name in self._custom_vars:
169 return self._custom_vars[var_name]
170 elif var_name in self._local_scope.get("vars", {}):
171 return self._local_scope["vars"][var_name]
172 raise gclient_utils.Error("Var is not defined: %s" % var_name)
173
174
maruel@chromium.org064186c2011-09-27 23:53:33 +0000175class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000176 """Immutable configuration settings."""
177 def __init__(
agablea98a6cd2016-11-15 14:30:10 -0800178 self, parent, url, managed, custom_deps, custom_vars,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200179 custom_hooks, deps_file, should_process, relative,
180 condition, condition_value):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000181 GClientKeywords.__init__(self)
182
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000183 # These are not mutable:
184 self._parent = parent
mmoss@chromium.org8f93f792014-08-26 23:24:09 +0000185 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000186 self._url = url
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200187 # The condition as string (or None). Useful to keep e.g. for flatten.
188 self._condition = condition
189 # Boolean value of the condition. If there's no condition, just True.
190 self._condition_value = condition_value
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000191 # 'managed' determines whether or not this dependency is synced/updated by
192 # gclient after gclient checks it out initially. The difference between
193 # 'managed' and 'should_process' is that the user specifies 'managed' via
smutae7ea312016-07-18 11:59:41 -0700194 # the --unmanaged command-line flag or a .gclient config, where
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000195 # 'should_process' is dynamically set by gclient if it goes over its
196 # recursion limit and controls gclient's behavior so it does not misbehave.
197 self._managed = managed
198 self._should_process = should_process
agabledce6ddc2016-09-08 10:02:16 -0700199 # If this is a recursed-upon sub-dependency, and the parent has
200 # use_relative_paths set, then this dependency should check out its own
201 # dependencies relative to that parent's path for this, rather than
202 # relative to the .gclient file.
203 self._relative = relative
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000204 # This is a mutable value which has the list of 'target_os' OSes listed in
205 # the current deps file.
206 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000207
208 # These are only set in .gclient and not in DEPS files.
209 self._custom_vars = custom_vars or {}
210 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000211 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000212
maruel@chromium.org064186c2011-09-27 23:53:33 +0000213 # Post process the url to remove trailing slashes.
214 if isinstance(self._url, basestring):
215 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
216 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000217 self._url = self._url.replace('/@', '@')
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200218 elif not isinstance(self._url, (None.__class__)):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000219 raise gclient_utils.Error(
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200220 ('dependency url must be either string or None, '
221 'instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000222 # Make any deps_file path platform-appropriate.
223 for sep in ['/', '\\']:
224 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000225
226 @property
227 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000228 return self._deps_file
229
230 @property
231 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000232 return self._managed
233
234 @property
235 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000236 return self._parent
237
238 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000239 def root(self):
240 """Returns the root node, a GClient object."""
241 if not self.parent:
242 # This line is to signal pylint that it could be a GClient instance.
243 return self or GClient(None, None)
244 return self.parent.root
245
246 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000247 def should_process(self):
248 """True if this dependency should be processed, i.e. checked out."""
249 return self._should_process
250
251 @property
252 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000253 return self._custom_vars.copy()
254
255 @property
256 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000257 return self._custom_deps.copy()
258
maruel@chromium.org064186c2011-09-27 23:53:33 +0000259 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000260 def custom_hooks(self):
261 return self._custom_hooks[:]
262
263 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000264 def url(self):
265 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000266
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000267 @property
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200268 def condition(self):
269 return self._condition
270
271 @property
272 def condition_value(self):
273 return self._condition_value
274
275 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000276 def target_os(self):
277 if self.local_target_os is not None:
278 return tuple(set(self.local_target_os).union(self.parent.target_os))
279 else:
280 return self.parent.target_os
281
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000282 def get_custom_deps(self, name, url):
283 """Returns a custom deps if applicable."""
284 if self.parent:
285 url = self.parent.get_custom_deps(name, url)
286 # None is a valid return value to disable a dependency.
287 return self.custom_deps.get(name, url)
288
maruel@chromium.org064186c2011-09-27 23:53:33 +0000289
290class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000291 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000292
agablea98a6cd2016-11-15 14:30:10 -0800293 def __init__(self, parent, name, url, managed, custom_deps,
agabledce6ddc2016-09-08 10:02:16 -0700294 custom_vars, custom_hooks, deps_file, should_process,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200295 relative, condition, condition_value):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000296 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000297 DependencySettings.__init__(
agablea98a6cd2016-11-15 14:30:10 -0800298 self, parent, url, managed, custom_deps, custom_vars,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200299 custom_hooks, deps_file, should_process, relative,
300 condition, condition_value)
maruel@chromium.org68988972011-09-20 14:11:42 +0000301
302 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000303 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000304
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000305 self._pre_deps_hooks = []
306
maruel@chromium.org68988972011-09-20 14:11:42 +0000307 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000308 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000309 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000310 # A cache of the files affected by the current operation, necessary for
311 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000312 self._file_list = []
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000313 # List of host names from which dependencies are allowed.
314 # Default is an empty set, meaning unspecified in DEPS file, and hence all
315 # hosts will be allowed. Non-empty set means whitelist of hosts.
316 # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
317 self._allowed_hosts = frozenset()
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000318 # If it is not set to True, the dependency wasn't processed for its child
319 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000320 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000321 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000322 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000323 # This dependency had its pre-DEPS hooks run
324 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000325 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000326 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000327 # This is the scm used to checkout self.url. It may be used by dependencies
328 # to get the datetime of the revision we checked out.
329 self._used_scm = None
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000330 self._used_revision = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000331 # The actual revision we ended up getting, or None if that information is
332 # unavailable
333 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000334
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000335 # This is a mutable value that overrides the normal recursion limit for this
336 # dependency. It is read from the actual DEPS file so cannot be set on
337 # class instantiation.
338 self.recursion_override = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000339 # recursedeps is a mutable value that selectively overrides the default
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000340 # 'no recursion' setting on a dep-by-dep basis. It will replace
341 # recursion_override.
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000342 #
343 # It will be a dictionary of {deps_name: {"deps_file": depfile_name}} or
344 # None.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000345 self.recursedeps = None
hinoka885e5b12016-06-08 14:40:09 -0700346 # This is inherited from WorkItem. We want the URL to be a resource.
347 if url and isinstance(url, basestring):
348 # The url is usually given to gclient either as https://blah@123
qyearsley12fa6ff2016-08-24 09:18:40 -0700349 # or just https://blah. The @123 portion is irrelevant.
hinoka885e5b12016-06-08 14:40:09 -0700350 self.resources.append(url.split('@')[0])
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000351
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000352 if not self.name and self.parent:
353 raise gclient_utils.Error('Dependency without name')
354
maruel@chromium.org470b5432011-10-11 18:18:19 +0000355 @property
356 def requirements(self):
357 """Calculate the list of requirements."""
358 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000359 # self.parent is implicitly a requirement. This will be recursive by
360 # definition.
361 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000362 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000363
364 # For a tree with at least 2 levels*, the leaf node needs to depend
365 # on the level higher up in an orderly way.
366 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
367 # thus unsorted, while the .gclient format is a list thus sorted.
368 #
369 # * _recursion_limit is hard coded 2 and there is no hope to change this
370 # value.
371 #
372 # Interestingly enough, the following condition only works in the case we
373 # want: self is a 2nd level node. 3nd level node wouldn't need this since
374 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000375 if self.parent and self.parent.parent and not self.parent.parent.parent:
376 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000377
maruel@chromium.org470b5432011-10-11 18:18:19 +0000378 if self.name:
379 requirements |= set(
380 obj.name for obj in self.root.subtree(False)
381 if (obj is not self
382 and obj.name and
383 self.name.startswith(posixpath.join(obj.name, ''))))
384 requirements = tuple(sorted(requirements))
385 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
386 return requirements
387
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000388 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000389 def try_recursedeps(self):
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000390 """Returns False if recursion_override is ever specified."""
391 if self.recursion_override is not None:
392 return False
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000393 return self.parent.try_recursedeps
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000394
395 @property
396 def recursion_limit(self):
397 """Returns > 0 if this dependency is not too recursed to be processed."""
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000398 # We continue to support the absence of recursedeps until tools and DEPS
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000399 # using recursion_override are updated.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000400 if self.try_recursedeps and self.parent.recursedeps != None:
401 if self.name in self.parent.recursedeps:
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000402 return 1
403
404 if self.recursion_override is not None:
405 return self.recursion_override
406 return max(self.parent.recursion_limit - 1, 0)
407
maruel@chromium.org470b5432011-10-11 18:18:19 +0000408 def verify_validity(self):
409 """Verifies that this Dependency is fine to add as a child of another one.
410
411 Returns True if this entry should be added, False if it is a duplicate of
412 another entry.
413 """
414 logging.info('Dependency(%s).verify_validity()' % self.name)
415 if self.name in [s.name for s in self.parent.dependencies]:
416 raise gclient_utils.Error(
417 'The same name "%s" appears multiple times in the deps section' %
418 self.name)
419 if not self.should_process:
420 # Return early, no need to set requirements.
421 return True
422
423 # This require a full tree traversal with locks.
424 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
425 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000426 self_url = self.LateOverride(self.url)
427 sibling_url = sibling.LateOverride(sibling.url)
428 # Allow to have only one to be None or ''.
429 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000430 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000431 ('Dependency %s specified more than once:\n'
432 ' %s [%s]\n'
433 'vs\n'
434 ' %s [%s]') % (
435 self.name,
436 sibling.hierarchy(),
437 sibling_url,
438 self.hierarchy(),
439 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000440 # In theory we could keep it as a shadow of the other one. In
441 # practice, simply ignore it.
442 logging.warn('Won\'t process duplicate dependency %s' % sibling)
443 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000444 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000445
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000446 def LateOverride(self, url):
Paweł Hajdan, Jr7e9303b2017-05-23 14:38:27 +0200447 """Resolves the parsed url from url."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000448 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000449 parsed_url = self.get_custom_deps(self.name, url)
450 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000451 logging.info(
452 'Dependency(%s).LateOverride(%s) -> %s' %
453 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000454 return parsed_url
455
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000456 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000457 parsed_url = urlparse.urlparse(url)
scr@chromium.orgf1eccaf2014-04-11 15:51:33 +0000458 if (not parsed_url[0] and
459 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000460 # A relative url. Fetch the real base.
461 path = parsed_url[2]
462 if not path.startswith('/'):
463 raise gclient_utils.Error(
464 'relative DEPS entry \'%s\' must begin with a slash' % url)
465 # Create a scm just to query the full url.
466 parent_url = self.parent.parsed_url
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000467 scm = gclient_scm.CreateSCM(
468 parent_url, self.root.root_dir, None, self.outbuf)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000469 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000470 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000471 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000472 logging.info(
473 'Dependency(%s).LateOverride(%s) -> %s' %
474 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000475 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000476
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000477 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000478 logging.info(
479 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000480 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000481
482 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000483
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000484 @staticmethod
485 def MergeWithOsDeps(deps, deps_os, target_os_list):
486 """Returns a new "deps" structure that is the deps sent in updated
487 with information from deps_os (the deps_os section of the DEPS
488 file) that matches the list of target os."""
489 os_overrides = {}
490 for the_target_os in target_os_list:
491 the_target_os_deps = deps_os.get(the_target_os, {})
492 for os_dep_key, os_dep_value in the_target_os_deps.iteritems():
493 overrides = os_overrides.setdefault(os_dep_key, [])
494 overrides.append((the_target_os, os_dep_value))
495
496 # If any os didn't specify a value (we have fewer value entries
497 # than in the os list), then it wants to use the default value.
498 for os_dep_key, os_dep_value in os_overrides.iteritems():
499 if len(os_dep_value) != len(target_os_list):
qyearsley12fa6ff2016-08-24 09:18:40 -0700500 # Record the default value too so that we don't accidentally
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000501 # set it to None or miss a conflicting DEPS.
502 if os_dep_key in deps:
503 os_dep_value.append(('default', deps[os_dep_key]))
504
505 target_os_deps = {}
506 for os_dep_key, os_dep_value in os_overrides.iteritems():
507 # os_dep_value is a list of (os, value) pairs.
508 possible_values = set(x[1] for x in os_dep_value if x[1] is not None)
509 if not possible_values:
510 target_os_deps[os_dep_key] = None
511 else:
512 if len(possible_values) > 1:
513 # It would be possible to abort here but it would be
514 # unfortunate if we end up preventing any kind of checkout.
515 logging.error('Conflicting dependencies for %s: %s. (target_os=%s)',
516 os_dep_key, os_dep_value, target_os_list)
517 # Sorting to get the same result every time in case of conflicts.
518 target_os_deps[os_dep_key] = sorted(possible_values)[0]
519
520 new_deps = deps.copy()
521 new_deps.update(target_os_deps)
522 return new_deps
523
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000524 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000525 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000526 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000527 assert not self.dependencies
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000528
529 deps_content = None
530 use_strict = False
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000531
532 # First try to locate the configured deps file. If it's missing, fallback
533 # to DEPS.
534 deps_files = [self.deps_file]
535 if 'DEPS' not in deps_files:
536 deps_files.append('DEPS')
537 for deps_file in deps_files:
538 filepath = os.path.join(self.root.root_dir, self.name, deps_file)
539 if os.path.isfile(filepath):
540 logging.info(
541 'ParseDepsFile(%s): %s file found at %s', self.name, deps_file,
542 filepath)
543 break
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000544 logging.info(
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000545 'ParseDepsFile(%s): No %s file found at %s', self.name, deps_file,
546 filepath)
547
548 if os.path.isfile(filepath):
maruel@chromium.org46304292010-10-28 11:42:00 +0000549 deps_content = gclient_utils.FileRead(filepath)
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000550 logging.debug('ParseDepsFile(%s) read:\n%s', self.name, deps_content)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000551 use_strict = 'use strict' in deps_content.splitlines()[0]
552
553 local_scope = {}
554 if deps_content:
555 # One thing is unintuitive, vars = {} must happen before Var() use.
556 var = self.VarImpl(self.custom_vars, local_scope)
557 if use_strict:
558 logging.info(
559 'ParseDepsFile(%s): Strict Mode Enabled', self.name)
560 global_scope = {
561 '__builtins__': {'None': None},
562 'Var': var.Lookup,
563 'deps_os': {},
564 }
565 else:
566 global_scope = {
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000567 'Var': var.Lookup,
568 'deps_os': {},
569 }
maruel@chromium.org46304292010-10-28 11:42:00 +0000570 # Eval the content.
571 try:
Paweł Hajdan, Jrc485d5a2017-06-02 12:08:09 +0200572 if self._get_option('validate_syntax', False):
573 gclient_eval.Exec(deps_content, global_scope, local_scope, filepath)
574 else:
575 exec(deps_content, global_scope, local_scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000576 except SyntaxError as e:
maruel@chromium.org46304292010-10-28 11:42:00 +0000577 gclient_utils.SyntaxErrorToError(filepath, e)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000578 if use_strict:
579 for key, val in local_scope.iteritems():
580 if not isinstance(val, (dict, list, tuple, str)):
581 raise gclient_utils.Error(
582 'ParseDepsFile(%s): Strict mode disallows %r -> %r' %
583 (self.name, key, val))
584
maruel@chromium.org271375b2010-06-23 19:17:38 +0000585 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000586 if 'recursion' in local_scope:
587 self.recursion_override = local_scope.get('recursion')
588 logging.warning(
589 'Setting %s recursion to %d.', self.name, self.recursion_limit)
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000590 self.recursedeps = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000591 if 'recursedeps' in local_scope:
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000592 self.recursedeps = {}
593 for ent in local_scope['recursedeps']:
594 if isinstance(ent, basestring):
595 self.recursedeps[ent] = {"deps_file": self.deps_file}
596 else: # (depname, depsfilename)
597 self.recursedeps[ent[0]] = {"deps_file": ent[1]}
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000598 logging.warning('Found recursedeps %r.', repr(self.recursedeps))
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000599 # If present, save 'target_os' in the local_target_os property.
600 if 'target_os' in local_scope:
601 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000602 # load os specific dependencies if defined. these dependencies may
603 # override or extend the values defined by the 'deps' member.
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000604 target_os_list = self.target_os
605 if 'deps_os' in local_scope and target_os_list:
606 deps = self.MergeWithOsDeps(deps, local_scope['deps_os'], target_os_list)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000607
maruel@chromium.org271375b2010-06-23 19:17:38 +0000608 # If a line is in custom_deps, but not in the solution, we want to append
609 # this line to the solution.
610 for d in self.custom_deps:
611 if d not in deps:
612 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000613
614 # If use_relative_paths is set in the DEPS file, regenerate
615 # the dictionary using paths relative to the directory containing
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000616 # the DEPS file. Also update recursedeps if use_relative_paths is
617 # enabled.
agabledce6ddc2016-09-08 10:02:16 -0700618 # If the deps file doesn't set use_relative_paths, but the parent did
619 # (and therefore set self.relative on this Dependency object), then we
620 # want to modify the deps and recursedeps by prepending the parent
621 # directory of this dependency.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000622 use_relative_paths = local_scope.get('use_relative_paths', False)
agabledce6ddc2016-09-08 10:02:16 -0700623 rel_prefix = None
maruel@chromium.org271375b2010-06-23 19:17:38 +0000624 if use_relative_paths:
agabledce6ddc2016-09-08 10:02:16 -0700625 rel_prefix = self.name
626 elif self._relative:
627 rel_prefix = os.path.dirname(self.name)
628 if rel_prefix:
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000629 logging.warning('use_relative_paths enabled.')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000630 rel_deps = {}
631 for d, url in deps.items():
632 # normpath is required to allow DEPS to use .. in their
633 # dependency local path.
agabledce6ddc2016-09-08 10:02:16 -0700634 rel_deps[os.path.normpath(os.path.join(rel_prefix, d))] = url
635 logging.warning('Updating deps by prepending %s.', rel_prefix)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000636 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000637
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000638 # Update recursedeps if it's set.
639 if self.recursedeps is not None:
agabledce6ddc2016-09-08 10:02:16 -0700640 logging.warning('Updating recursedeps by prepending %s.', rel_prefix)
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000641 rel_deps = {}
642 for depname, options in self.recursedeps.iteritems():
agabledce6ddc2016-09-08 10:02:16 -0700643 rel_deps[
644 os.path.normpath(os.path.join(rel_prefix, depname))] = options
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000645 self.recursedeps = rel_deps
646
agabledce6ddc2016-09-08 10:02:16 -0700647
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000648 if 'allowed_hosts' in local_scope:
649 try:
650 self._allowed_hosts = frozenset(local_scope.get('allowed_hosts'))
651 except TypeError: # raised if non-iterable
652 pass
653 if not self._allowed_hosts:
654 logging.warning("allowed_hosts is specified but empty %s",
655 self._allowed_hosts)
656 raise gclient_utils.Error(
657 'ParseDepsFile(%s): allowed_hosts must be absent '
658 'or a non-empty iterable' % self.name)
659
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000660 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000661 deps_to_add = []
Paweł Hajdan, Jrc7ba0332017-05-29 16:38:45 +0200662 for name, dep_value in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000663 should_process = self.recursion_limit and self.should_process
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000664 deps_file = self.deps_file
665 if self.recursedeps is not None:
666 ent = self.recursedeps.get(name)
667 if ent is not None:
668 deps_file = ent['deps_file']
Paweł Hajdan, Jr11016452017-05-29 18:02:15 +0200669 if dep_value is None:
670 continue
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200671 condition = None
672 condition_value = True
Paweł Hajdan, Jrc7ba0332017-05-29 16:38:45 +0200673 if isinstance(dep_value, basestring):
674 url = dep_value
675 else:
676 # This should be guaranteed by schema checking in gclient_eval.
677 assert isinstance(dep_value, dict)
678 url = dep_value['url']
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200679 condition = dep_value.get('condition')
680 if condition:
681 # TODO(phajdan.jr): should we also take custom vars into account?
682 condition_value = gclient_eval.EvaluateCondition(
683 condition, local_scope.get('vars', {}))
684 should_process = should_process and condition_value
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000685 deps_to_add.append(Dependency(
agablea98a6cd2016-11-15 14:30:10 -0800686 self, name, url, None, None, self.custom_vars, None,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +0200687 deps_file, should_process, use_relative_paths, condition,
688 condition_value))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000689 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000690
691 # override named sets of hooks by the custom hooks
692 hooks_to_run = []
693 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
694 for hook in local_scope.get('hooks', []):
695 if hook.get('name', '') not in hook_names_to_suppress:
696 hooks_to_run.append(hook)
Scott Grahamc4826742017-05-11 16:59:23 -0700697 if 'hooks_os' in local_scope and target_os_list:
698 hooks_os = local_scope['hooks_os']
699 # Specifically append these to ensure that hooks_os run after hooks.
700 for the_target_os in target_os_list:
701 the_target_os_hooks = hooks_os.get(the_target_os, [])
702 hooks_to_run.extend(the_target_os_hooks)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000703
704 # add the replacements and any additions
705 for hook in self.custom_hooks:
706 if 'action' in hook:
707 hooks_to_run.append(hook)
708
Dirk Prankeda3a29e2017-02-27 15:29:36 -0800709 if self.recursion_limit:
Paweł Hajdan, Jr35b298f2017-05-23 14:37:05 +0200710 self._pre_deps_hooks = [self.GetHookAction(hook) for hook in
Dirk Prankeda3a29e2017-02-27 15:29:36 -0800711 local_scope.get('pre_deps_hooks', [])]
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000712
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000713 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000714 logging.info('ParseDepsFile(%s) done' % self.name)
715
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +0200716 def _get_option(self, attr, default):
717 obj = self
718 while not hasattr(obj, '_options'):
719 obj = obj.parent
720 return getattr(obj._options, attr, default)
721
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000722 def add_dependencies_and_close(self, deps_to_add, hooks):
723 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000724 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000725 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000726 self.add_dependency(dep)
727 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000728
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000729 def findDepsFromNotAllowedHosts(self):
730 """Returns a list of depenecies from not allowed hosts.
731
732 If allowed_hosts is not set, allows all hosts and returns empty list.
733 """
734 if not self._allowed_hosts:
735 return []
736 bad_deps = []
737 for dep in self._dependencies:
szager@chromium.orgbd772dd2014-11-05 18:43:08 +0000738 # Don't enforce this for custom_deps.
739 if dep.name in self._custom_deps:
740 continue
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000741 if isinstance(dep.url, basestring):
742 parsed_url = urlparse.urlparse(dep.url)
743 if parsed_url.netloc and parsed_url.netloc not in self._allowed_hosts:
744 bad_deps.append(dep)
745 return bad_deps
746
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000747 # Arguments number differs from overridden method
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800748 # pylint: disable=arguments-differ
maruel@chromium.org3742c842010-09-09 19:27:14 +0000749 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000750 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000751 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000752 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000753 if not self.should_process:
754 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000755 # When running runhooks, there's no need to consult the SCM.
756 # All known hooks are expected to run unconditionally regardless of working
757 # copy state, so skip the SCM status check.
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +0200758 run_scm = command not in (
759 'flatten', 'runhooks', 'recurse', 'validate', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000760 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000761 file_list = [] if not options.nohooks else None
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000762 revision_override = revision_overrides.pop(self.name, None)
Dave Tubbda9712017-06-01 15:10:53 -0700763 if not revision_override and parsed_url:
764 revision_override = revision_overrides.get(parsed_url.split('@')[0], None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000765 if run_scm and parsed_url:
agabled437d762016-10-17 09:35:11 -0700766 # Create a shallow copy to mutate revision.
767 options = copy.copy(options)
768 options.revision = revision_override
769 self._used_revision = options.revision
770 self._used_scm = gclient_scm.CreateSCM(
771 parsed_url, self.root.root_dir, self.name, self.outbuf,
772 out_cb=work_queue.out_cb)
773 self._got_revision = self._used_scm.RunCommand(command, options, args,
774 file_list)
775 if file_list:
776 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000777
778 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
779 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000780 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000781 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000782 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000783 continue
784 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000785 [self.root.root_dir.lower(), file_list[i].lower()])
786 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000787 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000788 while file_list[i].startswith(('\\', '/')):
789 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000790
791 # Always parse the DEPS file.
792 self.ParseDepsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000793 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000794 if command in ('update', 'revert') and not options.noprehooks:
795 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000796
797 if self.recursion_limit:
798 # Parse the dependencies of this dependency.
799 for s in self.dependencies:
800 work_queue.enqueue(s)
801
802 if command == 'recurse':
agabled437d762016-10-17 09:35:11 -0700803 # Skip file only checkout.
804 scm = gclient_scm.GetScmName(parsed_url)
805 if not options.scm or scm in options.scm:
806 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
807 # Pass in the SCM type as an env variable. Make sure we don't put
808 # unicode strings in the environment.
809 env = os.environ.copy()
810 if scm:
811 env['GCLIENT_SCM'] = str(scm)
812 if parsed_url:
813 env['GCLIENT_URL'] = str(parsed_url)
814 env['GCLIENT_DEP_PATH'] = str(self.name)
815 if options.prepend_dir and scm == 'git':
816 print_stdout = False
817 def filter_fn(line):
818 """Git-specific path marshaling. It is optimized for git-grep."""
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000819
agabled437d762016-10-17 09:35:11 -0700820 def mod_path(git_pathspec):
821 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
822 modified_path = os.path.join(self.name, match.group(2))
823 branch = match.group(1) or ''
824 return '%s%s' % (branch, modified_path)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000825
agabled437d762016-10-17 09:35:11 -0700826 match = re.match('^Binary file ([^\0]+) matches$', line)
827 if match:
828 print('Binary file %s matches\n' % mod_path(match.group(1)))
829 return
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000830
agabled437d762016-10-17 09:35:11 -0700831 items = line.split('\0')
832 if len(items) == 2 and items[1]:
833 print('%s : %s' % (mod_path(items[0]), items[1]))
834 elif len(items) >= 2:
835 # Multiple null bytes or a single trailing null byte indicate
836 # git is likely displaying filenames only (such as with -l)
837 print('\n'.join(mod_path(path) for path in items if path))
838 else:
839 print(line)
840 else:
841 print_stdout = True
842 filter_fn = None
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000843
agabled437d762016-10-17 09:35:11 -0700844 if parsed_url is None:
845 print('Skipped omitted dependency %s' % cwd, file=sys.stderr)
846 elif os.path.isdir(cwd):
847 try:
848 gclient_utils.CheckCallAndFilter(
849 args, cwd=cwd, env=env, print_stdout=print_stdout,
850 filter_fn=filter_fn,
851 )
852 except subprocess2.CalledProcessError:
853 if not options.ignore:
854 raise
855 else:
856 print('Skipped missing %s' % cwd, file=sys.stderr)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000857
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000858
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000859 @gclient_utils.lockedmethod
860 def _run_is_done(self, file_list, parsed_url):
861 # Both these are kept for hooks that are run as a separate tree traversal.
862 self._file_list = file_list
863 self._parsed_url = parsed_url
864 self._processed = True
865
szager@google.comb9a78d32012-03-13 18:46:21 +0000866 @staticmethod
Paweł Hajdan, Jr35b298f2017-05-23 14:37:05 +0200867 def GetHookAction(hook_dict):
szager@google.comb9a78d32012-03-13 18:46:21 +0000868 """Turns a parsed 'hook' dict into an executable command."""
869 logging.debug(hook_dict)
szager@google.comb9a78d32012-03-13 18:46:21 +0000870 command = hook_dict['action'][:]
871 if command[0] == 'python':
872 # If the hook specified "python" as the first item, the action is a
873 # Python script. Run it by starting a new copy of the same
874 # interpreter.
875 command[0] = sys.executable
szager@google.comb9a78d32012-03-13 18:46:21 +0000876 return command
877
878 def GetHooks(self, options):
879 """Evaluates all hooks, and return them in a flat list.
880
881 RunOnDeps() must have been called before to load the DEPS.
882 """
883 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000884 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000885 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000886 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000887 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000888 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000889 if self.deps_hooks:
agabled437d762016-10-17 09:35:11 -0700890 # TODO(maruel): If the user is using git, then we don't know
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000891 # what files have changed so we always run all hooks. It'd be nice to fix
892 # that.
893 if (options.force or
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000894 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000895 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000896 for hook_dict in self.deps_hooks:
Paweł Hajdan, Jr35b298f2017-05-23 14:37:05 +0200897 result.append(self.GetHookAction(hook_dict))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000898 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000899 # Run hooks on the basis of whether the files from the gclient operation
900 # match each hook's pattern.
901 for hook_dict in self.deps_hooks:
902 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000903 matching_file_list = [
904 f for f in self.file_list_and_children if pattern.search(f)
905 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000906 if matching_file_list:
Paweł Hajdan, Jr35b298f2017-05-23 14:37:05 +0200907 result.append(self.GetHookAction(hook_dict))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000908 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000909 result.extend(s.GetHooks(options))
910 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000911
szager@google.comb9a78d32012-03-13 18:46:21 +0000912 def RunHooksRecursively(self, options):
913 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000914 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000915 for hook in self.GetHooks(options):
916 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000917 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +0000918 gclient_utils.CheckCallAndFilterAndHeader(
919 hook, cwd=self.root.root_dir, always=True)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000920 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
szager@google.comb9a78d32012-03-13 18:46:21 +0000921 # Use a discrete exit status code of 2 to indicate that a hook action
922 # failed. Users of this script may wish to treat hook action failures
923 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000924 print('Error: %s' % str(e), file=sys.stderr)
szager@google.comb9a78d32012-03-13 18:46:21 +0000925 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000926 finally:
927 elapsed_time = time.time() - start_time
928 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000929 print("Hook '%s' took %.2f secs" % (
930 gclient_utils.CommandToStr(hook), elapsed_time))
maruel@chromium.orgeaf61062010-07-07 18:42:39 +0000931
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000932 def RunPreDepsHooks(self):
933 assert self.processed
934 assert self.deps_parsed
935 assert not self.pre_deps_hooks_ran
936 assert not self.hooks_ran
937 for s in self.dependencies:
938 assert not s.processed
939 self._pre_deps_hooks_ran = True
940 for hook in self.pre_deps_hooks:
941 try:
942 start_time = time.time()
943 gclient_utils.CheckCallAndFilterAndHeader(
944 hook, cwd=self.root.root_dir, always=True)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000945 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000946 # Use a discrete exit status code of 2 to indicate that a hook action
947 # failed. Users of this script may wish to treat hook action failures
948 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000949 print('Error: %s' % str(e), file=sys.stderr)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000950 sys.exit(2)
951 finally:
952 elapsed_time = time.time() - start_time
953 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000954 print("Hook '%s' took %.2f secs" % (
955 gclient_utils.CommandToStr(hook), elapsed_time))
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000956
957
maruel@chromium.org0d812442010-08-10 12:41:08 +0000958 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000959 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000960 dependencies = self.dependencies
961 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000962 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000963 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +0000964 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +0000965 for i in d.subtree(include_all):
966 yield i
967
968 def depth_first_tree(self):
969 """Depth-first recursion including the root node."""
970 yield self
971 for i in self.dependencies:
972 for j in i.depth_first_tree():
973 if j.should_process:
974 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +0000975
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000976 @gclient_utils.lockedmethod
977 def add_dependency(self, new_dep):
978 self._dependencies.append(new_dep)
979
980 @gclient_utils.lockedmethod
981 def _mark_as_parsed(self, new_hooks):
982 self._deps_hooks.extend(new_hooks)
983 self._deps_parsed = True
984
maruel@chromium.org68988972011-09-20 14:11:42 +0000985 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000986 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000987 def dependencies(self):
988 return tuple(self._dependencies)
989
990 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000991 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +0000992 def deps_hooks(self):
993 return tuple(self._deps_hooks)
994
995 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000996 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000997 def pre_deps_hooks(self):
998 return tuple(self._pre_deps_hooks)
999
1000 @property
1001 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001002 def parsed_url(self):
1003 return self._parsed_url
1004
1005 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001006 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001007 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +00001008 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +00001009 return self._deps_parsed
1010
1011 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001012 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001013 def processed(self):
1014 return self._processed
1015
1016 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001017 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001018 def pre_deps_hooks_ran(self):
1019 return self._pre_deps_hooks_ran
1020
1021 @property
1022 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001023 def hooks_ran(self):
1024 return self._hooks_ran
1025
1026 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001027 @gclient_utils.lockedmethod
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001028 def allowed_hosts(self):
1029 return self._allowed_hosts
1030
1031 @property
1032 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001033 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001034 return tuple(self._file_list)
1035
1036 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +00001037 def used_scm(self):
1038 """SCMWrapper instance for this dependency or None if not processed yet."""
1039 return self._used_scm
1040
1041 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001042 @gclient_utils.lockedmethod
1043 def got_revision(self):
1044 return self._got_revision
1045
1046 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001047 def file_list_and_children(self):
1048 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001049 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001050 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +00001051 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001052
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001053 def __str__(self):
1054 out = []
agablea98a6cd2016-11-15 14:30:10 -08001055 for i in ('name', 'url', 'parsed_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001056 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001057 'processed', 'hooks_ran', 'deps_parsed', 'requirements',
1058 'allowed_hosts'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001059 # First try the native property if it exists.
1060 if hasattr(self, '_' + i):
1061 value = getattr(self, '_' + i, False)
1062 else:
1063 value = getattr(self, i, False)
1064 if value:
1065 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001066
1067 for d in self.dependencies:
1068 out.extend([' ' + x for x in str(d).splitlines()])
1069 out.append('')
1070 return '\n'.join(out)
1071
1072 def __repr__(self):
1073 return '%s: %s' % (self.name, self.url)
1074
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001075 def hierarchy(self, include_url=True):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +00001076 """Returns a human-readable hierarchical reference to a Dependency."""
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001077 def format_name(d):
1078 if include_url:
1079 return '%s(%s)' % (d.name, d.url)
1080 return d.name
1081 out = format_name(self)
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001082 i = self.parent
1083 while i and i.name:
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001084 out = '%s -> %s' % (format_name(i), out)
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001085 i = i.parent
1086 return out
1087
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001088
1089class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001090 """Object that represent a gclient checkout. A tree of Dependency(), one per
1091 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001092
1093 DEPS_OS_CHOICES = {
1094 "win32": "win",
1095 "win": "win",
1096 "cygwin": "win",
1097 "darwin": "mac",
1098 "mac": "mac",
1099 "unix": "unix",
1100 "linux": "unix",
1101 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +00001102 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +00001103 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001104 }
1105
1106 DEFAULT_CLIENT_FILE_TEXT = ("""\
1107solutions = [
smutae7ea312016-07-18 11:59:41 -07001108 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001109 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001110 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001111 "managed" : %(managed)s,
smutae7ea312016-07-18 11:59:41 -07001112 "custom_deps" : {
1113 },
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001114 },
1115]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001116cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001117""")
1118
1119 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
smutae7ea312016-07-18 11:59:41 -07001120 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001121 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001122 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001123 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001124 "custom_deps" : {
smutae7ea312016-07-18 11:59:41 -07001125%(solution_deps)s },
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001126 },
1127""")
1128
1129 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1130# Snapshot generated with gclient revinfo --snapshot
1131solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001132%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001133""")
1134
1135 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001136 # Do not change previous behavior. Only solution level and immediate DEPS
1137 # are processed.
1138 self._recursion_limit = 2
agablea98a6cd2016-11-15 14:30:10 -08001139 Dependency.__init__(self, None, None, None, True, None, None, None,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001140 'unused', True, None, None, True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001141 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001142 if options.deps_os:
1143 enforced_os = options.deps_os.split(',')
1144 else:
1145 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1146 if 'all' in enforced_os:
1147 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001148 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001149 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001150 self.config_content = None
1151
borenet@google.com88d10082014-03-21 17:24:48 +00001152 def _CheckConfig(self):
1153 """Verify that the config matches the state of the existing checked-out
1154 solutions."""
1155 for dep in self.dependencies:
1156 if dep.managed and dep.url:
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001157 scm = gclient_scm.CreateSCM(
1158 dep.url, self.root_dir, dep.name, self.outbuf)
smut@google.comd33eab32014-07-07 19:35:18 +00001159 actual_url = scm.GetActualRemoteURL(self._options)
borenet@google.com4e9be262014-04-08 19:40:30 +00001160 if actual_url and not scm.DoesRemoteURLMatch(self._options):
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001161 mirror = scm.GetCacheMirror()
1162 if mirror:
1163 mirror_string = '%s (exists=%s)' % (mirror.mirror_path,
1164 mirror.exists())
1165 else:
1166 mirror_string = 'not used'
borenet@google.com0a427372014-04-02 19:12:13 +00001167 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001168Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001169is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001170
borenet@google.com97882362014-04-07 20:06:02 +00001171The .gclient file contains:
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001172URL: %(expected_url)s (%(expected_scm)s)
1173Cache mirror: %(mirror_string)s
borenet@google.com97882362014-04-07 20:06:02 +00001174
1175The local checkout in %(checkout_path)s reports:
1176%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001177
1178You should ensure that the URL listed in .gclient is correct and either change
agabled437d762016-10-17 09:35:11 -07001179it or fix the checkout.
borenet@google.com88d10082014-03-21 17:24:48 +00001180''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1181 'expected_url': dep.url,
1182 'expected_scm': gclient_scm.GetScmName(dep.url),
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001183 'mirror_string' : mirror_string,
borenet@google.com88d10082014-03-21 17:24:48 +00001184 'actual_url': actual_url,
1185 'actual_scm': gclient_scm.GetScmName(actual_url)})
1186
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001187 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001188 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001189 config_dict = {}
1190 self.config_content = content
1191 try:
1192 exec(content, config_dict)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001193 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001194 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001195
peter@chromium.org1efccc82012-04-27 16:34:38 +00001196 # Append any target OS that is not already being enforced to the tuple.
1197 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001198 if config_dict.get('target_os_only', False):
1199 self._enforced_os = tuple(set(target_os))
1200 else:
1201 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1202
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001203 cache_dir = config_dict.get('cache_dir')
1204 if cache_dir:
1205 cache_dir = os.path.join(self.root_dir, cache_dir)
1206 cache_dir = os.path.abspath(cache_dir)
szager@chromium.orgcaf5bef2014-08-24 18:56:32 +00001207 # If running on a bot, force break any stale git cache locks.
dnj@chromium.orgb682b3e2014-08-25 19:17:12 +00001208 if os.path.exists(cache_dir) and os.environ.get('CHROME_HEADLESS'):
szager@chromium.org4848fb62014-08-24 19:16:31 +00001209 subprocess2.check_call(['git', 'cache', 'unlock', '--cache-dir',
1210 cache_dir, '--force', '--all'])
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001211 gclient_scm.GitWrapper.cache_dir = cache_dir
1212 git_cache.Mirror.SetCachePath(cache_dir)
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001213
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001214 if not target_os and config_dict.get('target_os_only', False):
1215 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1216 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001217
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001218 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001219 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001220 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001221 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +00001222 self, s['name'], s['url'],
smutae7ea312016-07-18 11:59:41 -07001223 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001224 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001225 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001226 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001227 s.get('deps_file', 'DEPS'),
agabledce6ddc2016-09-08 10:02:16 -07001228 True,
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001229 None,
1230 None,
1231 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001232 except KeyError:
1233 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1234 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001235 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1236 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001237
1238 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001239 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001240 self._options.config_filename),
1241 self.config_content)
1242
1243 @staticmethod
1244 def LoadCurrentConfig(options):
1245 """Searches for and loads a .gclient file relative to the current working
1246 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001247 if options.spec:
1248 client = GClient('.', options)
1249 client.SetConfig(options.spec)
1250 else:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001251 if options.verbose:
1252 print('Looking for %s starting from %s\n' % (
1253 options.config_filename, os.getcwd()))
szager@chromium.orge2e03202012-07-31 18:05:16 +00001254 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1255 if not path:
1256 return None
1257 client = GClient(path, options)
1258 client.SetConfig(gclient_utils.FileRead(
1259 os.path.join(path, options.config_filename)))
maruel@chromium.org69392e72011-10-13 22:09:00 +00001260
1261 if (options.revisions and
1262 len(client.dependencies) > 1 and
1263 any('@' not in r for r in options.revisions)):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001264 print(
1265 ('You must specify the full solution name like --revision %s@%s\n'
1266 'when you have multiple solutions setup in your .gclient file.\n'
1267 'Other solutions present are: %s.') % (
maruel@chromium.org69392e72011-10-13 22:09:00 +00001268 client.dependencies[0].name,
1269 options.revisions[0],
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001270 ', '.join(s.name for s in client.dependencies[1:])),
1271 file=sys.stderr)
maruel@chromium.org15804092010-09-02 17:07:37 +00001272 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001273
nsylvain@google.comefc80932011-05-31 21:27:56 +00001274 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
agablea98a6cd2016-11-15 14:30:10 -08001275 managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001276 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1277 'solution_name': solution_name,
1278 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001279 'deps_file': deps_file,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001280 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001281 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001282 })
1283
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001284 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001285 """Creates a .gclient_entries file to record the list of unique checkouts.
1286
1287 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001288 """
1289 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1290 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001291 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001292 for entry in self.root.subtree(False):
agabled437d762016-10-17 09:35:11 -07001293 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1294 pprint.pformat(entry.parsed_url))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001295 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001296 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001297 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001298 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001299
1300 def _ReadEntries(self):
1301 """Read the .gclient_entries file for the given client.
1302
1303 Returns:
1304 A sequence of solution names, which will be empty if there is the
1305 entries file hasn't been created yet.
1306 """
1307 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001308 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001309 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001310 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001311 try:
1312 exec(gclient_utils.FileRead(filename), scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001313 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001314 gclient_utils.SyntaxErrorToError(filename, e)
Aaron Gable3721ee92017-04-03 14:53:14 -07001315 return scope.get('entries', {})
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001316
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001317 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001318 """Checks for revision overrides."""
1319 revision_overrides = {}
smutae7ea312016-07-18 11:59:41 -07001320 if self._options.head:
1321 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001322 if not self._options.revisions:
1323 for s in self.dependencies:
smutae7ea312016-07-18 11:59:41 -07001324 if not s.managed:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001325 self._options.revisions.append('%s@unmanaged' % s.name)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001326 if not self._options.revisions:
1327 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001328 solutions_names = [s.name for s in self.dependencies]
smutae7ea312016-07-18 11:59:41 -07001329 index = 0
1330 for revision in self._options.revisions:
1331 if not '@' in revision:
maruel@chromium.org307d1792010-05-31 20:03:13 +00001332 # Support for --revision 123
smutae7ea312016-07-18 11:59:41 -07001333 revision = '%s@%s' % (solutions_names[index], revision)
1334 name, rev = revision.split('@', 1)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001335 revision_overrides[name] = rev
smutae7ea312016-07-18 11:59:41 -07001336 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001337 return revision_overrides
1338
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001339 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001340 """Runs a command on each dependency in a client and its dependencies.
1341
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001342 Args:
1343 command: The command to use (e.g., 'status' or 'diff')
1344 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001345 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001346 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001347 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001348
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001349 revision_overrides = {}
1350 # It's unnecessary to check for revision overrides for 'recurse'.
1351 # Save a few seconds by not calling _EnforceRevisions() in that case.
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02001352 if command not in ('diff', 'recurse', 'runhooks', 'status', 'revert',
1353 'validate'):
szager@chromium.org5273b8a2014-08-21 15:10:10 +00001354 self._CheckConfig()
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001355 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001356 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001357 # Disable progress for non-tty stdout.
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00001358 if (setup_color.IS_TTY and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001359 if command in ('update', 'revert'):
1360 pm = Progress('Syncing projects', 1)
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02001361 elif command in ('recurse', 'validate'):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001362 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001363 work_queue = gclient_utils.ExecutionQueue(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001364 self._options.jobs, pm, ignore_requirements=ignore_requirements,
1365 verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001366 for s in self.dependencies:
1367 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001368 work_queue.flush(revision_overrides, command, args, options=self._options)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001369 if revision_overrides:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001370 print('Please fix your script, having invalid --revision flags will soon '
1371 'considered an error.', file=sys.stderr)
piman@chromium.org6f363722010-04-27 00:41:09 +00001372
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001373 # Once all the dependencies have been processed, it's now safe to run the
1374 # hooks.
1375 if not self._options.nohooks:
1376 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001377
1378 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001379 # Notify the user if there is an orphaned entry in their working copy.
1380 # Only delete the directory if there are no changes in it, and
1381 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001382 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001383 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1384 for e in entries]
1385
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001386 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001387 if not prev_url:
1388 # entry must have been overridden via .gclient custom_deps
1389 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001390 # Fix path separator on Windows.
1391 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001392 e_dir = os.path.join(self.root_dir, entry_fixed)
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001393 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001394 if (entry not in entries and
1395 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001396 os.path.exists(e_dir)):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001397 # The entry has been removed from DEPS.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001398 scm = gclient_scm.CreateSCM(
1399 prev_url, self.root_dir, entry_fixed, self.outbuf)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001400
1401 # Check to see if this directory is now part of a higher-up checkout.
borenet@google.com359bb642014-05-13 17:28:19 +00001402 scm_root = None
agabled437d762016-10-17 09:35:11 -07001403 try:
1404 scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(scm.checkout_path)
1405 except subprocess2.CalledProcessError:
1406 pass
1407 if not scm_root:
borenet@google.com359bb642014-05-13 17:28:19 +00001408 logging.warning('Could not find checkout root for %s. Unable to '
1409 'determine whether it is part of a higher-level '
1410 'checkout, so not removing.' % entry)
1411 continue
primiano@chromium.org1c127382015-02-17 11:15:40 +00001412
1413 # This is to handle the case of third_party/WebKit migrating from
1414 # being a DEPS entry to being part of the main project.
1415 # If the subproject is a Git project, we need to remove its .git
1416 # folder. Otherwise git operations on that folder will have different
1417 # effects depending on the current working directory.
agabled437d762016-10-17 09:35:11 -07001418 if os.path.abspath(scm_root) == os.path.abspath(e_dir):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001419 e_par_dir = os.path.join(e_dir, os.pardir)
agabled437d762016-10-17 09:35:11 -07001420 if gclient_scm.scm.GIT.IsInsideWorkTree(e_par_dir):
1421 par_scm_root = gclient_scm.scm.GIT.GetCheckoutRoot(e_par_dir)
primiano@chromium.org1c127382015-02-17 11:15:40 +00001422 # rel_e_dir : relative path of entry w.r.t. its parent repo.
1423 rel_e_dir = os.path.relpath(e_dir, par_scm_root)
agabled437d762016-10-17 09:35:11 -07001424 if gclient_scm.scm.GIT.IsDirectoryVersioned(
1425 par_scm_root, rel_e_dir):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001426 save_dir = scm.GetGitBackupDirPath()
1427 # Remove any eventual stale backup dir for the same project.
1428 if os.path.exists(save_dir):
1429 gclient_utils.rmtree(save_dir)
1430 os.rename(os.path.join(e_dir, '.git'), save_dir)
1431 # When switching between the two states (entry/ is a subproject
1432 # -> entry/ is part of the outer project), it is very likely
1433 # that some files are changed in the checkout, unless we are
1434 # jumping *exactly* across the commit which changed just DEPS.
1435 # In such case we want to cleanup any eventual stale files
1436 # (coming from the old subproject) in order to end up with a
1437 # clean checkout.
agabled437d762016-10-17 09:35:11 -07001438 gclient_scm.scm.GIT.CleanupDir(par_scm_root, rel_e_dir)
primiano@chromium.org1c127382015-02-17 11:15:40 +00001439 assert not os.path.exists(os.path.join(e_dir, '.git'))
1440 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher '
1441 'level checkout. The git folder containing all the local'
1442 ' branches has been saved to %s.\n'
1443 'If you don\'t care about its state you can safely '
1444 'remove that folder to free up space.') %
1445 (entry, save_dir))
1446 continue
1447
borenet@google.com359bb642014-05-13 17:28:19 +00001448 if scm_root in full_entries:
primiano@chromium.org1c127382015-02-17 11:15:40 +00001449 logging.info('%s is part of a higher level checkout, not removing',
1450 scm.GetCheckoutRoot())
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001451 continue
1452
1453 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001454 scm.status(self._options, [], file_list)
1455 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001456 if (not self._options.delete_unversioned_trees or
1457 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001458 # There are modified files in this entry. Keep warning until
1459 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001460 print(('\nWARNING: \'%s\' is no longer part of this client. '
1461 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001462 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001463 else:
1464 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001465 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001466 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001467 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001468 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001469 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001470 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001471
1472 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001473 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001474 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001475 # Load all the settings.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001476 work_queue = gclient_utils.ExecutionQueue(
1477 self._options.jobs, None, False, verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001478 for s in self.dependencies:
1479 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001480 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001481
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001482 def GetURLAndRev(dep):
1483 """Returns the revision-qualified SCM url for a Dependency."""
1484 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001485 return None
agabled437d762016-10-17 09:35:11 -07001486 url, _ = gclient_utils.SplitUrlRevision(dep.parsed_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001487 scm = gclient_scm.CreateSCM(
agabled437d762016-10-17 09:35:11 -07001488 dep.parsed_url, self.root_dir, dep.name, self.outbuf)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001489 if not os.path.isdir(scm.checkout_path):
1490 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001491 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001492
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001493 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001494 new_gclient = ''
1495 # First level at .gclient
1496 for d in self.dependencies:
1497 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001498 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001499 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001500 for d in dep.dependencies:
1501 entries[d.name] = GetURLAndRev(d)
1502 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001503 GrabDeps(d)
1504 custom_deps = []
1505 for k in sorted(entries.keys()):
1506 if entries[k]:
1507 # Quotes aren't escaped...
1508 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1509 else:
1510 custom_deps.append(' \"%s\": None,\n' % k)
1511 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1512 'solution_name': d.name,
1513 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001514 'deps_file': d.deps_file,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001515 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001516 'solution_deps': ''.join(custom_deps),
1517 }
1518 # Print the snapshot configuration file
1519 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001520 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001521 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001522 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001523 if self._options.actual:
1524 entries[d.name] = GetURLAndRev(d)
1525 else:
1526 entries[d.name] = d.parsed_url
1527 keys = sorted(entries.keys())
1528 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001529 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001530 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001531
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001532 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001533 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001534 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001535
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001536 def PrintLocationAndContents(self):
1537 # Print out the .gclient file. This is longer than if we just printed the
1538 # client dict, but more legible, and it might contain helpful comments.
1539 print('Loaded .gclient config in %s:\n%s' % (
1540 self.root_dir, self.config_content))
1541
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001542 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001543 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001544 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001545 return self._root_dir
1546
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001547 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001548 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001549 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001550 return self._enforced_os
1551
maruel@chromium.org68988972011-09-20 14:11:42 +00001552 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001553 def recursion_limit(self):
1554 """How recursive can each dependencies in DEPS file can load DEPS file."""
1555 return self._recursion_limit
1556
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001557 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +00001558 def try_recursedeps(self):
1559 """Whether to attempt using recursedeps-style recursion processing."""
cmp@chromium.orge84ac912014-06-30 23:14:35 +00001560 return True
1561
1562 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001563 def target_os(self):
1564 return self._enforced_os
1565
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001566
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001567#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001568
1569
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001570@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001571def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001572 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001573
1574 Runs a shell command on all entries.
qyearsley12fa6ff2016-08-24 09:18:40 -07001575 Sets GCLIENT_DEP_PATH environment variable as the dep's relative location to
ilevy@chromium.org37116242012-11-28 01:32:48 +00001576 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001577 """
1578 # Stop parsing at the first non-arg so that these go through to the command
1579 parser.disable_interspersed_args()
1580 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001581 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001582 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001583 help='Ignore non-zero return codes from subcommands.')
1584 parser.add_option('--prepend-dir', action='store_true',
1585 help='Prepend relative dir for use with git <cmd> --null.')
1586 parser.add_option('--no-progress', action='store_true',
1587 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001588 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001589 if not args:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001590 print('Need to supply a command!', file=sys.stderr)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001591 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001592 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1593 if not root_and_entries:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001594 print(
maruel@chromium.org78cba522010-10-18 13:32:05 +00001595 'You need to run gclient sync at least once to use \'recurse\'.\n'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001596 'This is because .gclient_entries needs to exist and be up to date.',
1597 file=sys.stderr)
maruel@chromium.org78cba522010-10-18 13:32:05 +00001598 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001599
1600 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001601 scm_set = set()
1602 for scm in options.scm:
1603 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001604 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001605
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001606 options.nohooks = True
1607 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001608 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1609 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001610
1611
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001612@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001613def CMDfetch(parser, args):
1614 """Fetches upstream commits for all modules.
1615
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001616 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1617 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001618 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001619 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001620 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1621
1622
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001623def CMDflatten(parser, args):
1624 """Flattens the solutions into a single DEPS file."""
1625 parser.add_option('--output-deps', help='Path to the output DEPS file')
1626 parser.add_option(
1627 '--require-pinned-revisions', action='store_true',
1628 help='Fail if any of the dependencies uses unpinned revision.')
1629 options, args = parser.parse_args(args)
1630
1631 options.nohooks = True
1632 client = GClient.LoadCurrentConfig(options)
1633
1634 # Only print progress if we're writing to a file. Otherwise, progress updates
1635 # could obscure intended output.
1636 code = client.RunOnDeps('flatten', args, progress=options.output_deps)
1637 if code != 0:
1638 return code
1639
1640 deps = {}
1641 hooks = []
1642 pre_deps_hooks = []
1643 unpinned_deps = {}
1644
1645 for solution in client.dependencies:
1646 _FlattenSolution(solution, deps, hooks, pre_deps_hooks, unpinned_deps)
1647
1648 if options.require_pinned_revisions and unpinned_deps:
1649 sys.stderr.write('The following dependencies are not pinned:\n')
1650 sys.stderr.write('\n'.join(sorted(unpinned_deps)))
1651 return 1
1652
1653 flattened_deps = '\n'.join(
1654 _DepsToLines(deps) +
1655 _HooksToLines('hooks', hooks) +
1656 _HooksToLines('pre_deps_hooks', pre_deps_hooks) +
1657 [''] # Ensure newline at end of file.
1658 )
1659
1660 if options.output_deps:
1661 with open(options.output_deps, 'w') as f:
1662 f.write(flattened_deps)
1663 else:
1664 print(flattened_deps)
1665
1666 return 0
1667
1668
1669def _FlattenSolution(solution, deps, hooks, pre_deps_hooks, unpinned_deps):
1670 """Visits a solution in order to flatten it (see CMDflatten).
1671
1672 Arguments:
1673 solution (Dependency): one of top-level solutions in .gclient
1674
1675 Out-parameters:
1676 deps (dict of name -> Dependency): will be filled with all Dependency
1677 objects indexed by their name
1678 hooks (list of (Dependency, hook)): will be filled with flattened hooks
1679 pre_deps_hooks (list of (Dependency, hook)): will be filled with flattened
1680 pre_deps_hooks
1681 unpinned_deps (dict of name -> Dependency): will be filled with unpinned
1682 deps
1683 """
1684 logging.debug('_FlattenSolution(%r)', solution)
1685
1686 _FlattenDep(solution, deps, hooks, pre_deps_hooks, unpinned_deps)
1687 _FlattenRecurse(solution, deps, hooks, pre_deps_hooks, unpinned_deps)
1688
1689
1690def _FlattenDep(dep, deps, hooks, pre_deps_hooks, unpinned_deps):
1691 """Visits a dependency in order to flatten it (see CMDflatten).
1692
1693 Arguments:
1694 dep (Dependency): dependency to process
1695
1696 Out-parameters:
1697 deps (dict): will be filled with flattened deps
1698 hooks (list): will be filled with flattened hooks
1699 pre_deps_hooks (list): will be filled with flattened pre_deps_hooks
1700 unpinned_deps (dict): will be filled with unpinned deps
1701 """
1702 logging.debug('_FlattenDep(%r)', dep)
1703
1704 _AddDep(dep, deps, unpinned_deps)
1705
1706 deps_by_name = dict((d.name, d) for d in dep.dependencies)
1707 for recurse_dep_name in (dep.recursedeps or []):
1708 _FlattenRecurse(
1709 deps_by_name[recurse_dep_name], deps, hooks, pre_deps_hooks,
1710 unpinned_deps)
1711
1712 # TODO(phajdan.jr): also handle hooks_os.
1713 hooks.extend([(dep, hook) for hook in dep.deps_hooks])
1714 pre_deps_hooks.extend(
1715 [(dep, {'action': hook}) for hook in dep.pre_deps_hooks])
1716
1717
1718def _FlattenRecurse(dep, deps, hooks, pre_deps_hooks, unpinned_deps):
1719 """Helper for flatten that recurses into |dep|'s dependencies.
1720
1721 Arguments:
1722 dep (Dependency): dependency to process
1723
1724 Out-parameters:
1725 deps (dict): will be filled with flattened deps
1726 hooks (list): will be filled with flattened hooks
1727 pre_deps_hooks (list): will be filled with flattened pre_deps_hooks
1728 unpinned_deps (dict): will be filled with unpinned deps
1729 """
1730 logging.debug('_FlattenRecurse(%r)', dep)
1731
1732 # TODO(phajdan.jr): also handle deps_os.
1733 for dep in dep.dependencies:
1734 _FlattenDep(dep, deps, hooks, pre_deps_hooks, unpinned_deps)
1735
1736
1737def _AddDep(dep, deps, unpinned_deps):
1738 """Helper to add a dependency to flattened lists.
1739
1740 Arguments:
1741 dep (Dependency): dependency to process
1742
1743 Out-parameters:
1744 deps (dict): will be filled with flattened deps
1745 unpinned_deps (dict): will be filled with unpinned deps
1746 """
1747 logging.debug('_AddDep(%r)', dep)
1748
1749 assert dep.name not in deps
1750 deps[dep.name] = dep
1751
1752 # Detect unpinned deps.
1753 _, revision = gclient_utils.SplitUrlRevision(dep.url)
1754 if not revision or not gclient_utils.IsGitSha(revision):
1755 unpinned_deps[dep.name] = dep
1756
1757
1758def _DepsToLines(deps):
1759 """Converts |deps| dict to list of lines for output."""
1760 s = ['deps = {']
1761 for name, dep in sorted(deps.iteritems()):
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001762 condition_part = ([' "condition": "%s",' % dep.condition]
1763 if dep.condition else [])
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001764 s.extend([
1765 ' # %s' % dep.hierarchy(include_url=False),
Paweł Hajdan, Jrf69860b2017-06-05 20:24:28 +02001766 ' "%s": {' % (name,),
1767 ' "url": "%s",' % (dep.url,),
1768 ] + condition_part + [
1769 ' },',
Paweł Hajdan, Jr064f6f42017-05-18 22:17:55 +02001770 '',
1771 ])
1772 s.extend(['}', ''])
1773 return s
1774
1775
1776def _HooksToLines(name, hooks):
1777 """Converts |hooks| list to list of lines for output."""
1778 s = ['%s = [' % name]
1779 for dep, hook in hooks:
1780 s.extend([
1781 ' # %s' % dep.hierarchy(include_url=False),
1782 ' {',
1783 ])
1784 if 'name' in hook:
1785 s.append(' "name": "%s",' % hook['name'])
1786 if 'pattern' in hook:
1787 s.append(' "pattern": "%s",' % hook['pattern'])
1788 # TODO(phajdan.jr): actions may contain paths that need to be adjusted,
1789 # i.e. they may be relative to the dependency path, not solution root.
1790 s.extend(
1791 [' "action": ['] +
1792 [' "%s",' % arg for arg in hook['action']] +
1793 [' ]', ' },', '']
1794 )
1795 s.extend([']', ''])
1796 return s
1797
1798
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001799def CMDgrep(parser, args):
1800 """Greps through git repos managed by gclient.
1801
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001802 Runs 'git grep [args...]' for each module.
1803 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001804 # We can't use optparse because it will try to parse arguments sent
1805 # to git grep and throw an error. :-(
1806 if not args or re.match('(-h|--help)$', args[0]):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001807 print(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001808 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1809 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1810 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1811 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001812 ' end of your query.',
1813 file=sys.stderr)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001814 return 1
1815
1816 jobs_arg = ['--jobs=1']
1817 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1818 jobs_arg, args = args[:1], args[1:]
1819 elif re.match(r'(-j|--jobs)$', args[0]):
1820 jobs_arg, args = args[:2], args[2:]
1821
1822 return CMDrecurse(
1823 parser,
1824 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1825 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001826
1827
stip@chromium.orga735da22015-04-29 23:18:20 +00001828def CMDroot(parser, args):
1829 """Outputs the solution root (or current dir if there isn't one)."""
1830 (options, args) = parser.parse_args(args)
1831 client = GClient.LoadCurrentConfig(options)
1832 if client:
1833 print(os.path.abspath(client.root_dir))
1834 else:
1835 print(os.path.abspath('.'))
1836
1837
agablea98a6cd2016-11-15 14:30:10 -08001838@subcommand.usage('[url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001839def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001840 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001841
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001842 This specifies the configuration for further commands. After update/sync,
1843 top-level DEPS files in each module are read to determine dependent
1844 modules to operate on as well. If optional [url] parameter is
1845 provided, then configuration is read from a specified Subversion server
1846 URL.
1847 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00001848 # We do a little dance with the --gclientfile option. 'gclient config' is the
1849 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1850 # arguments. So, we temporarily stash any --gclientfile parameter into
1851 # options.output_config_file until after the (gclientfile xor spec) error
1852 # check.
1853 parser.remove_option('--gclientfile')
1854 parser.add_option('--gclientfile', dest='output_config_file',
1855 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001856 parser.add_option('--name',
1857 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001858 parser.add_option('--deps-file', default='DEPS',
1859 help='overrides the default name for the DEPS file for the'
1860 'main solutions and all sub-dependencies')
smutae7ea312016-07-18 11:59:41 -07001861 parser.add_option('--unmanaged', action='store_true', default=False,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001862 help='overrides the default behavior to make it possible '
smutae7ea312016-07-18 11:59:41 -07001863 'to have the main solution untouched by gclient '
1864 '(gclient will check out unmanaged dependencies but '
1865 'will never sync them)')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001866 parser.add_option('--cache-dir',
1867 help='(git only) Cache all git repos into this dir and do '
1868 'shared clones from the cache, instead of cloning '
1869 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001870 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001871 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001872 if options.output_config_file:
1873 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001874 if ((options.spec and args) or len(args) > 2 or
1875 (not options.spec and not args)):
1876 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1877
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001878 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001879 if options.spec:
1880 client.SetConfig(options.spec)
1881 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001882 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001883 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001884 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001885 if name.endswith('.git'):
1886 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001887 else:
1888 # specify an alternate relpath for the given URL.
1889 name = options.name
agable@chromium.orgf2214672015-10-27 21:02:48 +00001890 if not os.path.abspath(os.path.join(os.getcwd(), name)).startswith(
1891 os.getcwd()):
1892 parser.error('Do not pass a relative path for --name.')
1893 if any(x in ('..', '.', '/', '\\') for x in name.split(os.sep)):
1894 parser.error('Do not include relative path components in --name.')
1895
nsylvain@google.comefc80932011-05-31 21:27:56 +00001896 deps_file = options.deps_file
agablea98a6cd2016-11-15 14:30:10 -08001897 client.SetDefaultConfig(name, deps_file, base_url,
smutae7ea312016-07-18 11:59:41 -07001898 managed=not options.unmanaged,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001899 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001900 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001901 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001902
1903
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001904@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001905 gclient pack > patch.txt
1906 generate simple patch for configured client and dependences
1907""")
1908def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001909 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001910
agabled437d762016-10-17 09:35:11 -07001911 Internally, runs 'git diff' on each checked out module and
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001912 dependencies, and performs minimal postprocessing of the output. The
1913 resulting patch is printed to stdout and can be applied to a freshly
1914 checked out tree via 'patch -p0 < patchfile'.
1915 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001916 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1917 help='override deps for the specified (comma-separated) '
1918 'platform(s); \'all\' will process all deps_os '
1919 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001920 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001921 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001922 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001923 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001924 client = GClient.LoadCurrentConfig(options)
1925 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001926 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001927 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001928 client.PrintLocationAndContents()
kbr@google.comab318592009-09-04 00:54:55 +00001929 return client.RunOnDeps('pack', args)
1930
1931
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001932def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001933 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001934 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1935 help='override deps for the specified (comma-separated) '
1936 'platform(s); \'all\' will process all deps_os '
1937 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001938 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001939 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001940 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001941 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001942 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001943 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001944 return client.RunOnDeps('status', args)
1945
1946
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001947@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001948 gclient sync
1949 update files from SCM according to current configuration,
1950 *for modules which have changed since last update or sync*
1951 gclient sync --force
1952 update files from SCM according to current configuration, for
1953 all modules (useful for recovering files deleted from local copy)
1954 gclient sync --revision src@31000
1955 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001956
1957JSON output format:
1958If the --output-json option is specified, the following document structure will
1959be emitted to the provided file. 'null' entries may occur for subprojects which
1960are present in the gclient solution, but were not processed (due to custom_deps,
1961os_deps, etc.)
1962
1963{
1964 "solutions" : {
1965 "<name>": { # <name> is the posix-normalized path to the solution.
agabled437d762016-10-17 09:35:11 -07001966 "revision": [<git id hex string>|null],
1967 "scm": ["git"|null],
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001968 }
1969 }
1970}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001971""")
1972def CMDsync(parser, args):
1973 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001974 parser.add_option('-f', '--force', action='store_true',
1975 help='force update even for unchanged modules')
1976 parser.add_option('-n', '--nohooks', action='store_true',
1977 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001978 parser.add_option('-p', '--noprehooks', action='store_true',
1979 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001980 parser.add_option('-r', '--revision', action='append',
1981 dest='revisions', metavar='REV', default=[],
1982 help='Enforces revision/hash for the solutions with the '
1983 'format src@rev. The src@ part is optional and can be '
1984 'skipped. -r can be used multiple times when .gclient '
1985 'has multiple solutions configured and will work even '
agablea98a6cd2016-11-15 14:30:10 -08001986 'if the src@ part is skipped.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00001987 parser.add_option('--with_branch_heads', action='store_true',
1988 help='Clone git "branch_heads" refspecs in addition to '
1989 'the default refspecs. This adds about 1/2GB to a '
1990 'full checkout. (git only)')
szager@chromium.org8d3348f2014-08-19 22:49:16 +00001991 parser.add_option('--with_tags', action='store_true',
1992 help='Clone git tags in addition to the default refspecs.')
agable2697cd12016-06-28 10:23:53 -07001993 parser.add_option('-H', '--head', action='store_true',
agablea98a6cd2016-11-15 14:30:10 -08001994 help='DEPRECATED: only made sense with safesync urls.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001995 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00001996 help='Deletes from the working copy any dependencies that '
1997 'have been removed since the last sync, as long as '
1998 'there are no local modifications. When used with '
1999 '--force, such dependencies are removed even if they '
2000 'have local modifications. When used with --reset, '
2001 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002002 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002003 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002004 parser.add_option('-R', '--reset', action='store_true',
2005 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00002006 parser.add_option('-M', '--merge', action='store_true',
2007 help='merge upstream changes instead of trying to '
2008 'fast-forward or rebase')
dnj@chromium.org5b23e872015-02-20 21:25:57 +00002009 parser.add_option('-A', '--auto_rebase', action='store_true',
2010 help='Automatically rebase repositories against local '
2011 'checkout during update (git only).')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002012 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2013 help='override deps for the specified (comma-separated) '
2014 'platform(s); \'all\' will process all deps_os '
2015 'references')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002016 parser.add_option('--upstream', action='store_true',
2017 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002018 parser.add_option('--output-json',
2019 help='Output a json document to this path containing '
2020 'summary information about the sync.')
primiano@chromium.org5439ea52014-08-06 17:18:18 +00002021 parser.add_option('--no-history', action='store_true',
2022 help='GIT ONLY - Reduces the size/time of the checkout at '
2023 'the cost of no history. Requires Git 1.9+')
hinoka@chromium.org46b87412014-05-15 00:42:05 +00002024 parser.add_option('--shallow', action='store_true',
2025 help='GIT ONLY - Do a shallow clone into the cache dir. '
2026 'Requires Git 1.9+')
e.hakkinen@samsung.come8bc1aa2015-04-08 08:00:37 +00002027 parser.add_option('--no_bootstrap', '--no-bootstrap',
2028 action='store_true',
2029 help='Don\'t bootstrap from Google Storage.')
hinoka@chromium.org8a10f6d2014-06-23 18:38:57 +00002030 parser.add_option('--ignore_locks', action='store_true',
2031 help='GIT ONLY - Ignore cache locks.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +00002032 parser.add_option('--break_repo_locks', action='store_true',
2033 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2034 'index.lock). This should only be used if you know for '
2035 'certain that this invocation of gclient is the only '
2036 'thing operating on the git repos (e.g. on a bot).')
nodir@chromium.org5b48e482016-03-18 20:27:54 +00002037 parser.add_option('--lock_timeout', type='int', default=5000,
szager@chromium.orgdbb6f822016-02-02 22:59:30 +00002038 help='GIT ONLY - Deadline (in seconds) to wait for git '
nodir@chromium.org5b48e482016-03-18 20:27:54 +00002039 'cache lock to become available. Default is %default.')
agabled437d762016-10-17 09:35:11 -07002040 # TODO(agable): Remove these when the oldest CrOS release milestone is M56.
2041 parser.add_option('-t', '--transitive', action='store_true',
2042 help='DEPRECATED: This is a no-op.')
sdefresne69b1be12016-10-18 05:48:02 -07002043 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
agabled437d762016-10-17 09:35:11 -07002044 help='DEPRECATED: This is a no-op.')
Paweł Hajdan, Jr7c7b5592017-05-23 15:06:05 +02002045 # TODO(phajdan.jr): Remove validation options once default (crbug/570091).
Paweł Hajdan, Jr694773d2017-05-29 16:06:23 +02002046 parser.add_option('--validate-syntax', action='store_true', default=True,
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02002047 help='Validate the .gclient and DEPS syntax')
Paweł Hajdan, Jr7c7b5592017-05-23 15:06:05 +02002048 parser.add_option('--disable-syntax-validation', action='store_false',
2049 dest='validate_syntax',
2050 help='Disable validation of .gclient and DEPS syntax.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002051 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002052 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002053
2054 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002055 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002056
smutae7ea312016-07-18 11:59:41 -07002057 if options.revisions and options.head:
2058 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
2059 print('Warning: you cannot use both --head and --revision')
2060
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002061 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002062 client.PrintLocationAndContents()
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002063 ret = client.RunOnDeps('update', args)
2064 if options.output_json:
2065 slns = {}
2066 for d in client.subtree(True):
2067 normed = d.name.replace('\\', '/').rstrip('/') + '/'
2068 slns[normed] = {
2069 'revision': d.got_revision,
2070 'scm': d.used_scm.name if d.used_scm else None,
hinoka@chromium.org17db9052014-05-10 01:11:29 +00002071 'url': str(d.url) if d.url else None,
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002072 }
2073 with open(options.output_json, 'wb') as f:
2074 json.dump({'solutions': slns}, f)
2075 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002076
2077
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002078CMDupdate = CMDsync
2079
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002080
Paweł Hajdan, Jre2f9feec2017-05-09 10:04:02 +02002081def CMDvalidate(parser, args):
2082 """Validates the .gclient and DEPS syntax."""
2083 options, args = parser.parse_args(args)
2084 options.validate_syntax = True
2085 client = GClient.LoadCurrentConfig(options)
2086 rv = client.RunOnDeps('validate', args)
2087 if rv == 0:
2088 print('validate: SUCCESS')
2089 else:
2090 print('validate: FAILURE')
2091 return rv
2092
2093
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002094def CMDdiff(parser, args):
2095 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002096 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2097 help='override deps for the specified (comma-separated) '
2098 'platform(s); \'all\' will process all deps_os '
2099 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002100 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002101 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002102 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002103 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002104 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002105 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002106 return client.RunOnDeps('diff', args)
2107
2108
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002109def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002110 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00002111
2112 That's the nuclear option to get back to a 'clean' state. It removes anything
agabled437d762016-10-17 09:35:11 -07002113 that shows up in git status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002114 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2115 help='override deps for the specified (comma-separated) '
2116 'platform(s); \'all\' will process all deps_os '
2117 'references')
2118 parser.add_option('-n', '--nohooks', action='store_true',
2119 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002120 parser.add_option('-p', '--noprehooks', action='store_true',
2121 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002122 parser.add_option('--upstream', action='store_true',
2123 help='Make repo state match upstream branch.')
iannucci@chromium.orgbf525dc2016-04-07 22:00:28 +00002124 parser.add_option('--break_repo_locks', action='store_true',
2125 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2126 'index.lock). This should only be used if you know for '
2127 'certain that this invocation of gclient is the only '
2128 'thing operating on the git repos (e.g. on a bot).')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002129 (options, args) = parser.parse_args(args)
2130 # --force is implied.
2131 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002132 options.reset = False
2133 options.delete_unversioned_trees = False
agablec903d732016-07-26 09:07:24 -07002134 options.merge = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002135 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002136 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002137 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002138 return client.RunOnDeps('revert', args)
2139
2140
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002141def CMDrunhooks(parser, args):
2142 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002143 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2144 help='override deps for the specified (comma-separated) '
2145 'platform(s); \'all\' will process all deps_os '
2146 'references')
2147 parser.add_option('-f', '--force', action='store_true', default=True,
2148 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002149 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002150 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002151 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002152 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002153 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002154 client.PrintLocationAndContents()
maruel@chromium.org5df6a462009-08-28 18:52:26 +00002155 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002156 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002157 return client.RunOnDeps('runhooks', args)
2158
2159
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002160def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002161 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002162
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002163 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002164 can be used to reproduce the same tree in the future. It is only useful for
agabled437d762016-10-17 09:35:11 -07002165 'unpinned dependencies', i.e. DEPS/deps references without a git hash.
2166 A git branch name isn't 'pinned' since the actual commit can change.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002167 """
2168 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2169 help='override deps for the specified (comma-separated) '
2170 'platform(s); \'all\' will process all deps_os '
2171 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002172 parser.add_option('-a', '--actual', action='store_true',
2173 help='gets the actual checked out revisions instead of the '
2174 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002175 parser.add_option('-s', '--snapshot', action='store_true',
2176 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002177 'version of all repositories to reproduce the tree, '
2178 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002179 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002180 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002181 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002182 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002183 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002184 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002185
2186
szager@google.comb9a78d32012-03-13 18:46:21 +00002187def CMDhookinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002188 """Outputs the hooks that would be run by `gclient runhooks`."""
szager@google.comb9a78d32012-03-13 18:46:21 +00002189 (options, args) = parser.parse_args(args)
2190 options.force = True
2191 client = GClient.LoadCurrentConfig(options)
2192 if not client:
2193 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2194 client.RunOnDeps(None, [])
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002195 print('; '.join(' '.join(hook) for hook in client.GetHooks(options)))
szager@google.comb9a78d32012-03-13 18:46:21 +00002196 return 0
2197
2198
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002199def CMDverify(parser, args):
2200 """Verifies the DEPS file deps are only from allowed_hosts."""
2201 (options, args) = parser.parse_args(args)
2202 client = GClient.LoadCurrentConfig(options)
2203 if not client:
2204 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2205 client.RunOnDeps(None, [])
2206 # Look at each first-level dependency of this gclient only.
2207 for dep in client.dependencies:
2208 bad_deps = dep.findDepsFromNotAllowedHosts()
2209 if not bad_deps:
2210 continue
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002211 print("There are deps from not allowed hosts in file %s" % dep.deps_file)
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002212 for bad_dep in bad_deps:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002213 print("\t%s at %s" % (bad_dep.name, bad_dep.url))
2214 print("allowed_hosts:", ', '.join(dep.allowed_hosts))
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002215 sys.stdout.flush()
2216 raise gclient_utils.Error(
2217 'dependencies from disallowed hosts; check your DEPS file.')
2218 return 0
2219
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002220class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00002221 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002222
2223 def __init__(self, **kwargs):
2224 optparse.OptionParser.__init__(
2225 self, version='%prog ' + __version__, **kwargs)
2226
2227 # Some arm boards have issues with parallel sync.
2228 if platform.machine().startswith('arm'):
2229 jobs = 1
2230 else:
2231 jobs = max(8, gclient_utils.NumLocalCpus())
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002232
2233 self.add_option(
2234 '-j', '--jobs', default=jobs, type='int',
2235 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002236 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002237 self.add_option(
2238 '-v', '--verbose', action='count', default=0,
2239 help='Produces additional output for diagnostics. Can be used up to '
2240 'three times for more logging info.')
2241 self.add_option(
2242 '--gclientfile', dest='config_filename',
2243 help='Specify an alternate %s file' % self.gclientfile_default)
2244 self.add_option(
2245 '--spec',
2246 help='create a gclient file containing the provided string. Due to '
2247 'Cygwin/Python brokenness, it can\'t contain any newlines.')
2248 self.add_option(
2249 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00002250 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002251
2252 def parse_args(self, args=None, values=None):
2253 """Integrates standard options processing."""
2254 options, args = optparse.OptionParser.parse_args(self, args, values)
2255 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
2256 logging.basicConfig(
2257 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00002258 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002259 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002260 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00002261 if (options.config_filename and
2262 options.config_filename != os.path.basename(options.config_filename)):
2263 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002264 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002265 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00002266 options.entries_filename = options.config_filename + '_entries'
2267 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002268 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00002269
2270 # These hacks need to die.
2271 if not hasattr(options, 'revisions'):
2272 # GClient.RunOnDeps expects it even if not applicable.
2273 options.revisions = []
smutae7ea312016-07-18 11:59:41 -07002274 if not hasattr(options, 'head'):
2275 options.head = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00002276 if not hasattr(options, 'nohooks'):
2277 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002278 if not hasattr(options, 'noprehooks'):
2279 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00002280 if not hasattr(options, 'deps_os'):
2281 options.deps_os = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00002282 if not hasattr(options, 'force'):
2283 options.force = None
2284 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002285
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002286
2287def disable_buffering():
2288 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
2289 # operations. Python as a strong tendency to buffer sys.stdout.
2290 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
2291 # Make stdout annotated with the thread ids.
2292 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00002293
2294
sbc@chromium.org013731e2015-02-26 18:28:43 +00002295def main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002296 """Doesn't parse the arguments here, just find the right subcommand to
2297 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002298 if sys.hexversion < 0x02060000:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002299 print(
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002300 '\nYour python version %s is unsupported, please upgrade.\n' %
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002301 sys.version.split(' ', 1)[0],
2302 file=sys.stderr)
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002303 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002304 if not sys.executable:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002305 print(
2306 '\nPython cannot find the location of it\'s own executable.\n',
2307 file=sys.stderr)
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002308 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002309 fix_encoding.fix_encoding()
2310 disable_buffering()
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00002311 setup_color.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002312 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002313 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002314 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00002315 except KeyboardInterrupt:
2316 gclient_utils.GClientChildren.KillAllRemainingChildren()
2317 raise
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00002318 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002319 print('Error: %s' % str(e), file=sys.stderr)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002320 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00002321 finally:
2322 gclient_utils.PrintWarnings()
sbc@chromium.org013731e2015-02-26 18:28:43 +00002323 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002324
2325
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00002326if '__main__' == __name__:
sbc@chromium.org013731e2015-02-26 18:28:43 +00002327 try:
2328 sys.exit(main(sys.argv[1:]))
2329 except KeyboardInterrupt:
2330 sys.stderr.write('interrupted\n')
2331 sys.exit(1)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002332
2333# vim: ts=2:sw=2:tw=80:et: