blob: 03c9ac906148f89f1ebc73350e707eb6c87078b9 [file] [log] [blame]
iannucci@chromium.org405b87e2015-11-12 18:08:34 +00001#!/usr/bin/env python
thakis@chromium.org4f474b62012-01-18 01:31:29 +00002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
maruel@chromium.orgba551772010-02-03 18:21:42 +00003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00005
maruel@chromium.org39c0b222013-08-17 16:57:01 +00006"""Meta checkout manager supporting both Subversion and GIT."""
7# Files
8# .gclient : Current client configuration, written by 'config' command.
9# Format is a Python script defining 'solutions', a list whose
10# entries each are maps binding the strings "name" and "url"
11# to strings specifying the name and location of the client
12# module, as well as "custom_deps" to a map similar to the
13# deps section of the DEPS file below, as well as
14# "custom_hooks" to a list similar to the hooks sections of
15# the DEPS file below.
16# .gclient_entries : A cache constructed by 'update' command. Format is a
17# Python script defining 'entries', a list of the names
18# of all modules in the client
19# <module>/DEPS : Python script defining var 'deps' as a map from each
20# requisite submodule name to a URL where it can be found (via
21# one SCM)
22#
23# Hooks
24# .gclient and DEPS files may optionally contain a list named "hooks" to
25# allow custom actions to be performed based on files that have changed in the
26# working copy as a result of a "sync"/"update" or "revert" operation. This
27# can be prevented by using --nohooks (hooks run by default). Hooks can also
28# be forced to run with the "runhooks" operation. If "sync" is run with
29# --force, all known but not suppressed hooks will run regardless of the state
30# of the working copy.
31#
32# Each item in a "hooks" list is a dict, containing these two keys:
33# "pattern" The associated value is a string containing a regular
34# expression. When a file whose pathname matches the expression
35# is checked out, updated, or reverted, the hook's "action" will
36# run.
37# "action" A list describing a command to run along with its arguments, if
38# any. An action command will run at most one time per gclient
39# invocation, regardless of how many files matched the pattern.
40# The action is executed in the same directory as the .gclient
41# file. If the first item in the list is the string "python",
42# the current Python interpreter (sys.executable) will be used
43# to run the command. If the list contains string
44# "$matching_files" it will be removed from the list and the list
45# will be extended by the list of matching files.
46# "name" An optional string specifying the group to which a hook belongs
47# for overriding and organizing.
48#
49# Example:
50# hooks = [
51# { "pattern": "\\.(gif|jpe?g|pr0n|png)$",
52# "action": ["python", "image_indexer.py", "--all"]},
53# { "pattern": ".",
54# "name": "gyp",
55# "action": ["python", "src/build/gyp_chromium"]},
56# ]
57#
borenet@google.com2d1ee9e2013-10-15 08:13:16 +000058# Pre-DEPS Hooks
59# DEPS files may optionally contain a list named "pre_deps_hooks". These are
60# the same as normal hooks, except that they run before the DEPS are
61# processed. Pre-DEPS run with "sync" and "revert" unless the --noprehooks
62# flag is used.
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +000063#
maruel@chromium.org39c0b222013-08-17 16:57:01 +000064# Specifying a target OS
65# An optional key named "target_os" may be added to a gclient file to specify
66# one or more additional operating systems that should be considered when
67# processing the deps_os dict of a DEPS file.
68#
69# Example:
70# target_os = [ "android" ]
71#
72# If the "target_os_only" key is also present and true, then *only* the
73# operating systems listed in "target_os" will be used.
74#
75# Example:
76# target_os = [ "ios" ]
77# target_os_only = True
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000078
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +000079from __future__ import print_function
80
maruel@chromium.org39c0b222013-08-17 16:57:01 +000081__version__ = '0.7'
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000082
szager@chromium.org7b8b6de2014-08-23 00:57:31 +000083import ast
maruel@chromium.org9e5317a2010-08-13 20:35:11 +000084import copy
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +000085import json
maruel@chromium.org754960e2009-09-21 12:31:05 +000086import logging
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000087import optparse
88import os
bradnelson@google.com4949dab2012-04-19 16:41:07 +000089import platform
maruel@chromium.org621939b2010-08-10 20:12:00 +000090import posixpath
msb@chromium.org2e38de72009-09-28 17:04:47 +000091import pprint
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000092import re
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000093import sys
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +000094import time
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000095import urllib
bradnelson@google.com4949dab2012-04-19 16:41:07 +000096import urlparse
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000097
maruel@chromium.org35625c72011-03-23 17:34:02 +000098import fix_encoding
maruel@chromium.org5f3eee32009-09-17 00:34:30 +000099import gclient_scm
100import gclient_utils
szager@chromium.org848fd492014-04-09 19:06:44 +0000101import git_cache
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000102from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000103import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000104import subprocess2
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +0000105import setup_color
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000106
szager@chromium.org7b8b6de2014-08-23 00:57:31 +0000107CHROMIUM_SRC_URL = 'https://chromium.googlesource.com/chromium/src.git'
108
109
110def ast_dict_index(dnode, key):
111 """Search an ast.Dict for the argument key, and return its index."""
112 idx = [i for i in range(len(dnode.keys)) if (
113 type(dnode.keys[i]) is ast.Str and dnode.keys[i].s == key)]
114 if not idx:
115 return -1
116 elif len(idx) > 1:
117 raise gclient_utils.Error('Multiple dict entries with same key in AST')
118 return idx[-1]
119
120def ast2str(node, indent=0):
121 """Return a pretty-printed rendition of an ast.Node."""
122 t = type(node)
123 if t is ast.Module:
124 return '\n'.join([ast2str(x, indent) for x in node.body])
125 elif t is ast.Assign:
126 return ((' ' * indent) +
127 ' = '.join([ast2str(x) for x in node.targets] +
128 [ast2str(node.value, indent)]) + '\n')
129 elif t is ast.Name:
130 return node.id
131 elif t is ast.List:
132 if not node.elts:
133 return '[]'
134 elif len(node.elts) == 1:
135 return '[' + ast2str(node.elts[0], indent) + ']'
136 return ('[\n' + (' ' * (indent + 1)) +
137 (',\n' + (' ' * (indent + 1))).join(
138 [ast2str(x, indent + 1) for x in node.elts]) +
139 '\n' + (' ' * indent) + ']')
140 elif t is ast.Dict:
141 if not node.keys:
142 return '{}'
143 elif len(node.keys) == 1:
144 return '{%s: %s}' % (ast2str(node.keys[0]),
145 ast2str(node.values[0], indent + 1))
146 return ('{\n' + (' ' * (indent + 1)) +
147 (',\n' + (' ' * (indent + 1))).join(
148 ['%s: %s' % (ast2str(node.keys[i]),
149 ast2str(node.values[i], indent + 1))
150 for i in range(len(node.keys))]) +
151 '\n' + (' ' * indent) + '}')
152 elif t is ast.Str:
153 return "'%s'" % node.s
154 else:
155 raise gclient_utils.Error("Unexpected AST node at line %d, column %d: %s"
156 % (node.lineno, node.col_offset, t))
157
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000158
maruel@chromium.org116704f2010-06-11 17:34:38 +0000159class GClientKeywords(object):
160 class FromImpl(object):
161 """Used to implement the From() syntax."""
162
163 def __init__(self, module_name, sub_target_name=None):
164 """module_name is the dep module we want to include from. It can also be
165 the name of a subdirectory to include from.
166
167 sub_target_name is an optional parameter if the module name in the other
168 DEPS file is different. E.g., you might want to map src/net to net."""
169 self.module_name = module_name
170 self.sub_target_name = sub_target_name
171
172 def __str__(self):
173 return 'From(%s, %s)' % (repr(self.module_name),
174 repr(self.sub_target_name))
175
maruel@chromium.org116704f2010-06-11 17:34:38 +0000176 class FileImpl(object):
177 """Used to implement the File('') syntax which lets you sync a single file
maruel@chromium.orge3216c62010-07-08 03:31:43 +0000178 from a SVN repo."""
maruel@chromium.org116704f2010-06-11 17:34:38 +0000179
180 def __init__(self, file_location):
181 self.file_location = file_location
182
183 def __str__(self):
184 return 'File("%s")' % self.file_location
185
186 def GetPath(self):
187 return os.path.split(self.file_location)[0]
188
189 def GetFilename(self):
190 rev_tokens = self.file_location.split('@')
191 return os.path.split(rev_tokens[0])[1]
192
193 def GetRevision(self):
194 rev_tokens = self.file_location.split('@')
195 if len(rev_tokens) > 1:
196 return rev_tokens[1]
197 return None
198
199 class VarImpl(object):
200 def __init__(self, custom_vars, local_scope):
201 self._custom_vars = custom_vars
202 self._local_scope = local_scope
203
204 def Lookup(self, var_name):
205 """Implements the Var syntax."""
206 if var_name in self._custom_vars:
207 return self._custom_vars[var_name]
208 elif var_name in self._local_scope.get("vars", {}):
209 return self._local_scope["vars"][var_name]
210 raise gclient_utils.Error("Var is not defined: %s" % var_name)
211
212
maruel@chromium.org064186c2011-09-27 23:53:33 +0000213class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000214 """Immutable configuration settings."""
215 def __init__(
smutae7ea312016-07-18 11:59:41 -0700216 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000217 custom_hooks, deps_file, should_process):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000218 GClientKeywords.__init__(self)
219
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000220 # These are not mutable:
221 self._parent = parent
smutae7ea312016-07-18 11:59:41 -0700222 self._safesync_url = safesync_url
mmoss@chromium.org8f93f792014-08-26 23:24:09 +0000223 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000224 self._url = url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000225 # 'managed' determines whether or not this dependency is synced/updated by
226 # gclient after gclient checks it out initially. The difference between
227 # 'managed' and 'should_process' is that the user specifies 'managed' via
smutae7ea312016-07-18 11:59:41 -0700228 # the --unmanaged command-line flag or a .gclient config, where
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000229 # 'should_process' is dynamically set by gclient if it goes over its
230 # recursion limit and controls gclient's behavior so it does not misbehave.
231 self._managed = managed
232 self._should_process = should_process
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000233 # This is a mutable value which has the list of 'target_os' OSes listed in
234 # the current deps file.
235 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000236
237 # These are only set in .gclient and not in DEPS files.
238 self._custom_vars = custom_vars or {}
239 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000240 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000241
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000242 # TODO(iannucci): Remove this when all masters are correctly substituting
243 # the new blink url.
244 if (self._custom_vars.get('webkit_trunk', '') ==
245 'svn://svn-mirror.golo.chromium.org/webkit-readonly/trunk'):
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000246 new_url = 'svn://svn-mirror.golo.chromium.org/blink/trunk'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000247 print('Overwriting Var("webkit_trunk") with %s' % new_url)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000248 self._custom_vars['webkit_trunk'] = new_url
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000249
maruel@chromium.org064186c2011-09-27 23:53:33 +0000250 # Post process the url to remove trailing slashes.
251 if isinstance(self._url, basestring):
252 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
253 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000254 self._url = self._url.replace('/@', '@')
255 elif not isinstance(self._url,
256 (self.FromImpl, self.FileImpl, None.__class__)):
257 raise gclient_utils.Error(
258 ('dependency url must be either a string, None, '
259 'File() or From() instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000260 # Make any deps_file path platform-appropriate.
261 for sep in ['/', '\\']:
262 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000263
264 @property
265 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000266 return self._deps_file
267
268 @property
269 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000270 return self._managed
271
272 @property
273 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000274 return self._parent
275
276 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000277 def root(self):
278 """Returns the root node, a GClient object."""
279 if not self.parent:
280 # This line is to signal pylint that it could be a GClient instance.
281 return self or GClient(None, None)
282 return self.parent.root
283
284 @property
smutae7ea312016-07-18 11:59:41 -0700285 def safesync_url(self):
286 return self._safesync_url
287
288 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000289 def should_process(self):
290 """True if this dependency should be processed, i.e. checked out."""
291 return self._should_process
292
293 @property
294 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000295 return self._custom_vars.copy()
296
297 @property
298 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000299 return self._custom_deps.copy()
300
maruel@chromium.org064186c2011-09-27 23:53:33 +0000301 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000302 def custom_hooks(self):
303 return self._custom_hooks[:]
304
305 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000306 def url(self):
307 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000308
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000309 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000310 def target_os(self):
311 if self.local_target_os is not None:
312 return tuple(set(self.local_target_os).union(self.parent.target_os))
313 else:
314 return self.parent.target_os
315
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000316 def get_custom_deps(self, name, url):
317 """Returns a custom deps if applicable."""
318 if self.parent:
319 url = self.parent.get_custom_deps(name, url)
320 # None is a valid return value to disable a dependency.
321 return self.custom_deps.get(name, url)
322
maruel@chromium.org064186c2011-09-27 23:53:33 +0000323
324class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000325 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000326
smutae7ea312016-07-18 11:59:41 -0700327 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000328 custom_vars, custom_hooks, deps_file, should_process):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000329 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000330 DependencySettings.__init__(
smutae7ea312016-07-18 11:59:41 -0700331 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000332 custom_hooks, deps_file, should_process)
maruel@chromium.org68988972011-09-20 14:11:42 +0000333
334 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000335 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000336
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000337 self._pre_deps_hooks = []
338
maruel@chromium.org68988972011-09-20 14:11:42 +0000339 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000340 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000341 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000342 # A cache of the files affected by the current operation, necessary for
343 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000344 self._file_list = []
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000345 # List of host names from which dependencies are allowed.
346 # Default is an empty set, meaning unspecified in DEPS file, and hence all
347 # hosts will be allowed. Non-empty set means whitelist of hosts.
348 # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
349 self._allowed_hosts = frozenset()
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000350 # If it is not set to True, the dependency wasn't processed for its child
351 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000352 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000353 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000354 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000355 # This dependency had its pre-DEPS hooks run
356 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000357 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000358 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000359 # This is the scm used to checkout self.url. It may be used by dependencies
360 # to get the datetime of the revision we checked out.
361 self._used_scm = None
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000362 self._used_revision = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000363 # The actual revision we ended up getting, or None if that information is
364 # unavailable
365 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000366
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000367 # This is a mutable value that overrides the normal recursion limit for this
368 # dependency. It is read from the actual DEPS file so cannot be set on
369 # class instantiation.
370 self.recursion_override = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000371 # recursedeps is a mutable value that selectively overrides the default
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000372 # 'no recursion' setting on a dep-by-dep basis. It will replace
373 # recursion_override.
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000374 #
375 # It will be a dictionary of {deps_name: {"deps_file": depfile_name}} or
376 # None.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000377 self.recursedeps = None
hinoka885e5b12016-06-08 14:40:09 -0700378 # This is inherited from WorkItem. We want the URL to be a resource.
379 if url and isinstance(url, basestring):
380 # The url is usually given to gclient either as https://blah@123
381 # or just https://blah. The @123 portion is irrelevent.
382 self.resources.append(url.split('@')[0])
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000383
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000384 if not self.name and self.parent:
385 raise gclient_utils.Error('Dependency without name')
386
maruel@chromium.org470b5432011-10-11 18:18:19 +0000387 @property
388 def requirements(self):
389 """Calculate the list of requirements."""
390 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000391 # self.parent is implicitly a requirement. This will be recursive by
392 # definition.
393 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000394 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000395
396 # For a tree with at least 2 levels*, the leaf node needs to depend
397 # on the level higher up in an orderly way.
398 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
399 # thus unsorted, while the .gclient format is a list thus sorted.
400 #
401 # * _recursion_limit is hard coded 2 and there is no hope to change this
402 # value.
403 #
404 # Interestingly enough, the following condition only works in the case we
405 # want: self is a 2nd level node. 3nd level node wouldn't need this since
406 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000407 if self.parent and self.parent.parent and not self.parent.parent.parent:
408 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000409
410 if isinstance(self.url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000411 requirements.add(self.url.module_name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000412
maruel@chromium.org470b5432011-10-11 18:18:19 +0000413 if self.name:
414 requirements |= set(
415 obj.name for obj in self.root.subtree(False)
416 if (obj is not self
417 and obj.name and
418 self.name.startswith(posixpath.join(obj.name, ''))))
419 requirements = tuple(sorted(requirements))
420 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
421 return requirements
422
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000423 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000424 def try_recursedeps(self):
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000425 """Returns False if recursion_override is ever specified."""
426 if self.recursion_override is not None:
427 return False
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000428 return self.parent.try_recursedeps
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000429
430 @property
431 def recursion_limit(self):
432 """Returns > 0 if this dependency is not too recursed to be processed."""
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000433 # We continue to support the absence of recursedeps until tools and DEPS
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000434 # using recursion_override are updated.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000435 if self.try_recursedeps and self.parent.recursedeps != None:
436 if self.name in self.parent.recursedeps:
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000437 return 1
438
439 if self.recursion_override is not None:
440 return self.recursion_override
441 return max(self.parent.recursion_limit - 1, 0)
442
maruel@chromium.org470b5432011-10-11 18:18:19 +0000443 def verify_validity(self):
444 """Verifies that this Dependency is fine to add as a child of another one.
445
446 Returns True if this entry should be added, False if it is a duplicate of
447 another entry.
448 """
449 logging.info('Dependency(%s).verify_validity()' % self.name)
450 if self.name in [s.name for s in self.parent.dependencies]:
451 raise gclient_utils.Error(
452 'The same name "%s" appears multiple times in the deps section' %
453 self.name)
454 if not self.should_process:
455 # Return early, no need to set requirements.
456 return True
457
458 # This require a full tree traversal with locks.
459 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
460 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000461 self_url = self.LateOverride(self.url)
462 sibling_url = sibling.LateOverride(sibling.url)
463 # Allow to have only one to be None or ''.
464 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000465 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000466 ('Dependency %s specified more than once:\n'
467 ' %s [%s]\n'
468 'vs\n'
469 ' %s [%s]') % (
470 self.name,
471 sibling.hierarchy(),
472 sibling_url,
473 self.hierarchy(),
474 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000475 # In theory we could keep it as a shadow of the other one. In
476 # practice, simply ignore it.
477 logging.warn('Won\'t process duplicate dependency %s' % sibling)
478 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000479 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000480
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000481 def LateOverride(self, url):
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000482 """Resolves the parsed url from url.
483
484 Manages From() keyword accordingly. Do not touch self.parsed_url nor
485 self.url because it may called with other urls due to From()."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000486 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000487 parsed_url = self.get_custom_deps(self.name, url)
488 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000489 logging.info(
490 'Dependency(%s).LateOverride(%s) -> %s' %
491 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000492 return parsed_url
493
494 if isinstance(url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000495 # Requires tree traversal.
maruel@chromium.org68988972011-09-20 14:11:42 +0000496 ref = [
497 dep for dep in self.root.subtree(True) if url.module_name == dep.name
498 ]
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000499 if not ref:
500 raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
501 url.module_name, ref))
502 # It may happen that len(ref) > 1 but it's no big deal.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000503 ref = ref[0]
maruel@chromium.org98d05fa2010-07-22 21:58:01 +0000504 sub_target = url.sub_target_name or self.name
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000505 found_deps = [d for d in ref.dependencies if d.name == sub_target]
506 if len(found_deps) != 1:
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000507 raise gclient_utils.Error(
maruel@chromium.org98023df2011-09-07 18:44:47 +0000508 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
509 sub_target, ref.name, self.name, self.parent.name,
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000510 str(self.root)))
maruel@chromium.org98023df2011-09-07 18:44:47 +0000511
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000512 # Call LateOverride() again.
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000513 found_dep = found_deps[0]
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000514 parsed_url = found_dep.LateOverride(found_dep.url)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000515 logging.info(
maruel@chromium.org470b5432011-10-11 18:18:19 +0000516 'Dependency(%s).LateOverride(%s) -> %s (From)' %
517 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000518 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000519
520 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000521 parsed_url = urlparse.urlparse(url)
scr@chromium.orgf1eccaf2014-04-11 15:51:33 +0000522 if (not parsed_url[0] and
523 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000524 # A relative url. Fetch the real base.
525 path = parsed_url[2]
526 if not path.startswith('/'):
527 raise gclient_utils.Error(
528 'relative DEPS entry \'%s\' must begin with a slash' % url)
529 # Create a scm just to query the full url.
530 parent_url = self.parent.parsed_url
531 if isinstance(parent_url, self.FileImpl):
532 parent_url = parent_url.file_location
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000533 scm = gclient_scm.CreateSCM(
534 parent_url, self.root.root_dir, None, self.outbuf)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000535 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000536 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000537 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000538 logging.info(
539 'Dependency(%s).LateOverride(%s) -> %s' %
540 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000541 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000542
543 if isinstance(url, self.FileImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000544 logging.info(
545 'Dependency(%s).LateOverride(%s) -> %s (File)' %
546 (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000547 return url
548
549 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000550 logging.info(
551 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000552 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000553
554 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000555
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000556 @staticmethod
557 def MergeWithOsDeps(deps, deps_os, target_os_list):
558 """Returns a new "deps" structure that is the deps sent in updated
559 with information from deps_os (the deps_os section of the DEPS
560 file) that matches the list of target os."""
561 os_overrides = {}
562 for the_target_os in target_os_list:
563 the_target_os_deps = deps_os.get(the_target_os, {})
564 for os_dep_key, os_dep_value in the_target_os_deps.iteritems():
565 overrides = os_overrides.setdefault(os_dep_key, [])
566 overrides.append((the_target_os, os_dep_value))
567
568 # If any os didn't specify a value (we have fewer value entries
569 # than in the os list), then it wants to use the default value.
570 for os_dep_key, os_dep_value in os_overrides.iteritems():
571 if len(os_dep_value) != len(target_os_list):
572 # Record the default value too so that we don't accidently
573 # set it to None or miss a conflicting DEPS.
574 if os_dep_key in deps:
575 os_dep_value.append(('default', deps[os_dep_key]))
576
577 target_os_deps = {}
578 for os_dep_key, os_dep_value in os_overrides.iteritems():
579 # os_dep_value is a list of (os, value) pairs.
580 possible_values = set(x[1] for x in os_dep_value if x[1] is not None)
581 if not possible_values:
582 target_os_deps[os_dep_key] = None
583 else:
584 if len(possible_values) > 1:
585 # It would be possible to abort here but it would be
586 # unfortunate if we end up preventing any kind of checkout.
587 logging.error('Conflicting dependencies for %s: %s. (target_os=%s)',
588 os_dep_key, os_dep_value, target_os_list)
589 # Sorting to get the same result every time in case of conflicts.
590 target_os_deps[os_dep_key] = sorted(possible_values)[0]
591
592 new_deps = deps.copy()
593 new_deps.update(target_os_deps)
594 return new_deps
595
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000596 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000597 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000598 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000599 assert not self.dependencies
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000600
601 deps_content = None
602 use_strict = False
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000603
604 # First try to locate the configured deps file. If it's missing, fallback
605 # to DEPS.
606 deps_files = [self.deps_file]
607 if 'DEPS' not in deps_files:
608 deps_files.append('DEPS')
609 for deps_file in deps_files:
610 filepath = os.path.join(self.root.root_dir, self.name, deps_file)
611 if os.path.isfile(filepath):
612 logging.info(
613 'ParseDepsFile(%s): %s file found at %s', self.name, deps_file,
614 filepath)
615 break
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000616 logging.info(
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000617 'ParseDepsFile(%s): No %s file found at %s', self.name, deps_file,
618 filepath)
619
620 if os.path.isfile(filepath):
maruel@chromium.org46304292010-10-28 11:42:00 +0000621 deps_content = gclient_utils.FileRead(filepath)
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000622 logging.debug('ParseDepsFile(%s) read:\n%s', self.name, deps_content)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000623 use_strict = 'use strict' in deps_content.splitlines()[0]
624
625 local_scope = {}
626 if deps_content:
627 # One thing is unintuitive, vars = {} must happen before Var() use.
628 var = self.VarImpl(self.custom_vars, local_scope)
629 if use_strict:
630 logging.info(
631 'ParseDepsFile(%s): Strict Mode Enabled', self.name)
632 global_scope = {
633 '__builtins__': {'None': None},
634 'Var': var.Lookup,
635 'deps_os': {},
636 }
637 else:
638 global_scope = {
639 'File': self.FileImpl,
640 'From': self.FromImpl,
641 'Var': var.Lookup,
642 'deps_os': {},
643 }
maruel@chromium.org46304292010-10-28 11:42:00 +0000644 # Eval the content.
645 try:
646 exec(deps_content, global_scope, local_scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +0000647 except SyntaxError as e:
maruel@chromium.org46304292010-10-28 11:42:00 +0000648 gclient_utils.SyntaxErrorToError(filepath, e)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000649 if use_strict:
650 for key, val in local_scope.iteritems():
651 if not isinstance(val, (dict, list, tuple, str)):
652 raise gclient_utils.Error(
653 'ParseDepsFile(%s): Strict mode disallows %r -> %r' %
654 (self.name, key, val))
655
maruel@chromium.org271375b2010-06-23 19:17:38 +0000656 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000657 if 'recursion' in local_scope:
658 self.recursion_override = local_scope.get('recursion')
659 logging.warning(
660 'Setting %s recursion to %d.', self.name, self.recursion_limit)
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000661 self.recursedeps = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000662 if 'recursedeps' in local_scope:
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000663 self.recursedeps = {}
664 for ent in local_scope['recursedeps']:
665 if isinstance(ent, basestring):
666 self.recursedeps[ent] = {"deps_file": self.deps_file}
667 else: # (depname, depsfilename)
668 self.recursedeps[ent[0]] = {"deps_file": ent[1]}
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000669 logging.warning('Found recursedeps %r.', repr(self.recursedeps))
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000670 # If present, save 'target_os' in the local_target_os property.
671 if 'target_os' in local_scope:
672 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000673 # load os specific dependencies if defined. these dependencies may
674 # override or extend the values defined by the 'deps' member.
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000675 target_os_list = self.target_os
676 if 'deps_os' in local_scope and target_os_list:
677 deps = self.MergeWithOsDeps(deps, local_scope['deps_os'], target_os_list)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000678
maruel@chromium.org271375b2010-06-23 19:17:38 +0000679 # If a line is in custom_deps, but not in the solution, we want to append
680 # this line to the solution.
681 for d in self.custom_deps:
682 if d not in deps:
683 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000684
685 # If use_relative_paths is set in the DEPS file, regenerate
686 # the dictionary using paths relative to the directory containing
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000687 # the DEPS file. Also update recursedeps if use_relative_paths is
688 # enabled.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000689 use_relative_paths = local_scope.get('use_relative_paths', False)
690 if use_relative_paths:
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000691 logging.warning('use_relative_paths enabled.')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000692 rel_deps = {}
693 for d, url in deps.items():
694 # normpath is required to allow DEPS to use .. in their
695 # dependency local path.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000696 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000697 logging.warning('Updating deps by prepending %s.', self.name)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000698 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000699
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000700 # Update recursedeps if it's set.
701 if self.recursedeps is not None:
702 logging.warning('Updating recursedeps by prepending %s.', self.name)
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000703 rel_deps = {}
704 for depname, options in self.recursedeps.iteritems():
705 rel_deps[os.path.normpath(os.path.join(self.name, depname))] = options
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000706 self.recursedeps = rel_deps
707
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000708 if 'allowed_hosts' in local_scope:
709 try:
710 self._allowed_hosts = frozenset(local_scope.get('allowed_hosts'))
711 except TypeError: # raised if non-iterable
712 pass
713 if not self._allowed_hosts:
714 logging.warning("allowed_hosts is specified but empty %s",
715 self._allowed_hosts)
716 raise gclient_utils.Error(
717 'ParseDepsFile(%s): allowed_hosts must be absent '
718 'or a non-empty iterable' % self.name)
719
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000720 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000721 deps_to_add = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000722 for name, url in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000723 should_process = self.recursion_limit and self.should_process
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000724 deps_file = self.deps_file
725 if self.recursedeps is not None:
726 ent = self.recursedeps.get(name)
727 if ent is not None:
728 deps_file = ent['deps_file']
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000729 deps_to_add.append(Dependency(
smutae7ea312016-07-18 11:59:41 -0700730 self, name, url, None, None, None, self.custom_vars, None,
iannucci@chromium.orgafa11ac2016-05-04 22:17:34 +0000731 deps_file, should_process))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000732 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000733
734 # override named sets of hooks by the custom hooks
735 hooks_to_run = []
736 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
737 for hook in local_scope.get('hooks', []):
738 if hook.get('name', '') not in hook_names_to_suppress:
739 hooks_to_run.append(hook)
740
741 # add the replacements and any additions
742 for hook in self.custom_hooks:
743 if 'action' in hook:
744 hooks_to_run.append(hook)
745
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000746 self._pre_deps_hooks = [self.GetHookAction(hook, []) for hook in
747 local_scope.get('pre_deps_hooks', [])]
748
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000749 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000750 logging.info('ParseDepsFile(%s) done' % self.name)
751
752 def add_dependencies_and_close(self, deps_to_add, hooks):
753 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000754 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000755 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000756 self.add_dependency(dep)
757 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000758
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000759 def maybeGetParentRevision(self, command, options, parsed_url, parent):
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000760 """Uses revision/timestamp of parent if no explicit revision was specified.
761
762 If we are performing an update and --transitive is set, use
763 - the parent's revision if 'self.url' is in the same repository
764 - the parent's timestamp otherwise
765 to update 'self.url'. The used revision/timestamp will be set in
766 'options.revision'.
767 If we have an explicit revision do nothing.
768 """
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000769 if command == 'update' and options.transitive and not options.revision:
770 _, revision = gclient_utils.SplitUrlRevision(parsed_url)
771 if not revision:
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000772 options.revision = getattr(parent, '_used_revision', None)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000773 if (options.revision and
774 not gclient_utils.IsDateRevision(options.revision)):
775 assert self.parent and self.parent.used_scm
776 # If this dependency is in the same repository as parent it's url will
777 # start with a slash. If so we take the parent revision instead of
778 # it's timestamp.
779 # (The timestamps of commits in google code are broken -- which can
780 # result in dependencies to be checked out at the wrong revision)
781 if self.url.startswith('/'):
782 if options.verbose:
783 print('Using parent\'s revision %s since we are in the same '
784 'repository.' % options.revision)
785 else:
786 parent_revision_date = self.parent.used_scm.GetRevisionDate(
787 options.revision)
788 options.revision = gclient_utils.MakeDateRevision(
789 parent_revision_date)
790 if options.verbose:
791 print('Using parent\'s revision date %s since we are in a '
792 'different repository.' % options.revision)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000793
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000794 def findDepsFromNotAllowedHosts(self):
795 """Returns a list of depenecies from not allowed hosts.
796
797 If allowed_hosts is not set, allows all hosts and returns empty list.
798 """
799 if not self._allowed_hosts:
800 return []
801 bad_deps = []
802 for dep in self._dependencies:
szager@chromium.orgbd772dd2014-11-05 18:43:08 +0000803 # Don't enforce this for custom_deps.
804 if dep.name in self._custom_deps:
805 continue
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000806 if isinstance(dep.url, basestring):
807 parsed_url = urlparse.urlparse(dep.url)
808 if parsed_url.netloc and parsed_url.netloc not in self._allowed_hosts:
809 bad_deps.append(dep)
810 return bad_deps
811
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000812 # Arguments number differs from overridden method
813 # pylint: disable=W0221
maruel@chromium.org3742c842010-09-09 19:27:14 +0000814 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000815 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000816 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000817 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000818 if not self.should_process:
819 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000820 # When running runhooks, there's no need to consult the SCM.
821 # All known hooks are expected to run unconditionally regardless of working
822 # copy state, so skip the SCM status check.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000823 run_scm = command not in ('runhooks', 'recurse', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000824 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000825 file_list = [] if not options.nohooks else None
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000826 revision_override = revision_overrides.pop(self.name, None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000827 if run_scm and parsed_url:
828 if isinstance(parsed_url, self.FileImpl):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000829 # Special support for single-file checkout.
830 if not command in (None, 'cleanup', 'diff', 'pack', 'status'):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000831 # Sadly, pylint doesn't realize that parsed_url is of FileImpl.
832 # pylint: disable=E1103
833 options.revision = parsed_url.GetRevision()
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000834 self._used_scm = gclient_scm.SVNWrapper(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000835 parsed_url.GetPath(), self.root.root_dir, self.name,
836 out_cb=work_queue.out_cb)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000837 self._used_scm.RunCommand('updatesingle',
838 options, args + [parsed_url.GetFilename()], file_list)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000839 else:
maruel@chromium.org9e5317a2010-08-13 20:35:11 +0000840 # Create a shallow copy to mutate revision.
841 options = copy.copy(options)
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000842 options.revision = revision_override
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000843 self.maybeGetParentRevision(
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000844 command, options, parsed_url, self.parent)
845 self._used_revision = options.revision
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000846 self._used_scm = gclient_scm.CreateSCM(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000847 parsed_url, self.root.root_dir, self.name, self.outbuf,
848 out_cb=work_queue.out_cb)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000849 self._got_revision = self._used_scm.RunCommand(command, options, args,
850 file_list)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000851 if file_list:
852 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000853
854 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
855 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000856 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000857 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000858 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000859 continue
860 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000861 [self.root.root_dir.lower(), file_list[i].lower()])
862 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000863 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000864 while file_list[i].startswith(('\\', '/')):
865 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000866
867 # Always parse the DEPS file.
868 self.ParseDepsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000869 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000870 if command in ('update', 'revert') and not options.noprehooks:
871 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000872
873 if self.recursion_limit:
874 # Parse the dependencies of this dependency.
875 for s in self.dependencies:
876 work_queue.enqueue(s)
877
878 if command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000879 if not isinstance(parsed_url, self.FileImpl):
880 # Skip file only checkout.
881 scm = gclient_scm.GetScmName(parsed_url)
882 if not options.scm or scm in options.scm:
883 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000884 # Pass in the SCM type as an env variable. Make sure we don't put
885 # unicode strings in the environment.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000886 env = os.environ.copy()
887 if scm:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000888 env['GCLIENT_SCM'] = str(scm)
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000889 if parsed_url:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000890 env['GCLIENT_URL'] = str(parsed_url)
891 env['GCLIENT_DEP_PATH'] = str(self.name)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000892 if options.prepend_dir and scm == 'git':
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000893 print_stdout = False
894 def filter_fn(line):
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000895 """Git-specific path marshaling. It is optimized for git-grep."""
896
897 def mod_path(git_pathspec):
898 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
899 modified_path = os.path.join(self.name, match.group(2))
900 branch = match.group(1) or ''
901 return '%s%s' % (branch, modified_path)
902
903 match = re.match('^Binary file ([^\0]+) matches$', line)
904 if match:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000905 print('Binary file %s matches\n' % mod_path(match.group(1)))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000906 return
907
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000908 items = line.split('\0')
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000909 if len(items) == 2 and items[1]:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000910 print('%s : %s' % (mod_path(items[0]), items[1]))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000911 elif len(items) >= 2:
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000912 # Multiple null bytes or a single trailing null byte indicate
913 # git is likely displaying filenames only (such as with -l)
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000914 print('\n'.join(mod_path(path) for path in items if path))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000915 else:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000916 print(line)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000917 else:
918 print_stdout = True
919 filter_fn = None
920
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000921 if parsed_url is None:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000922 print('Skipped omitted dependency %s' % cwd, file=sys.stderr)
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000923 elif os.path.isdir(cwd):
maruel@chromium.org288054d2012-03-05 00:43:07 +0000924 try:
925 gclient_utils.CheckCallAndFilter(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000926 args, cwd=cwd, env=env, print_stdout=print_stdout,
927 filter_fn=filter_fn,
928 )
maruel@chromium.org288054d2012-03-05 00:43:07 +0000929 except subprocess2.CalledProcessError:
930 if not options.ignore:
931 raise
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000932 else:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000933 print('Skipped missing %s' % cwd, file=sys.stderr)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000934
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000935
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000936 @gclient_utils.lockedmethod
937 def _run_is_done(self, file_list, parsed_url):
938 # Both these are kept for hooks that are run as a separate tree traversal.
939 self._file_list = file_list
940 self._parsed_url = parsed_url
941 self._processed = True
942
szager@google.comb9a78d32012-03-13 18:46:21 +0000943 @staticmethod
944 def GetHookAction(hook_dict, matching_file_list):
945 """Turns a parsed 'hook' dict into an executable command."""
946 logging.debug(hook_dict)
947 logging.debug(matching_file_list)
948 command = hook_dict['action'][:]
949 if command[0] == 'python':
950 # If the hook specified "python" as the first item, the action is a
951 # Python script. Run it by starting a new copy of the same
952 # interpreter.
953 command[0] = sys.executable
954 if '$matching_files' in command:
955 splice_index = command.index('$matching_files')
956 command[splice_index:splice_index + 1] = matching_file_list
957 return command
958
959 def GetHooks(self, options):
960 """Evaluates all hooks, and return them in a flat list.
961
962 RunOnDeps() must have been called before to load the DEPS.
963 """
964 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000965 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000966 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000967 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000968 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000969 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000970 if self.deps_hooks:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000971 # TODO(maruel): If the user is using git or git-svn, then we don't know
972 # what files have changed so we always run all hooks. It'd be nice to fix
973 # that.
974 if (options.force or
975 isinstance(self.parsed_url, self.FileImpl) or
976 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000977 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000978 for hook_dict in self.deps_hooks:
szager@google.comb9a78d32012-03-13 18:46:21 +0000979 result.append(self.GetHookAction(hook_dict, []))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000980 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000981 # Run hooks on the basis of whether the files from the gclient operation
982 # match each hook's pattern.
983 for hook_dict in self.deps_hooks:
984 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000985 matching_file_list = [
986 f for f in self.file_list_and_children if pattern.search(f)
987 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000988 if matching_file_list:
szager@google.comb9a78d32012-03-13 18:46:21 +0000989 result.append(self.GetHookAction(hook_dict, matching_file_list))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000990 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000991 result.extend(s.GetHooks(options))
992 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000993
szager@google.comb9a78d32012-03-13 18:46:21 +0000994 def RunHooksRecursively(self, options):
995 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000996 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000997 for hook in self.GetHooks(options):
998 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000999 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +00001000 gclient_utils.CheckCallAndFilterAndHeader(
1001 hook, cwd=self.root.root_dir, always=True)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001002 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
szager@google.comb9a78d32012-03-13 18:46:21 +00001003 # Use a discrete exit status code of 2 to indicate that a hook action
1004 # failed. Users of this script may wish to treat hook action failures
1005 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001006 print('Error: %s' % str(e), file=sys.stderr)
szager@google.comb9a78d32012-03-13 18:46:21 +00001007 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +00001008 finally:
1009 elapsed_time = time.time() - start_time
1010 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001011 print("Hook '%s' took %.2f secs" % (
1012 gclient_utils.CommandToStr(hook), elapsed_time))
maruel@chromium.orgeaf61062010-07-07 18:42:39 +00001013
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001014 def RunPreDepsHooks(self):
1015 assert self.processed
1016 assert self.deps_parsed
1017 assert not self.pre_deps_hooks_ran
1018 assert not self.hooks_ran
1019 for s in self.dependencies:
1020 assert not s.processed
1021 self._pre_deps_hooks_ran = True
1022 for hook in self.pre_deps_hooks:
1023 try:
1024 start_time = time.time()
1025 gclient_utils.CheckCallAndFilterAndHeader(
1026 hook, cwd=self.root.root_dir, always=True)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001027 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001028 # Use a discrete exit status code of 2 to indicate that a hook action
1029 # failed. Users of this script may wish to treat hook action failures
1030 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001031 print('Error: %s' % str(e), file=sys.stderr)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001032 sys.exit(2)
1033 finally:
1034 elapsed_time = time.time() - start_time
1035 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001036 print("Hook '%s' took %.2f secs" % (
1037 gclient_utils.CommandToStr(hook), elapsed_time))
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001038
1039
maruel@chromium.org0d812442010-08-10 12:41:08 +00001040 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001041 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001042 dependencies = self.dependencies
1043 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001044 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001045 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001046 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001047 for i in d.subtree(include_all):
1048 yield i
1049
1050 def depth_first_tree(self):
1051 """Depth-first recursion including the root node."""
1052 yield self
1053 for i in self.dependencies:
1054 for j in i.depth_first_tree():
1055 if j.should_process:
1056 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +00001057
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001058 @gclient_utils.lockedmethod
1059 def add_dependency(self, new_dep):
1060 self._dependencies.append(new_dep)
1061
1062 @gclient_utils.lockedmethod
1063 def _mark_as_parsed(self, new_hooks):
1064 self._deps_hooks.extend(new_hooks)
1065 self._deps_parsed = True
1066
maruel@chromium.org68988972011-09-20 14:11:42 +00001067 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001068 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +00001069 def dependencies(self):
1070 return tuple(self._dependencies)
1071
1072 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001073 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001074 def deps_hooks(self):
1075 return tuple(self._deps_hooks)
1076
1077 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001078 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001079 def pre_deps_hooks(self):
1080 return tuple(self._pre_deps_hooks)
1081
1082 @property
1083 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001084 def parsed_url(self):
1085 return self._parsed_url
1086
1087 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001088 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001089 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +00001090 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +00001091 return self._deps_parsed
1092
1093 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001094 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001095 def processed(self):
1096 return self._processed
1097
1098 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001099 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001100 def pre_deps_hooks_ran(self):
1101 return self._pre_deps_hooks_ran
1102
1103 @property
1104 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001105 def hooks_ran(self):
1106 return self._hooks_ran
1107
1108 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001109 @gclient_utils.lockedmethod
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001110 def allowed_hosts(self):
1111 return self._allowed_hosts
1112
1113 @property
1114 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001115 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001116 return tuple(self._file_list)
1117
1118 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +00001119 def used_scm(self):
1120 """SCMWrapper instance for this dependency or None if not processed yet."""
1121 return self._used_scm
1122
1123 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001124 @gclient_utils.lockedmethod
1125 def got_revision(self):
1126 return self._got_revision
1127
1128 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001129 def file_list_and_children(self):
1130 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001131 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001132 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +00001133 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001134
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001135 def __str__(self):
1136 out = []
smutae7ea312016-07-18 11:59:41 -07001137 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001138 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001139 'processed', 'hooks_ran', 'deps_parsed', 'requirements',
1140 'allowed_hosts'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001141 # First try the native property if it exists.
1142 if hasattr(self, '_' + i):
1143 value = getattr(self, '_' + i, False)
1144 else:
1145 value = getattr(self, i, False)
1146 if value:
1147 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001148
1149 for d in self.dependencies:
1150 out.extend([' ' + x for x in str(d).splitlines()])
1151 out.append('')
1152 return '\n'.join(out)
1153
1154 def __repr__(self):
1155 return '%s: %s' % (self.name, self.url)
1156
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001157 def hierarchy(self):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +00001158 """Returns a human-readable hierarchical reference to a Dependency."""
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001159 out = '%s(%s)' % (self.name, self.url)
1160 i = self.parent
1161 while i and i.name:
1162 out = '%s(%s) -> %s' % (i.name, i.url, out)
1163 i = i.parent
1164 return out
1165
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001166
1167class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001168 """Object that represent a gclient checkout. A tree of Dependency(), one per
1169 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001170
1171 DEPS_OS_CHOICES = {
1172 "win32": "win",
1173 "win": "win",
1174 "cygwin": "win",
1175 "darwin": "mac",
1176 "mac": "mac",
1177 "unix": "unix",
1178 "linux": "unix",
1179 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +00001180 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +00001181 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001182 }
1183
1184 DEFAULT_CLIENT_FILE_TEXT = ("""\
1185solutions = [
smutae7ea312016-07-18 11:59:41 -07001186 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001187 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001188 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001189 "managed" : %(managed)s,
smutae7ea312016-07-18 11:59:41 -07001190 "custom_deps" : {
1191 },
1192 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001193 },
1194]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001195cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001196""")
1197
1198 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
smutae7ea312016-07-18 11:59:41 -07001199 { "name" : "%(solution_name)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001200 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001201 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001202 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001203 "custom_deps" : {
smutae7ea312016-07-18 11:59:41 -07001204%(solution_deps)s },
1205 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001206 },
1207""")
1208
1209 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1210# Snapshot generated with gclient revinfo --snapshot
1211solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001212%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001213""")
1214
1215 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001216 # Do not change previous behavior. Only solution level and immediate DEPS
1217 # are processed.
1218 self._recursion_limit = 2
smutae7ea312016-07-18 11:59:41 -07001219 Dependency.__init__(self, None, None, None, None, True, None, None, None,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001220 'unused', True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001221 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001222 if options.deps_os:
1223 enforced_os = options.deps_os.split(',')
1224 else:
1225 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1226 if 'all' in enforced_os:
1227 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001228 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001229 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001230 self.config_content = None
1231
borenet@google.com88d10082014-03-21 17:24:48 +00001232 def _CheckConfig(self):
1233 """Verify that the config matches the state of the existing checked-out
1234 solutions."""
1235 for dep in self.dependencies:
1236 if dep.managed and dep.url:
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001237 scm = gclient_scm.CreateSCM(
1238 dep.url, self.root_dir, dep.name, self.outbuf)
smut@google.comd33eab32014-07-07 19:35:18 +00001239 actual_url = scm.GetActualRemoteURL(self._options)
borenet@google.com4e9be262014-04-08 19:40:30 +00001240 if actual_url and not scm.DoesRemoteURLMatch(self._options):
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001241 mirror = scm.GetCacheMirror()
1242 if mirror:
1243 mirror_string = '%s (exists=%s)' % (mirror.mirror_path,
1244 mirror.exists())
1245 else:
1246 mirror_string = 'not used'
borenet@google.com0a427372014-04-02 19:12:13 +00001247 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001248Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001249is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001250
borenet@google.com97882362014-04-07 20:06:02 +00001251The .gclient file contains:
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001252URL: %(expected_url)s (%(expected_scm)s)
1253Cache mirror: %(mirror_string)s
borenet@google.com97882362014-04-07 20:06:02 +00001254
1255The local checkout in %(checkout_path)s reports:
1256%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001257
1258You should ensure that the URL listed in .gclient is correct and either change
1259it or fix the checkout. If you're managing your own git checkout in
smutae7ea312016-07-18 11:59:41 -07001260%(checkout_path)s but the URL in .gclient is for an svn repository, you probably
1261want to set 'managed': False in .gclient.
borenet@google.com88d10082014-03-21 17:24:48 +00001262''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1263 'expected_url': dep.url,
1264 'expected_scm': gclient_scm.GetScmName(dep.url),
levarum@chromium.org27a6f9a2016-05-28 00:21:49 +00001265 'mirror_string' : mirror_string,
borenet@google.com88d10082014-03-21 17:24:48 +00001266 'actual_url': actual_url,
1267 'actual_scm': gclient_scm.GetScmName(actual_url)})
1268
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001269 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001270 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001271 config_dict = {}
1272 self.config_content = content
1273 try:
1274 exec(content, config_dict)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001275 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001276 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001277
peter@chromium.org1efccc82012-04-27 16:34:38 +00001278 # Append any target OS that is not already being enforced to the tuple.
1279 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001280 if config_dict.get('target_os_only', False):
1281 self._enforced_os = tuple(set(target_os))
1282 else:
1283 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1284
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001285 cache_dir = config_dict.get('cache_dir')
1286 if cache_dir:
1287 cache_dir = os.path.join(self.root_dir, cache_dir)
1288 cache_dir = os.path.abspath(cache_dir)
szager@chromium.orgcaf5bef2014-08-24 18:56:32 +00001289 # If running on a bot, force break any stale git cache locks.
dnj@chromium.orgb682b3e2014-08-25 19:17:12 +00001290 if os.path.exists(cache_dir) and os.environ.get('CHROME_HEADLESS'):
szager@chromium.org4848fb62014-08-24 19:16:31 +00001291 subprocess2.check_call(['git', 'cache', 'unlock', '--cache-dir',
1292 cache_dir, '--force', '--all'])
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001293 gclient_scm.GitWrapper.cache_dir = cache_dir
1294 git_cache.Mirror.SetCachePath(cache_dir)
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001295
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001296 if not target_os and config_dict.get('target_os_only', False):
1297 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1298 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001299
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001300 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001301 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001302 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001303 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +00001304 self, s['name'], s['url'],
smutae7ea312016-07-18 11:59:41 -07001305 s.get('safesync_url', None),
1306 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001307 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001308 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001309 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001310 s.get('deps_file', 'DEPS'),
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001311 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001312 except KeyError:
1313 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1314 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001315 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1316 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001317
1318 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001319 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001320 self._options.config_filename),
1321 self.config_content)
1322
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001323 def MigrateConfigToGit(self, path, options):
1324 svn_url_re = re.compile('^(https?://src\.chromium\.org/svn|'
1325 'svn://svn\.chromium\.org/chrome)/'
1326 '(trunk|branches/[^/]+)/src')
1327 old_git_re = re.compile('^(https?://git\.chromium\.org|'
1328 'ssh://([a-zA-Z_][a-zA-Z0-9_-]*@)?'
1329 'gerrit\.chromium\.org(:2941[89])?)/'
1330 'chromium/src\.git')
1331 # Scan existing .gclient file for obsolete settings. It would be simpler
1332 # to traverse self.dependencies, but working with the AST allows the code to
1333 # dump an updated .gclient file that preserves the ordering of the original.
1334 a = ast.parse(self.config_content, options.config_filename, 'exec')
1335 modified = False
1336 solutions = [elem for elem in a.body if 'solutions' in
1337 [target.id for target in elem.targets]]
1338 if not solutions:
1339 return self
1340 solutions = solutions[-1]
1341 for solution in solutions.value.elts:
1342 # Check for obsolete URL's
1343 url_idx = ast_dict_index(solution, 'url')
1344 if url_idx == -1:
1345 continue
1346 url_val = solution.values[url_idx]
1347 if type(url_val) is not ast.Str:
1348 continue
1349 if (svn_url_re.match(url_val.s.strip())):
1350 raise gclient_utils.Error(
1351"""
1352The chromium code repository has migrated completely to git.
1353Your SVN-based checkout is now obsolete; you need to create a brand-new
1354git checkout by following these instructions:
1355
1356http://www.chromium.org/developers/how-tos/get-the-code
1357""")
1358 if (old_git_re.match(url_val.s.strip())):
1359 url_val.s = CHROMIUM_SRC_URL
1360 modified = True
1361
szager@chromium.org808bcfb2014-08-24 19:38:43 +00001362 # Ensure deps_file is set to .DEPS.git. We enforce this here to smooth
1363 # over switching between pre-git-migration and post-git-migration
1364 # revisions.
1365 # - For pre-migration revisions, .DEPS.git must be explicitly set.
1366 # - For post-migration revisions, .DEPS.git is not present, so gclient
1367 # will correctly fall back to DEPS.
1368 if url_val.s == CHROMIUM_SRC_URL:
1369 deps_file_idx = ast_dict_index(solution, 'deps_file')
1370 if deps_file_idx != -1:
1371 continue
1372 solution.keys.append(ast.Str('deps_file'))
1373 solution.values.append(ast.Str('.DEPS.git'))
1374 modified = True
1375
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001376 if not modified:
1377 return self
1378
1379 print(
1380"""
1381WARNING: gclient detected an obsolete setting in your %s file. The file has
1382been automagically updated. The previous version is available at %s.old.
1383""" % (options.config_filename, options.config_filename))
1384
1385 # Replace existing .gclient with the updated version.
1386 # Return a new GClient instance based on the new content.
1387 new_content = ast2str(a)
1388 dot_gclient_fn = os.path.join(path, options.config_filename)
iannucci@chromium.orgc7c41682014-08-23 03:44:18 +00001389 try:
1390 os.rename(dot_gclient_fn, dot_gclient_fn + '.old')
1391 except OSError:
1392 pass
1393 with open(dot_gclient_fn, 'w') as fh:
1394 fh.write(new_content)
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001395 client = GClient(path, options)
1396 client.SetConfig(new_content)
1397 return client
1398
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001399 @staticmethod
1400 def LoadCurrentConfig(options):
1401 """Searches for and loads a .gclient file relative to the current working
1402 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001403 if options.spec:
1404 client = GClient('.', options)
1405 client.SetConfig(options.spec)
1406 else:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001407 if options.verbose:
1408 print('Looking for %s starting from %s\n' % (
1409 options.config_filename, os.getcwd()))
szager@chromium.orge2e03202012-07-31 18:05:16 +00001410 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1411 if not path:
1412 return None
1413 client = GClient(path, options)
1414 client.SetConfig(gclient_utils.FileRead(
1415 os.path.join(path, options.config_filename)))
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001416 client = client.MigrateConfigToGit(path, options)
maruel@chromium.org69392e72011-10-13 22:09:00 +00001417
1418 if (options.revisions and
1419 len(client.dependencies) > 1 and
1420 any('@' not in r for r in options.revisions)):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001421 print(
1422 ('You must specify the full solution name like --revision %s@%s\n'
1423 'when you have multiple solutions setup in your .gclient file.\n'
1424 'Other solutions present are: %s.') % (
maruel@chromium.org69392e72011-10-13 22:09:00 +00001425 client.dependencies[0].name,
1426 options.revisions[0],
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001427 ', '.join(s.name for s in client.dependencies[1:])),
1428 file=sys.stderr)
maruel@chromium.org15804092010-09-02 17:07:37 +00001429 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001430
nsylvain@google.comefc80932011-05-31 21:27:56 +00001431 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
smutae7ea312016-07-18 11:59:41 -07001432 safesync_url, managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001433 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1434 'solution_name': solution_name,
1435 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001436 'deps_file': deps_file,
smutae7ea312016-07-18 11:59:41 -07001437 'safesync_url' : safesync_url,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001438 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001439 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001440 })
1441
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001442 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001443 """Creates a .gclient_entries file to record the list of unique checkouts.
1444
1445 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001446 """
1447 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1448 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001449 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001450 for entry in self.root.subtree(False):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001451 # Skip over File() dependencies as we can't version them.
1452 if not isinstance(entry.parsed_url, self.FileImpl):
1453 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1454 pprint.pformat(entry.parsed_url))
1455 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001456 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001457 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001458 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001459
1460 def _ReadEntries(self):
1461 """Read the .gclient_entries file for the given client.
1462
1463 Returns:
1464 A sequence of solution names, which will be empty if there is the
1465 entries file hasn't been created yet.
1466 """
1467 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001468 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001469 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001470 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001471 try:
1472 exec(gclient_utils.FileRead(filename), scope)
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00001473 except SyntaxError as e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001474 gclient_utils.SyntaxErrorToError(filename, e)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001475 return scope['entries']
1476
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001477 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001478 """Checks for revision overrides."""
1479 revision_overrides = {}
smutae7ea312016-07-18 11:59:41 -07001480 if self._options.head:
1481 return revision_overrides
1482 # Do not check safesync_url if one or more --revision flag is specified.
joi@chromium.org792ea882010-11-10 02:37:27 +00001483 if not self._options.revisions:
1484 for s in self.dependencies:
smutae7ea312016-07-18 11:59:41 -07001485 if not s.managed:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001486 self._options.revisions.append('%s@unmanaged' % s.name)
smutae7ea312016-07-18 11:59:41 -07001487 elif s.safesync_url:
1488 self._ApplySafeSyncRev(dep=s)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001489 if not self._options.revisions:
1490 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001491 solutions_names = [s.name for s in self.dependencies]
smutae7ea312016-07-18 11:59:41 -07001492 index = 0
1493 for revision in self._options.revisions:
1494 if not '@' in revision:
maruel@chromium.org307d1792010-05-31 20:03:13 +00001495 # Support for --revision 123
smutae7ea312016-07-18 11:59:41 -07001496 revision = '%s@%s' % (solutions_names[index], revision)
1497 name, rev = revision.split('@', 1)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001498 revision_overrides[name] = rev
smutae7ea312016-07-18 11:59:41 -07001499 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001500 return revision_overrides
1501
smutae7ea312016-07-18 11:59:41 -07001502 def _ApplySafeSyncRev(self, dep):
1503 """Finds a valid revision from the content of the safesync_url and apply it
1504 by appending revisions to the revision list. Throws if revision appears to
1505 be invalid for the given |dep|."""
1506 assert len(dep.safesync_url) > 0
1507 handle = urllib.urlopen(dep.safesync_url)
1508 rev = handle.read().strip()
1509 handle.close()
1510 if not rev:
1511 raise gclient_utils.Error(
1512 'It appears your safesync_url (%s) is not working properly\n'
1513 '(as it returned an empty response). Check your config.' %
1514 dep.safesync_url)
1515 scm = gclient_scm.CreateSCM(
1516 dep.url, dep.root.root_dir, dep.name, self.outbuf)
1517 safe_rev = scm.GetUsableRev(rev, self._options)
1518 if self._options.verbose:
1519 print('Using safesync_url revision: %s.\n' % safe_rev)
1520 self._options.revisions.append('%s@%s' % (dep.name, safe_rev))
1521
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001522 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001523 """Runs a command on each dependency in a client and its dependencies.
1524
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001525 Args:
1526 command: The command to use (e.g., 'status' or 'diff')
1527 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001528 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001529 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001530 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001531
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001532 revision_overrides = {}
1533 # It's unnecessary to check for revision overrides for 'recurse'.
1534 # Save a few seconds by not calling _EnforceRevisions() in that case.
agable@chromium.org0242eb42015-06-09 00:45:31 +00001535 if command not in ('diff', 'recurse', 'runhooks', 'status', 'revert'):
szager@chromium.org5273b8a2014-08-21 15:10:10 +00001536 self._CheckConfig()
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001537 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001538 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001539 # Disable progress for non-tty stdout.
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00001540 if (setup_color.IS_TTY and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001541 if command in ('update', 'revert'):
1542 pm = Progress('Syncing projects', 1)
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +00001543 elif command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001544 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001545 work_queue = gclient_utils.ExecutionQueue(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001546 self._options.jobs, pm, ignore_requirements=ignore_requirements,
1547 verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001548 for s in self.dependencies:
1549 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001550 work_queue.flush(revision_overrides, command, args, options=self._options)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001551 if revision_overrides:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001552 print('Please fix your script, having invalid --revision flags will soon '
1553 'considered an error.', file=sys.stderr)
piman@chromium.org6f363722010-04-27 00:41:09 +00001554
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001555 # Once all the dependencies have been processed, it's now safe to run the
1556 # hooks.
1557 if not self._options.nohooks:
1558 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001559
1560 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001561 # Notify the user if there is an orphaned entry in their working copy.
1562 # Only delete the directory if there are no changes in it, and
1563 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001564 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001565 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1566 for e in entries]
1567
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001568 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001569 if not prev_url:
1570 # entry must have been overridden via .gclient custom_deps
1571 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001572 # Fix path separator on Windows.
1573 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001574 e_dir = os.path.join(self.root_dir, entry_fixed)
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001575 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001576 if (entry not in entries and
1577 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001578 os.path.exists(e_dir)):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001579 # The entry has been removed from DEPS.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001580 scm = gclient_scm.CreateSCM(
1581 prev_url, self.root_dir, entry_fixed, self.outbuf)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001582
1583 # Check to see if this directory is now part of a higher-up checkout.
borenet@google.com359bb642014-05-13 17:28:19 +00001584 # The directory might be part of a git OR svn checkout.
1585 scm_root = None
primiano@chromium.org1c127382015-02-17 11:15:40 +00001586 scm_class = None
borenet@google.com359bb642014-05-13 17:28:19 +00001587 for scm_class in (gclient_scm.scm.GIT, gclient_scm.scm.SVN):
1588 try:
1589 scm_root = scm_class.GetCheckoutRoot(scm.checkout_path)
1590 except subprocess2.CalledProcessError:
1591 pass
1592 if scm_root:
1593 break
1594 else:
1595 logging.warning('Could not find checkout root for %s. Unable to '
1596 'determine whether it is part of a higher-level '
1597 'checkout, so not removing.' % entry)
1598 continue
primiano@chromium.org1c127382015-02-17 11:15:40 +00001599
1600 # This is to handle the case of third_party/WebKit migrating from
1601 # being a DEPS entry to being part of the main project.
1602 # If the subproject is a Git project, we need to remove its .git
1603 # folder. Otherwise git operations on that folder will have different
1604 # effects depending on the current working directory.
1605 if scm_class == gclient_scm.scm.GIT and (
1606 os.path.abspath(scm_root) == os.path.abspath(e_dir)):
1607 e_par_dir = os.path.join(e_dir, os.pardir)
1608 if scm_class.IsInsideWorkTree(e_par_dir):
1609 par_scm_root = scm_class.GetCheckoutRoot(e_par_dir)
1610 # rel_e_dir : relative path of entry w.r.t. its parent repo.
1611 rel_e_dir = os.path.relpath(e_dir, par_scm_root)
1612 if scm_class.IsDirectoryVersioned(par_scm_root, rel_e_dir):
1613 save_dir = scm.GetGitBackupDirPath()
1614 # Remove any eventual stale backup dir for the same project.
1615 if os.path.exists(save_dir):
1616 gclient_utils.rmtree(save_dir)
1617 os.rename(os.path.join(e_dir, '.git'), save_dir)
1618 # When switching between the two states (entry/ is a subproject
1619 # -> entry/ is part of the outer project), it is very likely
1620 # that some files are changed in the checkout, unless we are
1621 # jumping *exactly* across the commit which changed just DEPS.
1622 # In such case we want to cleanup any eventual stale files
1623 # (coming from the old subproject) in order to end up with a
1624 # clean checkout.
1625 scm_class.CleanupDir(par_scm_root, rel_e_dir)
1626 assert not os.path.exists(os.path.join(e_dir, '.git'))
1627 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher '
1628 'level checkout. The git folder containing all the local'
1629 ' branches has been saved to %s.\n'
1630 'If you don\'t care about its state you can safely '
1631 'remove that folder to free up space.') %
1632 (entry, save_dir))
1633 continue
1634
borenet@google.com359bb642014-05-13 17:28:19 +00001635 if scm_root in full_entries:
primiano@chromium.org1c127382015-02-17 11:15:40 +00001636 logging.info('%s is part of a higher level checkout, not removing',
1637 scm.GetCheckoutRoot())
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001638 continue
1639
1640 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001641 scm.status(self._options, [], file_list)
1642 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001643 if (not self._options.delete_unversioned_trees or
1644 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001645 # There are modified files in this entry. Keep warning until
1646 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001647 print(('\nWARNING: \'%s\' is no longer part of this client. '
1648 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001649 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001650 else:
1651 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001652 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001653 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001654 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001655 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001656 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001657 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001658
1659 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001660 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001661 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001662 # Load all the settings.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001663 work_queue = gclient_utils.ExecutionQueue(
1664 self._options.jobs, None, False, verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001665 for s in self.dependencies:
1666 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001667 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001668
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001669 def GetURLAndRev(dep):
1670 """Returns the revision-qualified SCM url for a Dependency."""
1671 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001672 return None
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001673 if isinstance(dep.parsed_url, self.FileImpl):
1674 original_url = dep.parsed_url.file_location
1675 else:
1676 original_url = dep.parsed_url
nasser@codeaurora.org5d63eb82010-03-24 23:22:09 +00001677 url, _ = gclient_utils.SplitUrlRevision(original_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001678 scm = gclient_scm.CreateSCM(
1679 original_url, self.root_dir, dep.name, self.outbuf)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001680 if not os.path.isdir(scm.checkout_path):
1681 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001682 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001683
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001684 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001685 new_gclient = ''
1686 # First level at .gclient
1687 for d in self.dependencies:
1688 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001689 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001690 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001691 for d in dep.dependencies:
1692 entries[d.name] = GetURLAndRev(d)
1693 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001694 GrabDeps(d)
1695 custom_deps = []
1696 for k in sorted(entries.keys()):
1697 if entries[k]:
1698 # Quotes aren't escaped...
1699 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1700 else:
1701 custom_deps.append(' \"%s\": None,\n' % k)
1702 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1703 'solution_name': d.name,
1704 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001705 'deps_file': d.deps_file,
smutae7ea312016-07-18 11:59:41 -07001706 'safesync_url' : d.safesync_url or '',
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001707 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001708 'solution_deps': ''.join(custom_deps),
1709 }
1710 # Print the snapshot configuration file
1711 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001712 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001713 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001714 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001715 if self._options.actual:
1716 entries[d.name] = GetURLAndRev(d)
1717 else:
1718 entries[d.name] = d.parsed_url
1719 keys = sorted(entries.keys())
1720 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001721 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001722 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001723
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001724 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001725 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001726 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001727
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001728 def PrintLocationAndContents(self):
1729 # Print out the .gclient file. This is longer than if we just printed the
1730 # client dict, but more legible, and it might contain helpful comments.
1731 print('Loaded .gclient config in %s:\n%s' % (
1732 self.root_dir, self.config_content))
1733
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001734 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001735 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001736 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001737 return self._root_dir
1738
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001739 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001740 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001741 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001742 return self._enforced_os
1743
maruel@chromium.org68988972011-09-20 14:11:42 +00001744 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001745 def recursion_limit(self):
1746 """How recursive can each dependencies in DEPS file can load DEPS file."""
1747 return self._recursion_limit
1748
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001749 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +00001750 def try_recursedeps(self):
1751 """Whether to attempt using recursedeps-style recursion processing."""
cmp@chromium.orge84ac912014-06-30 23:14:35 +00001752 return True
1753
1754 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001755 def target_os(self):
1756 return self._enforced_os
1757
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001758
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001759#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001760
1761
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001762def CMDcleanup(parser, args):
1763 """Cleans up all working copies.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001764
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001765 Mostly svn-specific. Simply runs 'svn cleanup' for each module.
1766 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001767 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1768 help='override deps for the specified (comma-separated) '
1769 'platform(s); \'all\' will process all deps_os '
1770 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001771 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001772 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001773 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001774 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001775 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001776 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001777 return client.RunOnDeps('cleanup', args)
1778
1779
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001780@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001781def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001782 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001783
1784 Runs a shell command on all entries.
ilevy@chromium.org37116242012-11-28 01:32:48 +00001785 Sets GCLIENT_DEP_PATH enviroment variable as the dep's relative location to
1786 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001787 """
1788 # Stop parsing at the first non-arg so that these go through to the command
1789 parser.disable_interspersed_args()
1790 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001791 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001792 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001793 help='Ignore non-zero return codes from subcommands.')
1794 parser.add_option('--prepend-dir', action='store_true',
1795 help='Prepend relative dir for use with git <cmd> --null.')
1796 parser.add_option('--no-progress', action='store_true',
1797 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001798 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001799 if not args:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001800 print('Need to supply a command!', file=sys.stderr)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001801 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001802 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1803 if not root_and_entries:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001804 print(
maruel@chromium.org78cba522010-10-18 13:32:05 +00001805 'You need to run gclient sync at least once to use \'recurse\'.\n'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001806 'This is because .gclient_entries needs to exist and be up to date.',
1807 file=sys.stderr)
maruel@chromium.org78cba522010-10-18 13:32:05 +00001808 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001809
1810 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001811 scm_set = set()
1812 for scm in options.scm:
1813 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001814 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001815
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001816 options.nohooks = True
1817 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001818 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1819 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001820
1821
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001822@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001823def CMDfetch(parser, args):
1824 """Fetches upstream commits for all modules.
1825
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001826 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1827 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001828 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001829 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001830 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1831
1832
1833def CMDgrep(parser, args):
1834 """Greps through git repos managed by gclient.
1835
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001836 Runs 'git grep [args...]' for each module.
1837 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001838 # We can't use optparse because it will try to parse arguments sent
1839 # to git grep and throw an error. :-(
1840 if not args or re.match('(-h|--help)$', args[0]):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001841 print(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001842 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1843 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1844 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1845 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001846 ' end of your query.',
1847 file=sys.stderr)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001848 return 1
1849
1850 jobs_arg = ['--jobs=1']
1851 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1852 jobs_arg, args = args[:1], args[1:]
1853 elif re.match(r'(-j|--jobs)$', args[0]):
1854 jobs_arg, args = args[:2], args[2:]
1855
1856 return CMDrecurse(
1857 parser,
1858 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1859 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001860
1861
stip@chromium.orga735da22015-04-29 23:18:20 +00001862def CMDroot(parser, args):
1863 """Outputs the solution root (or current dir if there isn't one)."""
1864 (options, args) = parser.parse_args(args)
1865 client = GClient.LoadCurrentConfig(options)
1866 if client:
1867 print(os.path.abspath(client.root_dir))
1868 else:
1869 print(os.path.abspath('.'))
1870
1871
smutae7ea312016-07-18 11:59:41 -07001872@subcommand.usage('[url] [safesync url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001873def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001874 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001875
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001876 This specifies the configuration for further commands. After update/sync,
1877 top-level DEPS files in each module are read to determine dependent
1878 modules to operate on as well. If optional [url] parameter is
1879 provided, then configuration is read from a specified Subversion server
1880 URL.
1881 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00001882 # We do a little dance with the --gclientfile option. 'gclient config' is the
1883 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1884 # arguments. So, we temporarily stash any --gclientfile parameter into
1885 # options.output_config_file until after the (gclientfile xor spec) error
1886 # check.
1887 parser.remove_option('--gclientfile')
1888 parser.add_option('--gclientfile', dest='output_config_file',
1889 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001890 parser.add_option('--name',
1891 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001892 parser.add_option('--deps-file', default='DEPS',
1893 help='overrides the default name for the DEPS file for the'
1894 'main solutions and all sub-dependencies')
smutae7ea312016-07-18 11:59:41 -07001895 parser.add_option('--unmanaged', action='store_true', default=False,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001896 help='overrides the default behavior to make it possible '
smutae7ea312016-07-18 11:59:41 -07001897 'to have the main solution untouched by gclient '
1898 '(gclient will check out unmanaged dependencies but '
1899 'will never sync them)')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001900 parser.add_option('--cache-dir',
1901 help='(git only) Cache all git repos into this dir and do '
1902 'shared clones from the cache, instead of cloning '
1903 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001904 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001905 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001906 if options.output_config_file:
1907 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001908 if ((options.spec and args) or len(args) > 2 or
1909 (not options.spec and not args)):
1910 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1911
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001912 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001913 if options.spec:
1914 client.SetConfig(options.spec)
1915 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001916 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001917 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001918 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001919 if name.endswith('.git'):
1920 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001921 else:
1922 # specify an alternate relpath for the given URL.
1923 name = options.name
agable@chromium.orgf2214672015-10-27 21:02:48 +00001924 if not os.path.abspath(os.path.join(os.getcwd(), name)).startswith(
1925 os.getcwd()):
1926 parser.error('Do not pass a relative path for --name.')
1927 if any(x in ('..', '.', '/', '\\') for x in name.split(os.sep)):
1928 parser.error('Do not include relative path components in --name.')
1929
nsylvain@google.comefc80932011-05-31 21:27:56 +00001930 deps_file = options.deps_file
smutae7ea312016-07-18 11:59:41 -07001931 safesync_url = ''
1932 if len(args) > 1:
1933 safesync_url = args[1]
1934 client.SetDefaultConfig(name, deps_file, base_url, safesync_url,
1935 managed=not options.unmanaged,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001936 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001937 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001938 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001939
1940
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001941@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001942 gclient pack > patch.txt
1943 generate simple patch for configured client and dependences
1944""")
1945def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001946 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001947
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001948 Internally, runs 'svn diff'/'git diff' on each checked out module and
1949 dependencies, and performs minimal postprocessing of the output. The
1950 resulting patch is printed to stdout and can be applied to a freshly
1951 checked out tree via 'patch -p0 < patchfile'.
1952 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001953 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1954 help='override deps for the specified (comma-separated) '
1955 'platform(s); \'all\' will process all deps_os '
1956 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001957 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001958 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001959 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001960 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001961 client = GClient.LoadCurrentConfig(options)
1962 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001963 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001964 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001965 client.PrintLocationAndContents()
kbr@google.comab318592009-09-04 00:54:55 +00001966 return client.RunOnDeps('pack', args)
1967
1968
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001969def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001970 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001971 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1972 help='override deps for the specified (comma-separated) '
1973 'platform(s); \'all\' will process all deps_os '
1974 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001975 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001976 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001977 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001978 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001979 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00001980 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001981 return client.RunOnDeps('status', args)
1982
1983
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001984@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001985 gclient sync
1986 update files from SCM according to current configuration,
1987 *for modules which have changed since last update or sync*
1988 gclient sync --force
1989 update files from SCM according to current configuration, for
1990 all modules (useful for recovering files deleted from local copy)
1991 gclient sync --revision src@31000
1992 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001993
1994JSON output format:
1995If the --output-json option is specified, the following document structure will
1996be emitted to the provided file. 'null' entries may occur for subprojects which
1997are present in the gclient solution, but were not processed (due to custom_deps,
1998os_deps, etc.)
1999
2000{
2001 "solutions" : {
2002 "<name>": { # <name> is the posix-normalized path to the solution.
2003 "revision": [<svn rev int>|<git id hex string>|null],
2004 "scm": ["svn"|"git"|null],
2005 }
2006 }
2007}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002008""")
2009def CMDsync(parser, args):
2010 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002011 parser.add_option('-f', '--force', action='store_true',
2012 help='force update even for unchanged modules')
2013 parser.add_option('-n', '--nohooks', action='store_true',
2014 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002015 parser.add_option('-p', '--noprehooks', action='store_true',
2016 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002017 parser.add_option('-r', '--revision', action='append',
2018 dest='revisions', metavar='REV', default=[],
2019 help='Enforces revision/hash for the solutions with the '
2020 'format src@rev. The src@ part is optional and can be '
2021 'skipped. -r can be used multiple times when .gclient '
2022 'has multiple solutions configured and will work even '
smutae7ea312016-07-18 11:59:41 -07002023 'if the src@ part is skipped. Note that specifying '
2024 '--revision means your safesync_url gets ignored.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00002025 parser.add_option('--with_branch_heads', action='store_true',
2026 help='Clone git "branch_heads" refspecs in addition to '
2027 'the default refspecs. This adds about 1/2GB to a '
2028 'full checkout. (git only)')
szager@chromium.org8d3348f2014-08-19 22:49:16 +00002029 parser.add_option('--with_tags', action='store_true',
2030 help='Clone git tags in addition to the default refspecs.')
floitsch@google.comeaab7842011-04-28 09:07:58 +00002031 parser.add_option('-t', '--transitive', action='store_true',
2032 help='When a revision is specified (in the DEPS file or '
2033 'with the command-line flag), transitively update '
2034 'the dependencies to the date of the given revision. '
2035 'Only supported for SVN repositories.')
agable2697cd12016-06-28 10:23:53 -07002036 parser.add_option('-H', '--head', action='store_true',
smutae7ea312016-07-18 11:59:41 -07002037 help='skips any safesync_urls specified in '
2038 'configured solutions and sync to head instead')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002039 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002040 help='Deletes from the working copy any dependencies that '
2041 'have been removed since the last sync, as long as '
2042 'there are no local modifications. When used with '
2043 '--force, such dependencies are removed even if they '
2044 'have local modifications. When used with --reset, '
2045 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002046 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002047 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002048 parser.add_option('-R', '--reset', action='store_true',
2049 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00002050 parser.add_option('-M', '--merge', action='store_true',
2051 help='merge upstream changes instead of trying to '
2052 'fast-forward or rebase')
dnj@chromium.org5b23e872015-02-20 21:25:57 +00002053 parser.add_option('-A', '--auto_rebase', action='store_true',
2054 help='Automatically rebase repositories against local '
2055 'checkout during update (git only).')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002056 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2057 help='override deps for the specified (comma-separated) '
2058 'platform(s); \'all\' will process all deps_os '
2059 'references')
2060 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
2061 help='Skip svn up whenever possible by requesting '
2062 'actual HEAD revision from the repository')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002063 parser.add_option('--upstream', action='store_true',
2064 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002065 parser.add_option('--output-json',
2066 help='Output a json document to this path containing '
2067 'summary information about the sync.')
primiano@chromium.org5439ea52014-08-06 17:18:18 +00002068 parser.add_option('--no-history', action='store_true',
2069 help='GIT ONLY - Reduces the size/time of the checkout at '
2070 'the cost of no history. Requires Git 1.9+')
hinoka@chromium.org46b87412014-05-15 00:42:05 +00002071 parser.add_option('--shallow', action='store_true',
2072 help='GIT ONLY - Do a shallow clone into the cache dir. '
2073 'Requires Git 1.9+')
e.hakkinen@samsung.come8bc1aa2015-04-08 08:00:37 +00002074 parser.add_option('--no_bootstrap', '--no-bootstrap',
2075 action='store_true',
2076 help='Don\'t bootstrap from Google Storage.')
hinoka@chromium.org8a10f6d2014-06-23 18:38:57 +00002077 parser.add_option('--ignore_locks', action='store_true',
2078 help='GIT ONLY - Ignore cache locks.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +00002079 parser.add_option('--break_repo_locks', action='store_true',
2080 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2081 'index.lock). This should only be used if you know for '
2082 'certain that this invocation of gclient is the only '
2083 'thing operating on the git repos (e.g. on a bot).')
nodir@chromium.org5b48e482016-03-18 20:27:54 +00002084 parser.add_option('--lock_timeout', type='int', default=5000,
szager@chromium.orgdbb6f822016-02-02 22:59:30 +00002085 help='GIT ONLY - Deadline (in seconds) to wait for git '
nodir@chromium.org5b48e482016-03-18 20:27:54 +00002086 'cache lock to become available. Default is %default.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002087 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002088 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002089
2090 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002091 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002092
smutae7ea312016-07-18 11:59:41 -07002093 if options.revisions and options.head:
2094 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
2095 print('Warning: you cannot use both --head and --revision')
2096
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002097 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002098 client.PrintLocationAndContents()
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002099 ret = client.RunOnDeps('update', args)
2100 if options.output_json:
2101 slns = {}
2102 for d in client.subtree(True):
2103 normed = d.name.replace('\\', '/').rstrip('/') + '/'
2104 slns[normed] = {
2105 'revision': d.got_revision,
2106 'scm': d.used_scm.name if d.used_scm else None,
hinoka@chromium.org17db9052014-05-10 01:11:29 +00002107 'url': str(d.url) if d.url else None,
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002108 }
2109 with open(options.output_json, 'wb') as f:
2110 json.dump({'solutions': slns}, f)
2111 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002112
2113
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002114CMDupdate = CMDsync
2115
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002116
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002117def CMDdiff(parser, args):
2118 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002119 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2120 help='override deps for the specified (comma-separated) '
2121 'platform(s); \'all\' will process all deps_os '
2122 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002123 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002124 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002125 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002126 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002127 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002128 client.PrintLocationAndContents()
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002129 return client.RunOnDeps('diff', args)
2130
2131
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002132def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002133 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00002134
2135 That's the nuclear option to get back to a 'clean' state. It removes anything
2136 that shows up in svn status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002137 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2138 help='override deps for the specified (comma-separated) '
2139 'platform(s); \'all\' will process all deps_os '
2140 'references')
2141 parser.add_option('-n', '--nohooks', action='store_true',
2142 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002143 parser.add_option('-p', '--noprehooks', action='store_true',
2144 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002145 parser.add_option('--upstream', action='store_true',
2146 help='Make repo state match upstream branch.')
iannucci@chromium.orgbf525dc2016-04-07 22:00:28 +00002147 parser.add_option('--break_repo_locks', action='store_true',
2148 help='GIT ONLY - Forcibly remove repo locks (e.g. '
2149 'index.lock). This should only be used if you know for '
2150 'certain that this invocation of gclient is the only '
2151 'thing operating on the git repos (e.g. on a bot).')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002152 (options, args) = parser.parse_args(args)
2153 # --force is implied.
2154 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002155 options.reset = False
2156 options.delete_unversioned_trees = False
agablec903d732016-07-26 09:07:24 -07002157 options.merge = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002158 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002159 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002160 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002161 return client.RunOnDeps('revert', args)
2162
2163
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002164def CMDrunhooks(parser, args):
2165 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002166 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2167 help='override deps for the specified (comma-separated) '
2168 'platform(s); \'all\' will process all deps_os '
2169 'references')
2170 parser.add_option('-f', '--force', action='store_true', default=True,
2171 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002172 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002173 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002174 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002175 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002176 if options.verbose:
sergiyb@chromium.orgfa2707e2016-03-12 00:40:56 +00002177 client.PrintLocationAndContents()
maruel@chromium.org5df6a462009-08-28 18:52:26 +00002178 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002179 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002180 return client.RunOnDeps('runhooks', args)
2181
2182
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002183def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002184 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002185
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002186 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002187 can be used to reproduce the same tree in the future. It is only useful for
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002188 'unpinned dependencies', i.e. DEPS/deps references without a svn revision
2189 number or a git hash. A git branch name isn't 'pinned' since the actual
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002190 commit can change.
2191 """
2192 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2193 help='override deps for the specified (comma-separated) '
2194 'platform(s); \'all\' will process all deps_os '
2195 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002196 parser.add_option('-a', '--actual', action='store_true',
2197 help='gets the actual checked out revisions instead of the '
2198 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002199 parser.add_option('-s', '--snapshot', action='store_true',
2200 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002201 'version of all repositories to reproduce the tree, '
2202 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002203 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002204 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002205 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002206 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002207 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002208 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002209
2210
szager@google.comb9a78d32012-03-13 18:46:21 +00002211def CMDhookinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002212 """Outputs the hooks that would be run by `gclient runhooks`."""
szager@google.comb9a78d32012-03-13 18:46:21 +00002213 (options, args) = parser.parse_args(args)
2214 options.force = True
2215 client = GClient.LoadCurrentConfig(options)
2216 if not client:
2217 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2218 client.RunOnDeps(None, [])
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002219 print('; '.join(' '.join(hook) for hook in client.GetHooks(options)))
szager@google.comb9a78d32012-03-13 18:46:21 +00002220 return 0
2221
2222
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002223def CMDverify(parser, args):
2224 """Verifies the DEPS file deps are only from allowed_hosts."""
2225 (options, args) = parser.parse_args(args)
2226 client = GClient.LoadCurrentConfig(options)
2227 if not client:
2228 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2229 client.RunOnDeps(None, [])
2230 # Look at each first-level dependency of this gclient only.
2231 for dep in client.dependencies:
2232 bad_deps = dep.findDepsFromNotAllowedHosts()
2233 if not bad_deps:
2234 continue
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002235 print("There are deps from not allowed hosts in file %s" % dep.deps_file)
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002236 for bad_dep in bad_deps:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002237 print("\t%s at %s" % (bad_dep.name, bad_dep.url))
2238 print("allowed_hosts:", ', '.join(dep.allowed_hosts))
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002239 sys.stdout.flush()
2240 raise gclient_utils.Error(
2241 'dependencies from disallowed hosts; check your DEPS file.')
2242 return 0
2243
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002244class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00002245 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002246
2247 def __init__(self, **kwargs):
2248 optparse.OptionParser.__init__(
2249 self, version='%prog ' + __version__, **kwargs)
2250
2251 # Some arm boards have issues with parallel sync.
2252 if platform.machine().startswith('arm'):
2253 jobs = 1
2254 else:
2255 jobs = max(8, gclient_utils.NumLocalCpus())
2256 # cmp: 2013/06/19
2257 # Temporary workaround to lower bot-load on SVN server.
hinoka@google.com267f33e2014-02-28 22:02:32 +00002258 # Bypassed if a bot_update flag is detected.
2259 if (os.environ.get('CHROME_HEADLESS') == '1' and
2260 not os.path.exists('update.flag')):
xusydoc@chromium.org05028412013-07-29 13:40:10 +00002261 jobs = 1
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002262
2263 self.add_option(
2264 '-j', '--jobs', default=jobs, type='int',
2265 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002266 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002267 self.add_option(
2268 '-v', '--verbose', action='count', default=0,
2269 help='Produces additional output for diagnostics. Can be used up to '
2270 'three times for more logging info.')
2271 self.add_option(
2272 '--gclientfile', dest='config_filename',
2273 help='Specify an alternate %s file' % self.gclientfile_default)
2274 self.add_option(
2275 '--spec',
2276 help='create a gclient file containing the provided string. Due to '
2277 'Cygwin/Python brokenness, it can\'t contain any newlines.')
2278 self.add_option(
2279 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00002280 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002281
2282 def parse_args(self, args=None, values=None):
2283 """Integrates standard options processing."""
2284 options, args = optparse.OptionParser.parse_args(self, args, values)
2285 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
2286 logging.basicConfig(
2287 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00002288 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002289 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002290 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00002291 if (options.config_filename and
2292 options.config_filename != os.path.basename(options.config_filename)):
2293 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002294 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002295 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00002296 options.entries_filename = options.config_filename + '_entries'
2297 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002298 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00002299
2300 # These hacks need to die.
2301 if not hasattr(options, 'revisions'):
2302 # GClient.RunOnDeps expects it even if not applicable.
2303 options.revisions = []
smutae7ea312016-07-18 11:59:41 -07002304 if not hasattr(options, 'head'):
2305 options.head = None
maruel@chromium.org0895b752011-08-26 20:40:33 +00002306 if not hasattr(options, 'nohooks'):
2307 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002308 if not hasattr(options, 'noprehooks'):
2309 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00002310 if not hasattr(options, 'deps_os'):
2311 options.deps_os = None
2312 if not hasattr(options, 'manually_grab_svn_rev'):
2313 options.manually_grab_svn_rev = None
2314 if not hasattr(options, 'force'):
2315 options.force = None
2316 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002317
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002318
2319def disable_buffering():
2320 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
2321 # operations. Python as a strong tendency to buffer sys.stdout.
2322 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
2323 # Make stdout annotated with the thread ids.
2324 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00002325
2326
sbc@chromium.org013731e2015-02-26 18:28:43 +00002327def main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002328 """Doesn't parse the arguments here, just find the right subcommand to
2329 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002330 if sys.hexversion < 0x02060000:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002331 print(
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002332 '\nYour python version %s is unsupported, please upgrade.\n' %
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002333 sys.version.split(' ', 1)[0],
2334 file=sys.stderr)
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002335 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002336 if not sys.executable:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002337 print(
2338 '\nPython cannot find the location of it\'s own executable.\n',
2339 file=sys.stderr)
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002340 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002341 fix_encoding.fix_encoding()
2342 disable_buffering()
iannucci@chromium.org596cd5c2016-04-04 21:34:39 +00002343 setup_color.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002344 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002345 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002346 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00002347 except KeyboardInterrupt:
2348 gclient_utils.GClientChildren.KillAllRemainingChildren()
2349 raise
vapier@chromium.orga81a56e2015-11-11 07:56:13 +00002350 except (gclient_utils.Error, subprocess2.CalledProcessError) as e:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002351 print('Error: %s' % str(e), file=sys.stderr)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002352 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00002353 finally:
2354 gclient_utils.PrintWarnings()
sbc@chromium.org013731e2015-02-26 18:28:43 +00002355 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002356
2357
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00002358if '__main__' == __name__:
sbc@chromium.org013731e2015-02-26 18:28:43 +00002359 try:
2360 sys.exit(main(sys.argv[1:]))
2361 except KeyboardInterrupt:
2362 sys.stderr.write('interrupted\n')
2363 sys.exit(1)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002364
2365# vim: ts=2:sw=2:tw=80:et: