blob: f258890518b830655ec3a191134fcfe82d50bb2b [file] [log] [blame]
maruel@chromium.org725f1c32011-04-01 20:24:54 +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.orgcb2985f2010-11-03 14:08:31 +000098import breakpad # pylint: disable=W0611
maruel@chromium.orgada4c652009-12-03 15:32:01 +000099
maruel@chromium.org35625c72011-03-23 17:34:02 +0000100import fix_encoding
maruel@chromium.org5f3eee32009-09-17 00:34:30 +0000101import gclient_scm
102import gclient_utils
szager@chromium.org848fd492014-04-09 19:06:44 +0000103import git_cache
nasser@codeaurora.org1f7a3d12010-02-04 15:11:50 +0000104from third_party.repo.progress import Progress
maruel@chromium.org39c0b222013-08-17 16:57:01 +0000105import subcommand
maruel@chromium.org31cb48a2011-04-04 18:01:36 +0000106import subprocess2
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +0000107from third_party import colorama
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000108
szager@chromium.org7b8b6de2014-08-23 00:57:31 +0000109CHROMIUM_SRC_URL = 'https://chromium.googlesource.com/chromium/src.git'
110
111
112def ast_dict_index(dnode, key):
113 """Search an ast.Dict for the argument key, and return its index."""
114 idx = [i for i in range(len(dnode.keys)) if (
115 type(dnode.keys[i]) is ast.Str and dnode.keys[i].s == key)]
116 if not idx:
117 return -1
118 elif len(idx) > 1:
119 raise gclient_utils.Error('Multiple dict entries with same key in AST')
120 return idx[-1]
121
122def ast2str(node, indent=0):
123 """Return a pretty-printed rendition of an ast.Node."""
124 t = type(node)
125 if t is ast.Module:
126 return '\n'.join([ast2str(x, indent) for x in node.body])
127 elif t is ast.Assign:
128 return ((' ' * indent) +
129 ' = '.join([ast2str(x) for x in node.targets] +
130 [ast2str(node.value, indent)]) + '\n')
131 elif t is ast.Name:
132 return node.id
133 elif t is ast.List:
134 if not node.elts:
135 return '[]'
136 elif len(node.elts) == 1:
137 return '[' + ast2str(node.elts[0], indent) + ']'
138 return ('[\n' + (' ' * (indent + 1)) +
139 (',\n' + (' ' * (indent + 1))).join(
140 [ast2str(x, indent + 1) for x in node.elts]) +
141 '\n' + (' ' * indent) + ']')
142 elif t is ast.Dict:
143 if not node.keys:
144 return '{}'
145 elif len(node.keys) == 1:
146 return '{%s: %s}' % (ast2str(node.keys[0]),
147 ast2str(node.values[0], indent + 1))
148 return ('{\n' + (' ' * (indent + 1)) +
149 (',\n' + (' ' * (indent + 1))).join(
150 ['%s: %s' % (ast2str(node.keys[i]),
151 ast2str(node.values[i], indent + 1))
152 for i in range(len(node.keys))]) +
153 '\n' + (' ' * indent) + '}')
154 elif t is ast.Str:
155 return "'%s'" % node.s
156 else:
157 raise gclient_utils.Error("Unexpected AST node at line %d, column %d: %s"
158 % (node.lineno, node.col_offset, t))
159
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000160
maruel@chromium.org116704f2010-06-11 17:34:38 +0000161class GClientKeywords(object):
162 class FromImpl(object):
163 """Used to implement the From() syntax."""
164
165 def __init__(self, module_name, sub_target_name=None):
166 """module_name is the dep module we want to include from. It can also be
167 the name of a subdirectory to include from.
168
169 sub_target_name is an optional parameter if the module name in the other
170 DEPS file is different. E.g., you might want to map src/net to net."""
171 self.module_name = module_name
172 self.sub_target_name = sub_target_name
173
174 def __str__(self):
175 return 'From(%s, %s)' % (repr(self.module_name),
176 repr(self.sub_target_name))
177
maruel@chromium.org116704f2010-06-11 17:34:38 +0000178 class FileImpl(object):
179 """Used to implement the File('') syntax which lets you sync a single file
maruel@chromium.orge3216c62010-07-08 03:31:43 +0000180 from a SVN repo."""
maruel@chromium.org116704f2010-06-11 17:34:38 +0000181
182 def __init__(self, file_location):
183 self.file_location = file_location
184
185 def __str__(self):
186 return 'File("%s")' % self.file_location
187
188 def GetPath(self):
189 return os.path.split(self.file_location)[0]
190
191 def GetFilename(self):
192 rev_tokens = self.file_location.split('@')
193 return os.path.split(rev_tokens[0])[1]
194
195 def GetRevision(self):
196 rev_tokens = self.file_location.split('@')
197 if len(rev_tokens) > 1:
198 return rev_tokens[1]
199 return None
200
201 class VarImpl(object):
202 def __init__(self, custom_vars, local_scope):
203 self._custom_vars = custom_vars
204 self._local_scope = local_scope
205
206 def Lookup(self, var_name):
207 """Implements the Var syntax."""
208 if var_name in self._custom_vars:
209 return self._custom_vars[var_name]
210 elif var_name in self._local_scope.get("vars", {}):
211 return self._local_scope["vars"][var_name]
212 raise gclient_utils.Error("Var is not defined: %s" % var_name)
213
214
maruel@chromium.org064186c2011-09-27 23:53:33 +0000215class DependencySettings(GClientKeywords):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000216 """Immutable configuration settings."""
217 def __init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000218 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000219 custom_hooks, deps_file, should_process):
maruel@chromium.org064186c2011-09-27 23:53:33 +0000220 GClientKeywords.__init__(self)
221
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000222 # These are not mutable:
223 self._parent = parent
224 self._safesync_url = safesync_url
mmoss@chromium.org8f93f792014-08-26 23:24:09 +0000225 self._deps_file = deps_file
maruel@chromium.org064186c2011-09-27 23:53:33 +0000226 self._url = url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000227 # 'managed' determines whether or not this dependency is synced/updated by
228 # gclient after gclient checks it out initially. The difference between
229 # 'managed' and 'should_process' is that the user specifies 'managed' via
230 # the --unmanaged command-line flag or a .gclient config, where
231 # 'should_process' is dynamically set by gclient if it goes over its
232 # recursion limit and controls gclient's behavior so it does not misbehave.
233 self._managed = managed
234 self._should_process = should_process
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000235 # This is a mutable value which has the list of 'target_os' OSes listed in
236 # the current deps file.
237 self.local_target_os = None
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000238
239 # These are only set in .gclient and not in DEPS files.
240 self._custom_vars = custom_vars or {}
241 self._custom_deps = custom_deps or {}
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000242 self._custom_hooks = custom_hooks or []
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000243
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000244 # TODO(iannucci): Remove this when all masters are correctly substituting
245 # the new blink url.
246 if (self._custom_vars.get('webkit_trunk', '') ==
247 'svn://svn-mirror.golo.chromium.org/webkit-readonly/trunk'):
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000248 new_url = 'svn://svn-mirror.golo.chromium.org/blink/trunk'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000249 print('Overwriting Var("webkit_trunk") with %s' % new_url)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +0000250 self._custom_vars['webkit_trunk'] = new_url
iannucci@chromium.org3e8df4b2013-04-04 01:08:52 +0000251
maruel@chromium.org064186c2011-09-27 23:53:33 +0000252 # Post process the url to remove trailing slashes.
253 if isinstance(self._url, basestring):
254 # urls are sometime incorrectly written as proto://host/path/@rev. Replace
255 # it to proto://host/path@rev.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000256 self._url = self._url.replace('/@', '@')
257 elif not isinstance(self._url,
258 (self.FromImpl, self.FileImpl, None.__class__)):
259 raise gclient_utils.Error(
260 ('dependency url must be either a string, None, '
261 'File() or From() instead of %s') % self._url.__class__.__name__)
mmoss@chromium.orgd0b272b2013-01-30 23:55:33 +0000262 # Make any deps_file path platform-appropriate.
263 for sep in ['/', '\\']:
264 self._deps_file = self._deps_file.replace(sep, os.sep)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000265
266 @property
267 def deps_file(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000268 return self._deps_file
269
270 @property
271 def managed(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000272 return self._managed
273
274 @property
275 def parent(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000276 return self._parent
277
278 @property
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000279 def root(self):
280 """Returns the root node, a GClient object."""
281 if not self.parent:
282 # This line is to signal pylint that it could be a GClient instance.
283 return self or GClient(None, None)
284 return self.parent.root
285
286 @property
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000287 def safesync_url(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000288 return self._safesync_url
289
290 @property
291 def should_process(self):
292 """True if this dependency should be processed, i.e. checked out."""
293 return self._should_process
294
295 @property
296 def custom_vars(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000297 return self._custom_vars.copy()
298
299 @property
300 def custom_deps(self):
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000301 return self._custom_deps.copy()
302
maruel@chromium.org064186c2011-09-27 23:53:33 +0000303 @property
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000304 def custom_hooks(self):
305 return self._custom_hooks[:]
306
307 @property
maruel@chromium.org064186c2011-09-27 23:53:33 +0000308 def url(self):
309 return self._url
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000310
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000311 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000312 def target_os(self):
313 if self.local_target_os is not None:
314 return tuple(set(self.local_target_os).union(self.parent.target_os))
315 else:
316 return self.parent.target_os
317
maruel@chromium.org8c0d9582011-10-03 21:36:01 +0000318 def get_custom_deps(self, name, url):
319 """Returns a custom deps if applicable."""
320 if self.parent:
321 url = self.parent.get_custom_deps(name, url)
322 # None is a valid return value to disable a dependency.
323 return self.custom_deps.get(name, url)
324
maruel@chromium.org064186c2011-09-27 23:53:33 +0000325
326class Dependency(gclient_utils.WorkItem, DependencySettings):
maruel@chromium.org54a07a22010-06-14 19:07:39 +0000327 """Object that represents a dependency checkout."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +0000328
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000329 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000330 custom_vars, custom_hooks, deps_file, should_process):
maruel@chromium.org6ca8bf82011-09-19 23:04:30 +0000331 gclient_utils.WorkItem.__init__(self, name)
maruel@chromium.org8ac2b272011-09-26 18:49:49 +0000332 DependencySettings.__init__(
maruel@chromium.org064186c2011-09-27 23:53:33 +0000333 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000334 custom_hooks, deps_file, should_process)
maruel@chromium.org68988972011-09-20 14:11:42 +0000335
336 # This is in both .gclient and DEPS files:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000337 self._deps_hooks = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000338
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000339 self._pre_deps_hooks = []
340
maruel@chromium.org68988972011-09-20 14:11:42 +0000341 # Calculates properties:
maruel@chromium.org064186c2011-09-27 23:53:33 +0000342 self._parsed_url = None
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +0000343 self._dependencies = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000344 # A cache of the files affected by the current operation, necessary for
345 # hooks.
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000346 self._file_list = []
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000347 # List of host names from which dependencies are allowed.
348 # Default is an empty set, meaning unspecified in DEPS file, and hence all
349 # hosts will be allowed. Non-empty set means whitelist of hosts.
350 # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
351 self._allowed_hosts = frozenset()
maruel@chromium.org85c2a192010-07-22 21:14:43 +0000352 # If it is not set to True, the dependency wasn't processed for its child
353 # dependency, i.e. its DEPS wasn't read.
maruel@chromium.org064186c2011-09-27 23:53:33 +0000354 self._deps_parsed = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000355 # This dependency has been processed, i.e. checked out
maruel@chromium.org064186c2011-09-27 23:53:33 +0000356 self._processed = False
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000357 # This dependency had its pre-DEPS hooks run
358 self._pre_deps_hooks_ran = False
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000359 # This dependency had its hook run
maruel@chromium.org064186c2011-09-27 23:53:33 +0000360 self._hooks_ran = False
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000361 # This is the scm used to checkout self.url. It may be used by dependencies
362 # to get the datetime of the revision we checked out.
363 self._used_scm = None
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000364 self._used_revision = None
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000365 # The actual revision we ended up getting, or None if that information is
366 # unavailable
367 self._got_revision = None
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000368
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000369 # This is a mutable value that overrides the normal recursion limit for this
370 # dependency. It is read from the actual DEPS file so cannot be set on
371 # class instantiation.
372 self.recursion_override = None
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000373 # recursedeps is a mutable value that selectively overrides the default
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000374 # 'no recursion' setting on a dep-by-dep basis. It will replace
375 # recursion_override.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000376 self.recursedeps = None
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000377
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000378 if not self.name and self.parent:
379 raise gclient_utils.Error('Dependency without name')
380
maruel@chromium.org470b5432011-10-11 18:18:19 +0000381 @property
382 def requirements(self):
383 """Calculate the list of requirements."""
384 requirements = set()
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000385 # self.parent is implicitly a requirement. This will be recursive by
386 # definition.
387 if self.parent and self.parent.name:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000388 requirements.add(self.parent.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000389
390 # For a tree with at least 2 levels*, the leaf node needs to depend
391 # on the level higher up in an orderly way.
392 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
393 # thus unsorted, while the .gclient format is a list thus sorted.
394 #
395 # * _recursion_limit is hard coded 2 and there is no hope to change this
396 # value.
397 #
398 # Interestingly enough, the following condition only works in the case we
399 # want: self is a 2nd level node. 3nd level node wouldn't need this since
400 # they already have their parent as a requirement.
maruel@chromium.org470b5432011-10-11 18:18:19 +0000401 if self.parent and self.parent.parent and not self.parent.parent.parent:
402 requirements |= set(i.name for i in self.root.dependencies if i.name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000403
404 if isinstance(self.url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000405 requirements.add(self.url.module_name)
maruel@chromium.org118fb1c2011-09-01 20:04:24 +0000406
maruel@chromium.org470b5432011-10-11 18:18:19 +0000407 if self.name:
408 requirements |= set(
409 obj.name for obj in self.root.subtree(False)
410 if (obj is not self
411 and obj.name and
412 self.name.startswith(posixpath.join(obj.name, ''))))
413 requirements = tuple(sorted(requirements))
414 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
415 return requirements
416
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000417 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000418 def try_recursedeps(self):
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000419 """Returns False if recursion_override is ever specified."""
420 if self.recursion_override is not None:
421 return False
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000422 return self.parent.try_recursedeps
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000423
424 @property
425 def recursion_limit(self):
426 """Returns > 0 if this dependency is not too recursed to be processed."""
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000427 # We continue to support the absence of recursedeps until tools and DEPS
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000428 # using recursion_override are updated.
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000429 if self.try_recursedeps and self.parent.recursedeps != None:
430 if self.name in self.parent.recursedeps:
cmp@chromium.orge84ac912014-06-30 23:14:35 +0000431 return 1
432
433 if self.recursion_override is not None:
434 return self.recursion_override
435 return max(self.parent.recursion_limit - 1, 0)
436
maruel@chromium.org470b5432011-10-11 18:18:19 +0000437 def verify_validity(self):
438 """Verifies that this Dependency is fine to add as a child of another one.
439
440 Returns True if this entry should be added, False if it is a duplicate of
441 another entry.
442 """
443 logging.info('Dependency(%s).verify_validity()' % self.name)
444 if self.name in [s.name for s in self.parent.dependencies]:
445 raise gclient_utils.Error(
446 'The same name "%s" appears multiple times in the deps section' %
447 self.name)
448 if not self.should_process:
449 # Return early, no need to set requirements.
450 return True
451
452 # This require a full tree traversal with locks.
453 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
454 for sibling in siblings:
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000455 self_url = self.LateOverride(self.url)
456 sibling_url = sibling.LateOverride(sibling.url)
457 # Allow to have only one to be None or ''.
458 if self_url != sibling_url and bool(self_url) == bool(sibling_url):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000459 raise gclient_utils.Error(
maruel@chromium.orgb848d5b2012-10-10 23:25:50 +0000460 ('Dependency %s specified more than once:\n'
461 ' %s [%s]\n'
462 'vs\n'
463 ' %s [%s]') % (
464 self.name,
465 sibling.hierarchy(),
466 sibling_url,
467 self.hierarchy(),
468 self_url))
maruel@chromium.org470b5432011-10-11 18:18:19 +0000469 # In theory we could keep it as a shadow of the other one. In
470 # practice, simply ignore it.
471 logging.warn('Won\'t process duplicate dependency %s' % sibling)
472 return False
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000473 return True
maruel@chromium.org064186c2011-09-27 23:53:33 +0000474
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000475 def LateOverride(self, url):
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000476 """Resolves the parsed url from url.
477
478 Manages From() keyword accordingly. Do not touch self.parsed_url nor
479 self.url because it may called with other urls due to From()."""
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000480 assert self.parsed_url == None or not self.should_process, self.parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000481 parsed_url = self.get_custom_deps(self.name, url)
482 if parsed_url != url:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000483 logging.info(
484 'Dependency(%s).LateOverride(%s) -> %s' %
485 (self.name, url, parsed_url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000486 return parsed_url
487
488 if isinstance(url, self.FromImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000489 # Requires tree traversal.
maruel@chromium.org68988972011-09-20 14:11:42 +0000490 ref = [
491 dep for dep in self.root.subtree(True) if url.module_name == dep.name
492 ]
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000493 if not ref:
494 raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
495 url.module_name, ref))
496 # It may happen that len(ref) > 1 but it's no big deal.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000497 ref = ref[0]
maruel@chromium.org98d05fa2010-07-22 21:58:01 +0000498 sub_target = url.sub_target_name or self.name
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000499 found_deps = [d for d in ref.dependencies if d.name == sub_target]
500 if len(found_deps) != 1:
maruel@chromium.orgf3abb802010-08-10 17:19:56 +0000501 raise gclient_utils.Error(
maruel@chromium.org98023df2011-09-07 18:44:47 +0000502 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
503 sub_target, ref.name, self.name, self.parent.name,
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000504 str(self.root)))
maruel@chromium.org98023df2011-09-07 18:44:47 +0000505
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000506 # Call LateOverride() again.
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000507 found_dep = found_deps[0]
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000508 parsed_url = found_dep.LateOverride(found_dep.url)
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000509 logging.info(
maruel@chromium.org470b5432011-10-11 18:18:19 +0000510 'Dependency(%s).LateOverride(%s) -> %s (From)' %
511 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000512 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000513
514 if isinstance(url, basestring):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000515 parsed_url = urlparse.urlparse(url)
scr@chromium.orgf1eccaf2014-04-11 15:51:33 +0000516 if (not parsed_url[0] and
517 not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000518 # A relative url. Fetch the real base.
519 path = parsed_url[2]
520 if not path.startswith('/'):
521 raise gclient_utils.Error(
522 'relative DEPS entry \'%s\' must begin with a slash' % url)
523 # Create a scm just to query the full url.
524 parent_url = self.parent.parsed_url
525 if isinstance(parent_url, self.FileImpl):
526 parent_url = parent_url.file_location
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000527 scm = gclient_scm.CreateSCM(
528 parent_url, self.root.root_dir, None, self.outbuf)
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000529 parsed_url = scm.FullUrlForRelativeUrl(url)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000530 else:
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000531 parsed_url = url
maruel@chromium.org470b5432011-10-11 18:18:19 +0000532 logging.info(
533 'Dependency(%s).LateOverride(%s) -> %s' %
534 (self.name, url, parsed_url))
maruel@chromium.orgda7a1f92010-08-10 17:19:02 +0000535 return parsed_url
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000536
537 if isinstance(url, self.FileImpl):
maruel@chromium.org470b5432011-10-11 18:18:19 +0000538 logging.info(
539 'Dependency(%s).LateOverride(%s) -> %s (File)' %
540 (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000541 return url
542
543 if url is None:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000544 logging.info(
545 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000546 return url
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000547
548 raise gclient_utils.Error('Unknown url type')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000549
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000550 @staticmethod
551 def MergeWithOsDeps(deps, deps_os, target_os_list):
552 """Returns a new "deps" structure that is the deps sent in updated
553 with information from deps_os (the deps_os section of the DEPS
554 file) that matches the list of target os."""
555 os_overrides = {}
556 for the_target_os in target_os_list:
557 the_target_os_deps = deps_os.get(the_target_os, {})
558 for os_dep_key, os_dep_value in the_target_os_deps.iteritems():
559 overrides = os_overrides.setdefault(os_dep_key, [])
560 overrides.append((the_target_os, os_dep_value))
561
562 # If any os didn't specify a value (we have fewer value entries
563 # than in the os list), then it wants to use the default value.
564 for os_dep_key, os_dep_value in os_overrides.iteritems():
565 if len(os_dep_value) != len(target_os_list):
566 # Record the default value too so that we don't accidently
567 # set it to None or miss a conflicting DEPS.
568 if os_dep_key in deps:
569 os_dep_value.append(('default', deps[os_dep_key]))
570
571 target_os_deps = {}
572 for os_dep_key, os_dep_value in os_overrides.iteritems():
573 # os_dep_value is a list of (os, value) pairs.
574 possible_values = set(x[1] for x in os_dep_value if x[1] is not None)
575 if not possible_values:
576 target_os_deps[os_dep_key] = None
577 else:
578 if len(possible_values) > 1:
579 # It would be possible to abort here but it would be
580 # unfortunate if we end up preventing any kind of checkout.
581 logging.error('Conflicting dependencies for %s: %s. (target_os=%s)',
582 os_dep_key, os_dep_value, target_os_list)
583 # Sorting to get the same result every time in case of conflicts.
584 target_os_deps[os_dep_key] = sorted(possible_values)[0]
585
586 new_deps = deps.copy()
587 new_deps.update(target_os_deps)
588 return new_deps
589
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000590 def ParseDepsFile(self):
maruel@chromium.org271375b2010-06-23 19:17:38 +0000591 """Parses the DEPS file for this dependency."""
maruel@chromium.org3223edd2011-10-10 23:17:39 +0000592 assert not self.deps_parsed
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000593 assert not self.dependencies
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000594
595 deps_content = None
596 use_strict = False
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000597
598 # First try to locate the configured deps file. If it's missing, fallback
599 # to DEPS.
600 deps_files = [self.deps_file]
601 if 'DEPS' not in deps_files:
602 deps_files.append('DEPS')
603 for deps_file in deps_files:
604 filepath = os.path.join(self.root.root_dir, self.name, deps_file)
605 if os.path.isfile(filepath):
606 logging.info(
607 'ParseDepsFile(%s): %s file found at %s', self.name, deps_file,
608 filepath)
609 break
maruel@chromium.org1333cb32011-10-04 23:40:16 +0000610 logging.info(
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000611 'ParseDepsFile(%s): No %s file found at %s', self.name, deps_file,
612 filepath)
613
614 if os.path.isfile(filepath):
maruel@chromium.org46304292010-10-28 11:42:00 +0000615 deps_content = gclient_utils.FileRead(filepath)
cmp@chromium.org76ce73c2014-07-02 00:13:18 +0000616 logging.debug('ParseDepsFile(%s) read:\n%s', self.name, deps_content)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000617 use_strict = 'use strict' in deps_content.splitlines()[0]
618
619 local_scope = {}
620 if deps_content:
621 # One thing is unintuitive, vars = {} must happen before Var() use.
622 var = self.VarImpl(self.custom_vars, local_scope)
623 if use_strict:
624 logging.info(
625 'ParseDepsFile(%s): Strict Mode Enabled', self.name)
626 global_scope = {
627 '__builtins__': {'None': None},
628 'Var': var.Lookup,
629 'deps_os': {},
630 }
631 else:
632 global_scope = {
633 'File': self.FileImpl,
634 'From': self.FromImpl,
635 'Var': var.Lookup,
636 'deps_os': {},
637 }
maruel@chromium.org46304292010-10-28 11:42:00 +0000638 # Eval the content.
639 try:
640 exec(deps_content, global_scope, local_scope)
641 except SyntaxError, e:
642 gclient_utils.SyntaxErrorToError(filepath, e)
iannucci@chromium.org0c3f3052014-05-22 00:29:22 +0000643 if use_strict:
644 for key, val in local_scope.iteritems():
645 if not isinstance(val, (dict, list, tuple, str)):
646 raise gclient_utils.Error(
647 'ParseDepsFile(%s): Strict mode disallows %r -> %r' %
648 (self.name, key, val))
649
maruel@chromium.org271375b2010-06-23 19:17:38 +0000650 deps = local_scope.get('deps', {})
ilevy@chromium.org27ca3a92012-10-17 18:11:02 +0000651 if 'recursion' in local_scope:
652 self.recursion_override = local_scope.get('recursion')
653 logging.warning(
654 'Setting %s recursion to %d.', self.name, self.recursion_limit)
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000655 self.recursedeps = local_scope.get('recursedeps', None)
656 if 'recursedeps' in local_scope:
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000657 self.recursedeps = set(self.recursedeps)
cmp@chromium.orgc401ad12014-07-02 23:20:08 +0000658 logging.warning('Found recursedeps %r.', repr(self.recursedeps))
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +0000659 # If present, save 'target_os' in the local_target_os property.
660 if 'target_os' in local_scope:
661 self.local_target_os = local_scope['target_os']
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000662 # load os specific dependencies if defined. these dependencies may
663 # override or extend the values defined by the 'deps' member.
bratell@opera.comed2b4fe2013-12-16 14:34:12 +0000664 target_os_list = self.target_os
665 if 'deps_os' in local_scope and target_os_list:
666 deps = self.MergeWithOsDeps(deps, local_scope['deps_os'], target_os_list)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000667
maruel@chromium.org271375b2010-06-23 19:17:38 +0000668 # If a line is in custom_deps, but not in the solution, we want to append
669 # this line to the solution.
670 for d in self.custom_deps:
671 if d not in deps:
672 deps[d] = self.custom_deps[d]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000673
674 # If use_relative_paths is set in the DEPS file, regenerate
675 # the dictionary using paths relative to the directory containing
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000676 # the DEPS file. Also update recursedeps if use_relative_paths is
677 # enabled.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000678 use_relative_paths = local_scope.get('use_relative_paths', False)
679 if use_relative_paths:
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000680 logging.warning('use_relative_paths enabled.')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000681 rel_deps = {}
682 for d, url in deps.items():
683 # normpath is required to allow DEPS to use .. in their
684 # dependency local path.
maruel@chromium.org271375b2010-06-23 19:17:38 +0000685 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000686 logging.warning('Updating deps by prepending %s.', self.name)
maruel@chromium.org271375b2010-06-23 19:17:38 +0000687 deps = rel_deps
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000688
cmp@chromium.orgf2def0a2014-07-16 19:48:54 +0000689 # Update recursedeps if it's set.
690 if self.recursedeps is not None:
691 logging.warning('Updating recursedeps by prepending %s.', self.name)
692 rel_deps = set()
693 for d in self.recursedeps:
694 rel_deps.add(os.path.normpath(os.path.join(self.name, d)))
695 self.recursedeps = rel_deps
696
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000697 if 'allowed_hosts' in local_scope:
698 try:
699 self._allowed_hosts = frozenset(local_scope.get('allowed_hosts'))
700 except TypeError: # raised if non-iterable
701 pass
702 if not self._allowed_hosts:
703 logging.warning("allowed_hosts is specified but empty %s",
704 self._allowed_hosts)
705 raise gclient_utils.Error(
706 'ParseDepsFile(%s): allowed_hosts must be absent '
707 'or a non-empty iterable' % self.name)
708
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000709 # Convert the deps into real Dependency.
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000710 deps_to_add = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000711 for name, url in deps.iteritems():
maruel@chromium.org68988972011-09-20 14:11:42 +0000712 should_process = self.recursion_limit and self.should_process
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000713 deps_to_add.append(Dependency(
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000714 self, name, url, None, None, None, None, None,
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000715 self.deps_file, should_process))
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000716 deps_to_add.sort(key=lambda x: x.name)
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000717
718 # override named sets of hooks by the custom hooks
719 hooks_to_run = []
720 hook_names_to_suppress = [c.get('name', '') for c in self.custom_hooks]
721 for hook in local_scope.get('hooks', []):
722 if hook.get('name', '') not in hook_names_to_suppress:
723 hooks_to_run.append(hook)
724
725 # add the replacements and any additions
726 for hook in self.custom_hooks:
727 if 'action' in hook:
728 hooks_to_run.append(hook)
729
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000730 self._pre_deps_hooks = [self.GetHookAction(hook, []) for hook in
731 local_scope.get('pre_deps_hooks', [])]
732
petermayo@chromium.orge79161a2013-07-09 14:40:37 +0000733 self.add_dependencies_and_close(deps_to_add, hooks_to_run)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000734 logging.info('ParseDepsFile(%s) done' % self.name)
735
736 def add_dependencies_and_close(self, deps_to_add, hooks):
737 """Adds the dependencies, hooks and mark the parsing as done."""
maruel@chromium.orgb9be0652011-10-14 18:05:40 +0000738 for dep in deps_to_add:
maruel@chromium.org470b5432011-10-11 18:18:19 +0000739 if dep.verify_validity():
maruel@chromium.org0bcfd182011-10-10 20:06:09 +0000740 self.add_dependency(dep)
741 self._mark_as_parsed(hooks)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000742
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000743 def maybeGetParentRevision(self, command, options, parsed_url, parent):
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000744 """Uses revision/timestamp of parent if no explicit revision was specified.
745
746 If we are performing an update and --transitive is set, use
747 - the parent's revision if 'self.url' is in the same repository
748 - the parent's timestamp otherwise
749 to update 'self.url'. The used revision/timestamp will be set in
750 'options.revision'.
751 If we have an explicit revision do nothing.
752 """
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000753 if command == 'update' and options.transitive and not options.revision:
754 _, revision = gclient_utils.SplitUrlRevision(parsed_url)
755 if not revision:
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000756 options.revision = getattr(parent, '_used_revision', None)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000757 if (options.revision and
758 not gclient_utils.IsDateRevision(options.revision)):
759 assert self.parent and self.parent.used_scm
760 # If this dependency is in the same repository as parent it's url will
761 # start with a slash. If so we take the parent revision instead of
762 # it's timestamp.
763 # (The timestamps of commits in google code are broken -- which can
764 # result in dependencies to be checked out at the wrong revision)
765 if self.url.startswith('/'):
766 if options.verbose:
767 print('Using parent\'s revision %s since we are in the same '
768 'repository.' % options.revision)
769 else:
770 parent_revision_date = self.parent.used_scm.GetRevisionDate(
771 options.revision)
772 options.revision = gclient_utils.MakeDateRevision(
773 parent_revision_date)
774 if options.verbose:
775 print('Using parent\'s revision date %s since we are in a '
776 'different repository.' % options.revision)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000777
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000778 def findDepsFromNotAllowedHosts(self):
779 """Returns a list of depenecies from not allowed hosts.
780
781 If allowed_hosts is not set, allows all hosts and returns empty list.
782 """
783 if not self._allowed_hosts:
784 return []
785 bad_deps = []
786 for dep in self._dependencies:
szager@chromium.orgbd772dd2014-11-05 18:43:08 +0000787 # Don't enforce this for custom_deps.
788 if dep.name in self._custom_deps:
789 continue
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +0000790 if isinstance(dep.url, basestring):
791 parsed_url = urlparse.urlparse(dep.url)
792 if parsed_url.netloc and parsed_url.netloc not in self._allowed_hosts:
793 bad_deps.append(dep)
794 return bad_deps
795
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000796 # Arguments number differs from overridden method
797 # pylint: disable=W0221
maruel@chromium.org3742c842010-09-09 19:27:14 +0000798 def run(self, revision_overrides, command, args, work_queue, options):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000799 """Runs |command| then parse the DEPS file."""
maruel@chromium.org470b5432011-10-11 18:18:19 +0000800 logging.info('Dependency(%s).run()' % self.name)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +0000801 assert self._file_list == []
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000802 if not self.should_process:
803 return
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000804 # When running runhooks, there's no need to consult the SCM.
805 # All known hooks are expected to run unconditionally regardless of working
806 # copy state, so skip the SCM status check.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000807 run_scm = command not in ('runhooks', 'recurse', None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000808 parsed_url = self.LateOverride(self.url)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000809 file_list = [] if not options.nohooks else None
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000810 revision_override = revision_overrides.pop(self.name, None)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000811 if run_scm and parsed_url:
812 if isinstance(parsed_url, self.FileImpl):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000813 # Special support for single-file checkout.
814 if not command in (None, 'cleanup', 'diff', 'pack', 'status'):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000815 # Sadly, pylint doesn't realize that parsed_url is of FileImpl.
816 # pylint: disable=E1103
817 options.revision = parsed_url.GetRevision()
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000818 self._used_scm = gclient_scm.SVNWrapper(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000819 parsed_url.GetPath(), self.root.root_dir, self.name,
820 out_cb=work_queue.out_cb)
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000821 self._used_scm.RunCommand('updatesingle',
822 options, args + [parsed_url.GetFilename()], file_list)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000823 else:
maruel@chromium.org9e5317a2010-08-13 20:35:11 +0000824 # Create a shallow copy to mutate revision.
825 options = copy.copy(options)
szager@chromium.org3a3608d2014-10-22 21:13:52 +0000826 options.revision = revision_override
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000827 self.maybeGetParentRevision(
szager@chromium.org4ad264b2014-05-20 04:43:47 +0000828 command, options, parsed_url, self.parent)
829 self._used_revision = options.revision
kustermann@google.coma692e8f2013-04-18 08:32:04 +0000830 self._used_scm = gclient_scm.CreateSCM(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000831 parsed_url, self.root.root_dir, self.name, self.outbuf,
832 out_cb=work_queue.out_cb)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +0000833 self._got_revision = self._used_scm.RunCommand(command, options, args,
834 file_list)
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000835 if file_list:
836 file_list = [os.path.join(self.name, f.strip()) for f in file_list]
maruel@chromium.org68988972011-09-20 14:11:42 +0000837
838 # TODO(phajdan.jr): We should know exactly when the paths are absolute.
839 # Convert all absolute paths to relative.
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000840 for i in range(len(file_list or [])):
maruel@chromium.org68988972011-09-20 14:11:42 +0000841 # It depends on the command being executed (like runhooks vs sync).
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000842 if not os.path.isabs(file_list[i]):
maruel@chromium.org68988972011-09-20 14:11:42 +0000843 continue
844 prefix = os.path.commonprefix(
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000845 [self.root.root_dir.lower(), file_list[i].lower()])
846 file_list[i] = file_list[i][len(prefix):]
maruel@chromium.org68988972011-09-20 14:11:42 +0000847 # Strip any leading path separators.
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000848 while file_list[i].startswith(('\\', '/')):
849 file_list[i] = file_list[i][1:]
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000850
851 # Always parse the DEPS file.
852 self.ParseDepsFile()
iannucci@chromium.org396e1a62013-07-03 19:41:04 +0000853 self._run_is_done(file_list or [], parsed_url)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000854 if command in ('update', 'revert') and not options.noprehooks:
855 self.RunPreDepsHooks()
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000856
857 if self.recursion_limit:
858 # Parse the dependencies of this dependency.
859 for s in self.dependencies:
860 work_queue.enqueue(s)
861
862 if command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000863 if not isinstance(parsed_url, self.FileImpl):
864 # Skip file only checkout.
865 scm = gclient_scm.GetScmName(parsed_url)
866 if not options.scm or scm in options.scm:
867 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000868 # Pass in the SCM type as an env variable. Make sure we don't put
869 # unicode strings in the environment.
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000870 env = os.environ.copy()
871 if scm:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000872 env['GCLIENT_SCM'] = str(scm)
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000873 if parsed_url:
rnk@chromium.org2d3c28d2014-03-30 00:56:32 +0000874 env['GCLIENT_URL'] = str(parsed_url)
875 env['GCLIENT_DEP_PATH'] = str(self.name)
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000876 if options.prepend_dir and scm == 'git':
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000877 print_stdout = False
878 def filter_fn(line):
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000879 """Git-specific path marshaling. It is optimized for git-grep."""
880
881 def mod_path(git_pathspec):
882 match = re.match('^(\\S+?:)?([^\0]+)$', git_pathspec)
883 modified_path = os.path.join(self.name, match.group(2))
884 branch = match.group(1) or ''
885 return '%s%s' % (branch, modified_path)
886
887 match = re.match('^Binary file ([^\0]+) matches$', line)
888 if match:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000889 print('Binary file %s matches\n' % mod_path(match.group(1)))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000890 return
891
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000892 items = line.split('\0')
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000893 if len(items) == 2 and items[1]:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000894 print('%s : %s' % (mod_path(items[0]), items[1]))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000895 elif len(items) >= 2:
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000896 # Multiple null bytes or a single trailing null byte indicate
897 # git is likely displaying filenames only (such as with -l)
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000898 print('\n'.join(mod_path(path) for path in items if path))
ilevy@chromium.org0233ac22012-11-28 20:27:02 +0000899 else:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000900 print(line)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000901 else:
902 print_stdout = True
903 filter_fn = None
904
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000905 if parsed_url is None:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000906 print('Skipped omitted dependency %s' % cwd, file=sys.stderr)
iannucci@chromium.orgf3ec5782013-07-18 18:37:50 +0000907 elif os.path.isdir(cwd):
maruel@chromium.org288054d2012-03-05 00:43:07 +0000908 try:
909 gclient_utils.CheckCallAndFilter(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +0000910 args, cwd=cwd, env=env, print_stdout=print_stdout,
911 filter_fn=filter_fn,
912 )
maruel@chromium.org288054d2012-03-05 00:43:07 +0000913 except subprocess2.CalledProcessError:
914 if not options.ignore:
915 raise
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +0000916 else:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000917 print('Skipped missing %s' % cwd, file=sys.stderr)
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000918
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000919
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000920 @gclient_utils.lockedmethod
921 def _run_is_done(self, file_list, parsed_url):
922 # Both these are kept for hooks that are run as a separate tree traversal.
923 self._file_list = file_list
924 self._parsed_url = parsed_url
925 self._processed = True
926
szager@google.comb9a78d32012-03-13 18:46:21 +0000927 @staticmethod
928 def GetHookAction(hook_dict, matching_file_list):
929 """Turns a parsed 'hook' dict into an executable command."""
930 logging.debug(hook_dict)
931 logging.debug(matching_file_list)
932 command = hook_dict['action'][:]
933 if command[0] == 'python':
934 # If the hook specified "python" as the first item, the action is a
935 # Python script. Run it by starting a new copy of the same
936 # interpreter.
937 command[0] = sys.executable
938 if '$matching_files' in command:
939 splice_index = command.index('$matching_files')
940 command[splice_index:splice_index + 1] = matching_file_list
941 return command
942
943 def GetHooks(self, options):
944 """Evaluates all hooks, and return them in a flat list.
945
946 RunOnDeps() must have been called before to load the DEPS.
947 """
948 result = []
maruel@chromium.org68988972011-09-20 14:11:42 +0000949 if not self.should_process or not self.recursion_limit:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000950 # Don't run the hook when it is above recursion_limit.
szager@google.comb9a78d32012-03-13 18:46:21 +0000951 return result
maruel@chromium.orgdc7445d2010-07-09 21:05:29 +0000952 # If "--force" was specified, run all hooks regardless of what files have
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000953 # changed.
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000954 if self.deps_hooks:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000955 # TODO(maruel): If the user is using git or git-svn, then we don't know
956 # what files have changed so we always run all hooks. It'd be nice to fix
957 # that.
958 if (options.force or
959 isinstance(self.parsed_url, self.FileImpl) or
960 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +0000961 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000962 for hook_dict in self.deps_hooks:
szager@google.comb9a78d32012-03-13 18:46:21 +0000963 result.append(self.GetHookAction(hook_dict, []))
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000964 else:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000965 # Run hooks on the basis of whether the files from the gclient operation
966 # match each hook's pattern.
967 for hook_dict in self.deps_hooks:
968 pattern = re.compile(hook_dict['pattern'])
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +0000969 matching_file_list = [
970 f for f in self.file_list_and_children if pattern.search(f)
971 ]
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +0000972 if matching_file_list:
szager@google.comb9a78d32012-03-13 18:46:21 +0000973 result.append(self.GetHookAction(hook_dict, matching_file_list))
maruel@chromium.orgf50907b2010-08-12 17:05:48 +0000974 for s in self.dependencies:
szager@google.comb9a78d32012-03-13 18:46:21 +0000975 result.extend(s.GetHooks(options))
976 return result
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000977
szager@google.comb9a78d32012-03-13 18:46:21 +0000978 def RunHooksRecursively(self, options):
979 assert self.hooks_ran == False
maruel@chromium.org064186c2011-09-27 23:53:33 +0000980 self._hooks_ran = True
szager@google.comb9a78d32012-03-13 18:46:21 +0000981 for hook in self.GetHooks(options):
982 try:
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000983 start_time = time.time()
szager@google.comb9a78d32012-03-13 18:46:21 +0000984 gclient_utils.CheckCallAndFilterAndHeader(
985 hook, cwd=self.root.root_dir, always=True)
986 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
987 # Use a discrete exit status code of 2 to indicate that a hook action
988 # failed. Users of this script may wish to treat hook action failures
989 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000990 print('Error: %s' % str(e), file=sys.stderr)
szager@google.comb9a78d32012-03-13 18:46:21 +0000991 sys.exit(2)
ilevy@chromium.orgc28d3772013-07-12 19:42:37 +0000992 finally:
993 elapsed_time = time.time() - start_time
994 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +0000995 print("Hook '%s' took %.2f secs" % (
996 gclient_utils.CommandToStr(hook), elapsed_time))
maruel@chromium.orgeaf61062010-07-07 18:42:39 +0000997
borenet@google.com2d1ee9e2013-10-15 08:13:16 +0000998 def RunPreDepsHooks(self):
999 assert self.processed
1000 assert self.deps_parsed
1001 assert not self.pre_deps_hooks_ran
1002 assert not self.hooks_ran
1003 for s in self.dependencies:
1004 assert not s.processed
1005 self._pre_deps_hooks_ran = True
1006 for hook in self.pre_deps_hooks:
1007 try:
1008 start_time = time.time()
1009 gclient_utils.CheckCallAndFilterAndHeader(
1010 hook, cwd=self.root.root_dir, always=True)
1011 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
1012 # Use a discrete exit status code of 2 to indicate that a hook action
1013 # failed. Users of this script may wish to treat hook action failures
1014 # differently from VC failures.
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001015 print('Error: %s' % str(e), file=sys.stderr)
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001016 sys.exit(2)
1017 finally:
1018 elapsed_time = time.time() - start_time
1019 if elapsed_time > 10:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001020 print("Hook '%s' took %.2f secs" % (
1021 gclient_utils.CommandToStr(hook), elapsed_time))
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001022
1023
maruel@chromium.org0d812442010-08-10 12:41:08 +00001024 def subtree(self, include_all):
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001025 """Breadth first recursion excluding root node."""
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001026 dependencies = self.dependencies
1027 for d in dependencies:
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001028 if d.should_process or include_all:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001029 yield d
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001030 for d in dependencies:
maruel@chromium.orgad3287e2011-10-03 19:15:10 +00001031 for i in d.subtree(include_all):
1032 yield i
1033
1034 def depth_first_tree(self):
1035 """Depth-first recursion including the root node."""
1036 yield self
1037 for i in self.dependencies:
1038 for j in i.depth_first_tree():
1039 if j.should_process:
1040 yield j
maruel@chromium.orgc57e4f22010-07-22 21:37:46 +00001041
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001042 @gclient_utils.lockedmethod
1043 def add_dependency(self, new_dep):
1044 self._dependencies.append(new_dep)
1045
1046 @gclient_utils.lockedmethod
1047 def _mark_as_parsed(self, new_hooks):
1048 self._deps_hooks.extend(new_hooks)
1049 self._deps_parsed = True
1050
maruel@chromium.org68988972011-09-20 14:11:42 +00001051 @property
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001052 @gclient_utils.lockedmethod
maruel@chromium.org4bdd5fd2011-09-26 19:41:17 +00001053 def dependencies(self):
1054 return tuple(self._dependencies)
1055
1056 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001057 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001058 def deps_hooks(self):
1059 return tuple(self._deps_hooks)
1060
1061 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001062 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001063 def pre_deps_hooks(self):
1064 return tuple(self._pre_deps_hooks)
1065
1066 @property
1067 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001068 def parsed_url(self):
1069 return self._parsed_url
1070
1071 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001072 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001073 def deps_parsed(self):
maruel@chromium.org3223edd2011-10-10 23:17:39 +00001074 """This is purely for debugging purposes. It's not used anywhere."""
maruel@chromium.org064186c2011-09-27 23:53:33 +00001075 return self._deps_parsed
1076
1077 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001078 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001079 def processed(self):
1080 return self._processed
1081
1082 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001083 @gclient_utils.lockedmethod
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001084 def pre_deps_hooks_ran(self):
1085 return self._pre_deps_hooks_ran
1086
1087 @property
1088 @gclient_utils.lockedmethod
maruel@chromium.org064186c2011-09-27 23:53:33 +00001089 def hooks_ran(self):
1090 return self._hooks_ran
1091
1092 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001093 @gclient_utils.lockedmethod
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001094 def allowed_hosts(self):
1095 return self._allowed_hosts
1096
1097 @property
1098 @gclient_utils.lockedmethod
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001099 def file_list(self):
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001100 return tuple(self._file_list)
1101
1102 @property
kustermann@google.coma692e8f2013-04-18 08:32:04 +00001103 def used_scm(self):
1104 """SCMWrapper instance for this dependency or None if not processed yet."""
1105 return self._used_scm
1106
1107 @property
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001108 @gclient_utils.lockedmethod
1109 def got_revision(self):
1110 return self._got_revision
1111
1112 @property
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001113 def file_list_and_children(self):
1114 result = list(self.file_list)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001115 for d in self.dependencies:
maruel@chromium.orgbaa7be32011-10-10 20:49:47 +00001116 result.extend(d.file_list_and_children)
maruel@chromium.org68988972011-09-20 14:11:42 +00001117 return tuple(result)
maruel@chromium.org861fd0f2010-07-23 03:05:05 +00001118
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001119 def __str__(self):
1120 out = []
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001121 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps',
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001122 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00001123 'processed', 'hooks_ran', 'deps_parsed', 'requirements',
1124 'allowed_hosts'):
maruel@chromium.org3c74bc92011-09-15 19:17:21 +00001125 # First try the native property if it exists.
1126 if hasattr(self, '_' + i):
1127 value = getattr(self, '_' + i, False)
1128 else:
1129 value = getattr(self, i, False)
1130 if value:
1131 out.append('%s: %s' % (i, value))
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001132
1133 for d in self.dependencies:
1134 out.extend([' ' + x for x in str(d).splitlines()])
1135 out.append('')
1136 return '\n'.join(out)
1137
1138 def __repr__(self):
1139 return '%s: %s' % (self.name, self.url)
1140
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001141 def hierarchy(self):
maruel@chromium.orgbc2d2f92010-07-22 21:26:48 +00001142 """Returns a human-readable hierarchical reference to a Dependency."""
maruel@chromium.orgbffb9042010-07-22 20:59:36 +00001143 out = '%s(%s)' % (self.name, self.url)
1144 i = self.parent
1145 while i and i.name:
1146 out = '%s(%s) -> %s' % (i.name, i.url, out)
1147 i = i.parent
1148 return out
1149
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001150
1151class GClient(Dependency):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001152 """Object that represent a gclient checkout. A tree of Dependency(), one per
1153 solution or DEPS entry."""
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001154
1155 DEPS_OS_CHOICES = {
1156 "win32": "win",
1157 "win": "win",
1158 "cygwin": "win",
1159 "darwin": "mac",
1160 "mac": "mac",
1161 "unix": "unix",
1162 "linux": "unix",
1163 "linux2": "unix",
maruel@chromium.org244e3442011-06-12 15:20:55 +00001164 "linux3": "unix",
szager@chromium.orgf8c95cd2012-06-01 22:26:52 +00001165 "android": "android",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001166 }
1167
1168 DEFAULT_CLIENT_FILE_TEXT = ("""\
1169solutions = [
1170 { "name" : "%(solution_name)s",
1171 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001172 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001173 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001174 "custom_deps" : {
1175 },
maruel@chromium.org73e21142010-07-05 13:32:01 +00001176 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001177 },
1178]
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001179cache_dir = %(cache_dir)r
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001180""")
1181
1182 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
1183 { "name" : "%(solution_name)s",
1184 "url" : "%(solution_url)s",
nsylvain@google.comefc80932011-05-31 21:27:56 +00001185 "deps_file" : "%(deps_file)s",
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001186 "managed" : %(managed)s,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001187 "custom_deps" : {
maruel@chromium.org73e21142010-07-05 13:32:01 +00001188%(solution_deps)s },
1189 "safesync_url": "%(safesync_url)s",
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001190 },
1191""")
1192
1193 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1194# Snapshot generated with gclient revinfo --snapshot
1195solutions = [
maruel@chromium.org73e21142010-07-05 13:32:01 +00001196%(solution_list)s]
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001197""")
1198
1199 def __init__(self, root_dir, options):
maruel@chromium.org0d812442010-08-10 12:41:08 +00001200 # Do not change previous behavior. Only solution level and immediate DEPS
1201 # are processed.
1202 self._recursion_limit = 2
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001203 Dependency.__init__(self, None, None, None, None, True, None, None, None,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001204 'unused', True)
maruel@chromium.org0d425922010-06-21 19:22:24 +00001205 self._options = options
maruel@chromium.org271375b2010-06-23 19:17:38 +00001206 if options.deps_os:
1207 enforced_os = options.deps_os.split(',')
1208 else:
1209 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
1210 if 'all' in enforced_os:
1211 enforced_os = self.DEPS_OS_CHOICES.itervalues()
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001212 self._enforced_os = tuple(set(enforced_os))
maruel@chromium.org271375b2010-06-23 19:17:38 +00001213 self._root_dir = root_dir
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001214 self.config_content = None
1215
borenet@google.com88d10082014-03-21 17:24:48 +00001216 def _CheckConfig(self):
1217 """Verify that the config matches the state of the existing checked-out
1218 solutions."""
1219 for dep in self.dependencies:
1220 if dep.managed and dep.url:
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001221 scm = gclient_scm.CreateSCM(
1222 dep.url, self.root_dir, dep.name, self.outbuf)
smut@google.comd33eab32014-07-07 19:35:18 +00001223 actual_url = scm.GetActualRemoteURL(self._options)
borenet@google.com4e9be262014-04-08 19:40:30 +00001224 if actual_url and not scm.DoesRemoteURLMatch(self._options):
borenet@google.com0a427372014-04-02 19:12:13 +00001225 raise gclient_utils.Error('''
borenet@google.com88d10082014-03-21 17:24:48 +00001226Your .gclient file seems to be broken. The requested URL is different from what
borenet@google.com0a427372014-04-02 19:12:13 +00001227is actually checked out in %(checkout_path)s.
borenet@google.com88d10082014-03-21 17:24:48 +00001228
borenet@google.com97882362014-04-07 20:06:02 +00001229The .gclient file contains:
1230%(expected_url)s (%(expected_scm)s)
1231
1232The local checkout in %(checkout_path)s reports:
1233%(actual_url)s (%(actual_scm)s)
borenet@google.com88d10082014-03-21 17:24:48 +00001234
1235You should ensure that the URL listed in .gclient is correct and either change
1236it or fix the checkout. If you're managing your own git checkout in
1237%(checkout_path)s but the URL in .gclient is for an svn repository, you probably
1238want to set 'managed': False in .gclient.
borenet@google.com88d10082014-03-21 17:24:48 +00001239''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1240 'expected_url': dep.url,
1241 'expected_scm': gclient_scm.GetScmName(dep.url),
1242 'actual_url': actual_url,
1243 'actual_scm': gclient_scm.GetScmName(actual_url)})
1244
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001245 def SetConfig(self, content):
maruel@chromium.orgf13a4182011-09-22 00:26:15 +00001246 assert not self.dependencies
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001247 config_dict = {}
1248 self.config_content = content
1249 try:
1250 exec(content, config_dict)
1251 except SyntaxError, e:
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001252 gclient_utils.SyntaxErrorToError('.gclient', e)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001253
peter@chromium.org1efccc82012-04-27 16:34:38 +00001254 # Append any target OS that is not already being enforced to the tuple.
1255 target_os = config_dict.get('target_os', [])
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001256 if config_dict.get('target_os_only', False):
1257 self._enforced_os = tuple(set(target_os))
1258 else:
1259 self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1260
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001261 cache_dir = config_dict.get('cache_dir')
1262 if cache_dir:
1263 cache_dir = os.path.join(self.root_dir, cache_dir)
1264 cache_dir = os.path.abspath(cache_dir)
szager@chromium.orgcaf5bef2014-08-24 18:56:32 +00001265 # If running on a bot, force break any stale git cache locks.
dnj@chromium.orgb682b3e2014-08-25 19:17:12 +00001266 if os.path.exists(cache_dir) and os.environ.get('CHROME_HEADLESS'):
szager@chromium.org4848fb62014-08-24 19:16:31 +00001267 subprocess2.check_call(['git', 'cache', 'unlock', '--cache-dir',
1268 cache_dir, '--force', '--all'])
dyen@chromium.orgd915cca2014-08-07 21:41:37 +00001269 gclient_scm.GitWrapper.cache_dir = cache_dir
1270 git_cache.Mirror.SetCachePath(cache_dir)
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001271
stuartmorgan@chromium.org18a4f6a2012-11-02 13:37:24 +00001272 if not target_os and config_dict.get('target_os_only', False):
1273 raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1274 'not specified')
peter@chromium.org1efccc82012-04-27 16:34:38 +00001275
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001276 deps_to_add = []
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001277 for s in config_dict.get('solutions', []):
maruel@chromium.org81843b82010-06-28 16:49:26 +00001278 try:
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001279 deps_to_add.append(Dependency(
maruel@chromium.org81843b82010-06-28 16:49:26 +00001280 self, s['name'], s['url'],
1281 s.get('safesync_url', None),
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001282 s.get('managed', True),
maruel@chromium.org81843b82010-06-28 16:49:26 +00001283 s.get('custom_deps', {}),
maruel@chromium.org0d812442010-08-10 12:41:08 +00001284 s.get('custom_vars', {}),
petermayo@chromium.orge79161a2013-07-09 14:40:37 +00001285 s.get('custom_hooks', []),
nsylvain@google.comefc80932011-05-31 21:27:56 +00001286 s.get('deps_file', 'DEPS'),
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001287 True))
maruel@chromium.org81843b82010-06-28 16:49:26 +00001288 except KeyError:
1289 raise gclient_utils.Error('Invalid .gclient file. Solution is '
1290 'incomplete: %s' % s)
maruel@chromium.org0bcfd182011-10-10 20:06:09 +00001291 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
1292 logging.info('SetConfig() done')
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001293
1294 def SaveConfig(self):
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001295 gclient_utils.FileWrite(os.path.join(self.root_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001296 self._options.config_filename),
1297 self.config_content)
1298
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001299 def MigrateConfigToGit(self, path, options):
1300 svn_url_re = re.compile('^(https?://src\.chromium\.org/svn|'
1301 'svn://svn\.chromium\.org/chrome)/'
1302 '(trunk|branches/[^/]+)/src')
1303 old_git_re = re.compile('^(https?://git\.chromium\.org|'
1304 'ssh://([a-zA-Z_][a-zA-Z0-9_-]*@)?'
1305 'gerrit\.chromium\.org(:2941[89])?)/'
1306 'chromium/src\.git')
1307 # Scan existing .gclient file for obsolete settings. It would be simpler
1308 # to traverse self.dependencies, but working with the AST allows the code to
1309 # dump an updated .gclient file that preserves the ordering of the original.
1310 a = ast.parse(self.config_content, options.config_filename, 'exec')
1311 modified = False
1312 solutions = [elem for elem in a.body if 'solutions' in
1313 [target.id for target in elem.targets]]
1314 if not solutions:
1315 return self
1316 solutions = solutions[-1]
1317 for solution in solutions.value.elts:
1318 # Check for obsolete URL's
1319 url_idx = ast_dict_index(solution, 'url')
1320 if url_idx == -1:
1321 continue
1322 url_val = solution.values[url_idx]
1323 if type(url_val) is not ast.Str:
1324 continue
1325 if (svn_url_re.match(url_val.s.strip())):
1326 raise gclient_utils.Error(
1327"""
1328The chromium code repository has migrated completely to git.
1329Your SVN-based checkout is now obsolete; you need to create a brand-new
1330git checkout by following these instructions:
1331
1332http://www.chromium.org/developers/how-tos/get-the-code
1333""")
1334 if (old_git_re.match(url_val.s.strip())):
1335 url_val.s = CHROMIUM_SRC_URL
1336 modified = True
1337
szager@chromium.org808bcfb2014-08-24 19:38:43 +00001338 # Ensure deps_file is set to .DEPS.git. We enforce this here to smooth
1339 # over switching between pre-git-migration and post-git-migration
1340 # revisions.
1341 # - For pre-migration revisions, .DEPS.git must be explicitly set.
1342 # - For post-migration revisions, .DEPS.git is not present, so gclient
1343 # will correctly fall back to DEPS.
1344 if url_val.s == CHROMIUM_SRC_URL:
1345 deps_file_idx = ast_dict_index(solution, 'deps_file')
1346 if deps_file_idx != -1:
1347 continue
1348 solution.keys.append(ast.Str('deps_file'))
1349 solution.values.append(ast.Str('.DEPS.git'))
1350 modified = True
1351
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001352 if not modified:
1353 return self
1354
1355 print(
1356"""
1357WARNING: gclient detected an obsolete setting in your %s file. The file has
1358been automagically updated. The previous version is available at %s.old.
1359""" % (options.config_filename, options.config_filename))
1360
1361 # Replace existing .gclient with the updated version.
1362 # Return a new GClient instance based on the new content.
1363 new_content = ast2str(a)
1364 dot_gclient_fn = os.path.join(path, options.config_filename)
iannucci@chromium.orgc7c41682014-08-23 03:44:18 +00001365 try:
1366 os.rename(dot_gclient_fn, dot_gclient_fn + '.old')
1367 except OSError:
1368 pass
1369 with open(dot_gclient_fn, 'w') as fh:
1370 fh.write(new_content)
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001371 client = GClient(path, options)
1372 client.SetConfig(new_content)
1373 return client
1374
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001375 @staticmethod
1376 def LoadCurrentConfig(options):
1377 """Searches for and loads a .gclient file relative to the current working
1378 dir. Returns a GClient object."""
szager@chromium.orge2e03202012-07-31 18:05:16 +00001379 if options.spec:
1380 client = GClient('.', options)
1381 client.SetConfig(options.spec)
1382 else:
1383 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
1384 if not path:
1385 return None
1386 client = GClient(path, options)
1387 client.SetConfig(gclient_utils.FileRead(
1388 os.path.join(path, options.config_filename)))
szager@chromium.org7b8b6de2014-08-23 00:57:31 +00001389 client = client.MigrateConfigToGit(path, options)
maruel@chromium.org69392e72011-10-13 22:09:00 +00001390
1391 if (options.revisions and
1392 len(client.dependencies) > 1 and
1393 any('@' not in r for r in options.revisions)):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001394 print(
1395 ('You must specify the full solution name like --revision %s@%s\n'
1396 'when you have multiple solutions setup in your .gclient file.\n'
1397 'Other solutions present are: %s.') % (
maruel@chromium.org69392e72011-10-13 22:09:00 +00001398 client.dependencies[0].name,
1399 options.revisions[0],
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001400 ', '.join(s.name for s in client.dependencies[1:])),
1401 file=sys.stderr)
maruel@chromium.org15804092010-09-02 17:07:37 +00001402 return client
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001403
nsylvain@google.comefc80932011-05-31 21:27:56 +00001404 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001405 safesync_url, managed=True, cache_dir=None):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001406 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
1407 'solution_name': solution_name,
1408 'solution_url': solution_url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001409 'deps_file': deps_file,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001410 'safesync_url' : safesync_url,
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001411 'managed': managed,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001412 'cache_dir': cache_dir,
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001413 })
1414
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001415 def _SaveEntries(self):
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001416 """Creates a .gclient_entries file to record the list of unique checkouts.
1417
1418 The .gclient_entries file lives in the same directory as .gclient.
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001419 """
1420 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
1421 # makes testing a bit too fun.
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001422 result = 'entries = {\n'
maruel@chromium.org68988972011-09-20 14:11:42 +00001423 for entry in self.root.subtree(False):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001424 # Skip over File() dependencies as we can't version them.
1425 if not isinstance(entry.parsed_url, self.FileImpl):
1426 result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1427 pprint.pformat(entry.parsed_url))
1428 result += '}\n'
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001429 file_path = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org1333cb32011-10-04 23:40:16 +00001430 logging.debug(result)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001431 gclient_utils.FileWrite(file_path, result)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001432
1433 def _ReadEntries(self):
1434 """Read the .gclient_entries file for the given client.
1435
1436 Returns:
1437 A sequence of solution names, which will be empty if there is the
1438 entries file hasn't been created yet.
1439 """
1440 scope = {}
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001441 filename = os.path.join(self.root_dir, self._options.entries_filename)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001442 if not os.path.exists(filename):
maruel@chromium.org73e21142010-07-05 13:32:01 +00001443 return {}
maruel@chromium.org5990f9d2010-07-07 18:02:58 +00001444 try:
1445 exec(gclient_utils.FileRead(filename), scope)
1446 except SyntaxError, e:
1447 gclient_utils.SyntaxErrorToError(filename, e)
maruel@chromium.org9a66ddf2010-06-16 16:54:16 +00001448 return scope['entries']
1449
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001450 def _EnforceRevisions(self):
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001451 """Checks for revision overrides."""
1452 revision_overrides = {}
maruel@chromium.org307d1792010-05-31 20:03:13 +00001453 if self._options.head:
1454 return revision_overrides
joi@chromium.org792ea882010-11-10 02:37:27 +00001455 # Do not check safesync_url if one or more --revision flag is specified.
1456 if not self._options.revisions:
1457 for s in self.dependencies:
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001458 if not s.managed:
1459 self._options.revisions.append('%s@unmanaged' % s.name)
1460 elif s.safesync_url:
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001461 self._ApplySafeSyncRev(dep=s)
maruel@chromium.org307d1792010-05-31 20:03:13 +00001462 if not self._options.revisions:
1463 return revision_overrides
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001464 solutions_names = [s.name for s in self.dependencies]
maruel@chromium.org307d1792010-05-31 20:03:13 +00001465 index = 0
1466 for revision in self._options.revisions:
1467 if not '@' in revision:
1468 # Support for --revision 123
1469 revision = '%s@%s' % (solutions_names[index], revision)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001470 name, rev = revision.split('@', 1)
1471 revision_overrides[name] = rev
maruel@chromium.org307d1792010-05-31 20:03:13 +00001472 index += 1
maruel@chromium.org918a9ae2010-05-28 15:50:30 +00001473 return revision_overrides
1474
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001475 def _ApplySafeSyncRev(self, dep):
1476 """Finds a valid revision from the content of the safesync_url and apply it
1477 by appending revisions to the revision list. Throws if revision appears to
1478 be invalid for the given |dep|."""
1479 assert len(dep.safesync_url) > 0
1480 handle = urllib.urlopen(dep.safesync_url)
1481 rev = handle.read().strip()
1482 handle.close()
1483 if not rev:
1484 raise gclient_utils.Error(
1485 'It appears your safesync_url (%s) is not working properly\n'
1486 '(as it returned an empty response). Check your config.' %
1487 dep.safesync_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001488 scm = gclient_scm.CreateSCM(
1489 dep.url, dep.root.root_dir, dep.name, self.outbuf)
iannucci@chromium.org4a4b33b2013-07-04 20:25:46 +00001490 safe_rev = scm.GetUsableRev(rev, self._options)
dbeam@chromium.org051c88b2011-12-22 00:23:03 +00001491 if self._options.verbose:
1492 print('Using safesync_url revision: %s.\n' % safe_rev)
1493 self._options.revisions.append('%s@%s' % (dep.name, safe_rev))
1494
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001495 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001496 """Runs a command on each dependency in a client and its dependencies.
1497
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001498 Args:
1499 command: The command to use (e.g., 'status' or 'diff')
1500 args: list of str - extra arguments to add to the command line.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001501 """
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001502 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001503 raise gclient_utils.Error('No solution specified')
borenet@google.com0a427372014-04-02 19:12:13 +00001504
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001505 revision_overrides = {}
1506 # It's unnecessary to check for revision overrides for 'recurse'.
1507 # Save a few seconds by not calling _EnforceRevisions() in that case.
agable@chromium.org0242eb42015-06-09 00:45:31 +00001508 if command not in ('diff', 'recurse', 'runhooks', 'status', 'revert'):
szager@chromium.org5273b8a2014-08-21 15:10:10 +00001509 self._CheckConfig()
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001510 revision_overrides = self._EnforceRevisions()
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001511 pm = None
maruel@chromium.org5b3f8852010-09-10 16:49:54 +00001512 # Disable progress for non-tty stdout.
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001513 if (sys.stdout.isatty() and not self._options.verbose and progress):
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001514 if command in ('update', 'revert'):
1515 pm = Progress('Syncing projects', 1)
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +00001516 elif command == 'recurse':
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001517 pm = Progress(' '.join(args), 1)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001518 work_queue = gclient_utils.ExecutionQueue(
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001519 self._options.jobs, pm, ignore_requirements=ignore_requirements,
1520 verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001521 for s in self.dependencies:
1522 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001523 work_queue.flush(revision_overrides, command, args, options=self._options)
szager@chromium.org4ad264b2014-05-20 04:43:47 +00001524 if revision_overrides:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001525 print('Please fix your script, having invalid --revision flags will soon '
1526 'considered an error.', file=sys.stderr)
piman@chromium.org6f363722010-04-27 00:41:09 +00001527
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001528 # Once all the dependencies have been processed, it's now safe to run the
1529 # hooks.
1530 if not self._options.nohooks:
1531 self.RunHooksRecursively(self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001532
1533 if command == 'update':
ajwong@chromium.orgcdcee802009-06-23 15:30:42 +00001534 # Notify the user if there is an orphaned entry in their working copy.
1535 # Only delete the directory if there are no changes in it, and
1536 # delete_unversioned_trees is set to true.
maruel@chromium.org68988972011-09-20 14:11:42 +00001537 entries = [i.name for i in self.root.subtree(False) if i.url]
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001538 full_entries = [os.path.join(self.root_dir, e.replace('/', os.path.sep))
1539 for e in entries]
1540
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001541 for entry, prev_url in self._ReadEntries().iteritems():
maruel@chromium.org04dd7de2010-10-14 13:25:49 +00001542 if not prev_url:
1543 # entry must have been overridden via .gclient custom_deps
1544 continue
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001545 # Fix path separator on Windows.
1546 entry_fixed = entry.replace('/', os.path.sep)
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001547 e_dir = os.path.join(self.root_dir, entry_fixed)
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001548 # Use entry and not entry_fixed there.
jochen@chromium.orga78e5532013-03-11 13:33:03 +00001549 if (entry not in entries and
1550 (not any(path.startswith(entry + '/') for path in entries)) and
jochen@chromium.orgcc475722013-03-11 13:07:40 +00001551 os.path.exists(e_dir)):
primiano@chromium.org1c127382015-02-17 11:15:40 +00001552 # The entry has been removed from DEPS.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001553 scm = gclient_scm.CreateSCM(
1554 prev_url, self.root_dir, entry_fixed, self.outbuf)
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001555
1556 # Check to see if this directory is now part of a higher-up checkout.
borenet@google.com359bb642014-05-13 17:28:19 +00001557 # The directory might be part of a git OR svn checkout.
1558 scm_root = None
primiano@chromium.org1c127382015-02-17 11:15:40 +00001559 scm_class = None
borenet@google.com359bb642014-05-13 17:28:19 +00001560 for scm_class in (gclient_scm.scm.GIT, gclient_scm.scm.SVN):
1561 try:
1562 scm_root = scm_class.GetCheckoutRoot(scm.checkout_path)
1563 except subprocess2.CalledProcessError:
1564 pass
1565 if scm_root:
1566 break
1567 else:
1568 logging.warning('Could not find checkout root for %s. Unable to '
1569 'determine whether it is part of a higher-level '
1570 'checkout, so not removing.' % entry)
1571 continue
primiano@chromium.org1c127382015-02-17 11:15:40 +00001572
1573 # This is to handle the case of third_party/WebKit migrating from
1574 # being a DEPS entry to being part of the main project.
1575 # If the subproject is a Git project, we need to remove its .git
1576 # folder. Otherwise git operations on that folder will have different
1577 # effects depending on the current working directory.
1578 if scm_class == gclient_scm.scm.GIT and (
1579 os.path.abspath(scm_root) == os.path.abspath(e_dir)):
1580 e_par_dir = os.path.join(e_dir, os.pardir)
1581 if scm_class.IsInsideWorkTree(e_par_dir):
1582 par_scm_root = scm_class.GetCheckoutRoot(e_par_dir)
1583 # rel_e_dir : relative path of entry w.r.t. its parent repo.
1584 rel_e_dir = os.path.relpath(e_dir, par_scm_root)
1585 if scm_class.IsDirectoryVersioned(par_scm_root, rel_e_dir):
1586 save_dir = scm.GetGitBackupDirPath()
1587 # Remove any eventual stale backup dir for the same project.
1588 if os.path.exists(save_dir):
1589 gclient_utils.rmtree(save_dir)
1590 os.rename(os.path.join(e_dir, '.git'), save_dir)
1591 # When switching between the two states (entry/ is a subproject
1592 # -> entry/ is part of the outer project), it is very likely
1593 # that some files are changed in the checkout, unless we are
1594 # jumping *exactly* across the commit which changed just DEPS.
1595 # In such case we want to cleanup any eventual stale files
1596 # (coming from the old subproject) in order to end up with a
1597 # clean checkout.
1598 scm_class.CleanupDir(par_scm_root, rel_e_dir)
1599 assert not os.path.exists(os.path.join(e_dir, '.git'))
1600 print(('\nWARNING: \'%s\' has been moved from DEPS to a higher '
1601 'level checkout. The git folder containing all the local'
1602 ' branches has been saved to %s.\n'
1603 'If you don\'t care about its state you can safely '
1604 'remove that folder to free up space.') %
1605 (entry, save_dir))
1606 continue
1607
borenet@google.com359bb642014-05-13 17:28:19 +00001608 if scm_root in full_entries:
primiano@chromium.org1c127382015-02-17 11:15:40 +00001609 logging.info('%s is part of a higher level checkout, not removing',
1610 scm.GetCheckoutRoot())
xusydoc@chromium.org885a9602013-05-31 09:54:40 +00001611 continue
1612
1613 file_list = []
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001614 scm.status(self._options, [], file_list)
1615 modified_files = file_list != []
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00001616 if (not self._options.delete_unversioned_trees or
1617 (modified_files and not self._options.force)):
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001618 # There are modified files in this entry. Keep warning until
1619 # removed.
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001620 print(('\nWARNING: \'%s\' is no longer part of this client. '
1621 'It is recommended that you manually remove it.\n') %
maruel@chromium.orgc5e9aec2009-08-03 18:25:56 +00001622 entry_fixed)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001623 else:
1624 # Delete the entry
maruel@chromium.org73e21142010-07-05 13:32:01 +00001625 print('\n________ deleting \'%s\' in \'%s\'' % (
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001626 entry_fixed, self.root_dir))
digit@chromium.orgdc112ac2013-04-24 13:00:19 +00001627 gclient_utils.rmtree(e_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001628 # record the current list of entries for next time
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001629 self._SaveEntries()
maruel@chromium.org17cdf762010-05-28 17:30:52 +00001630 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001631
1632 def PrintRevInfo(self):
maruel@chromium.org54a07a22010-06-14 19:07:39 +00001633 if not self.dependencies:
maruel@chromium.org73e21142010-07-05 13:32:01 +00001634 raise gclient_utils.Error('No solution specified')
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001635 # Load all the settings.
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001636 work_queue = gclient_utils.ExecutionQueue(
1637 self._options.jobs, None, False, verbose=self._options.verbose)
maruel@chromium.org049bced2010-08-12 13:37:20 +00001638 for s in self.dependencies:
1639 work_queue.enqueue(s)
maruel@chromium.org3742c842010-09-09 19:27:14 +00001640 work_queue.flush({}, None, [], options=self._options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001641
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001642 def GetURLAndRev(dep):
1643 """Returns the revision-qualified SCM url for a Dependency."""
1644 if dep.parsed_url is None:
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001645 return None
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001646 if isinstance(dep.parsed_url, self.FileImpl):
1647 original_url = dep.parsed_url.file_location
1648 else:
1649 original_url = dep.parsed_url
nasser@codeaurora.org5d63eb82010-03-24 23:22:09 +00001650 url, _ = gclient_utils.SplitUrlRevision(original_url)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +00001651 scm = gclient_scm.CreateSCM(
1652 original_url, self.root_dir, dep.name, self.outbuf)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001653 if not os.path.isdir(scm.checkout_path):
1654 return None
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001655 return '%s@%s' % (url, scm.revinfo(self._options, [], None))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001656
maruel@chromium.orgbaa578e2010-07-12 17:36:59 +00001657 if self._options.snapshot:
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001658 new_gclient = ''
1659 # First level at .gclient
1660 for d in self.dependencies:
1661 entries = {}
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001662 def GrabDeps(dep):
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001663 """Recursively grab dependencies."""
maruel@chromium.org6da25d02010-08-11 17:32:55 +00001664 for d in dep.dependencies:
1665 entries[d.name] = GetURLAndRev(d)
1666 GrabDeps(d)
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001667 GrabDeps(d)
1668 custom_deps = []
1669 for k in sorted(entries.keys()):
1670 if entries[k]:
1671 # Quotes aren't escaped...
1672 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1673 else:
1674 custom_deps.append(' \"%s\": None,\n' % k)
1675 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1676 'solution_name': d.name,
1677 'solution_url': d.url,
nsylvain@google.comefc80932011-05-31 21:27:56 +00001678 'deps_file': d.deps_file,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001679 'safesync_url' : d.safesync_url or '',
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001680 'managed': d.managed,
maruel@chromium.orgdf2b3152010-07-21 17:35:24 +00001681 'solution_deps': ''.join(custom_deps),
1682 }
1683 # Print the snapshot configuration file
1684 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
nasser@codeaurora.orgde8f3522010-03-11 23:47:44 +00001685 else:
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001686 entries = {}
maruel@chromium.org68988972011-09-20 14:11:42 +00001687 for d in self.root.subtree(False):
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00001688 if self._options.actual:
1689 entries[d.name] = GetURLAndRev(d)
1690 else:
1691 entries[d.name] = d.parsed_url
1692 keys = sorted(entries.keys())
1693 for x in keys:
maruel@chromium.orgce464892010-08-12 17:12:18 +00001694 print('%s: %s' % (x, entries[x]))
maruel@chromium.orgdde32ee2010-08-10 17:44:05 +00001695 logging.info(str(self))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001696
maruel@chromium.orgf50907b2010-08-12 17:05:48 +00001697 def ParseDepsFile(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001698 """No DEPS to parse for a .gclient file."""
maruel@chromium.org049bced2010-08-12 13:37:20 +00001699 raise gclient_utils.Error('Internal error')
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001700
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001701 @property
maruel@chromium.org75a59272010-06-11 22:34:03 +00001702 def root_dir(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001703 """Root directory of gclient checkout."""
maruel@chromium.org75a59272010-06-11 22:34:03 +00001704 return self._root_dir
1705
maruel@chromium.orgd6db3d52011-09-20 14:00:45 +00001706 @property
maruel@chromium.org271375b2010-06-23 19:17:38 +00001707 def enforced_os(self):
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001708 """What deps_os entries that are to be parsed."""
maruel@chromium.org271375b2010-06-23 19:17:38 +00001709 return self._enforced_os
1710
maruel@chromium.org68988972011-09-20 14:11:42 +00001711 @property
maruel@chromium.orgd36fba82010-06-28 16:50:40 +00001712 def recursion_limit(self):
1713 """How recursive can each dependencies in DEPS file can load DEPS file."""
1714 return self._recursion_limit
1715
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001716 @property
cmp@chromium.orgc401ad12014-07-02 23:20:08 +00001717 def try_recursedeps(self):
1718 """Whether to attempt using recursedeps-style recursion processing."""
cmp@chromium.orge84ac912014-06-30 23:14:35 +00001719 return True
1720
1721 @property
sivachandra@chromium.orgd45e73e2012-10-24 23:42:48 +00001722 def target_os(self):
1723 return self._enforced_os
1724
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001725
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001726#### gclient commands.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001727
1728
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001729def CMDcleanup(parser, args):
1730 """Cleans up all working copies.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001731
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001732 Mostly svn-specific. Simply runs 'svn cleanup' for each module.
1733 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001734 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1735 help='override deps for the specified (comma-separated) '
1736 'platform(s); \'all\' will process all deps_os '
1737 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001738 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001739 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001740 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001741 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001742 if options.verbose:
1743 # Print out the .gclient file. This is longer than if we just printed the
1744 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001745 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001746 return client.RunOnDeps('cleanup', args)
1747
1748
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001749@subcommand.usage('[command] [args ...]')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001750def CMDrecurse(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001751 """Operates [command args ...] on all the dependencies.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001752
1753 Runs a shell command on all entries.
ilevy@chromium.org37116242012-11-28 01:32:48 +00001754 Sets GCLIENT_DEP_PATH enviroment variable as the dep's relative location to
1755 root directory of the checkout.
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001756 """
1757 # Stop parsing at the first non-arg so that these go through to the command
1758 parser.disable_interspersed_args()
1759 parser.add_option('-s', '--scm', action='append', default=[],
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001760 help='Choose scm types to operate upon.')
maruel@chromium.org288054d2012-03-05 00:43:07 +00001761 parser.add_option('-i', '--ignore', action='store_true',
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001762 help='Ignore non-zero return codes from subcommands.')
1763 parser.add_option('--prepend-dir', action='store_true',
1764 help='Prepend relative dir for use with git <cmd> --null.')
1765 parser.add_option('--no-progress', action='store_true',
1766 help='Disable progress bar that shows sub-command updates')
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001767 options, args = parser.parse_args(args)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001768 if not args:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001769 print('Need to supply a command!', file=sys.stderr)
maruel@chromium.org45e9f2d2010-10-18 13:33:46 +00001770 return 1
maruel@chromium.org78cba522010-10-18 13:32:05 +00001771 root_and_entries = gclient_utils.GetGClientRootAndEntries()
1772 if not root_and_entries:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001773 print(
maruel@chromium.org78cba522010-10-18 13:32:05 +00001774 'You need to run gclient sync at least once to use \'recurse\'.\n'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001775 'This is because .gclient_entries needs to exist and be up to date.',
1776 file=sys.stderr)
maruel@chromium.org78cba522010-10-18 13:32:05 +00001777 return 1
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001778
1779 # Normalize options.scm to a set()
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001780 scm_set = set()
1781 for scm in options.scm:
1782 scm_set.update(scm.split(','))
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001783 options.scm = scm_set
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001784
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001785 options.nohooks = True
1786 client = GClient.LoadCurrentConfig(options)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001787 return client.RunOnDeps('recurse', args, ignore_requirements=True,
1788 progress=not options.no_progress)
piman@chromium.org4b90e3a2010-07-01 20:28:26 +00001789
1790
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001791@subcommand.usage('[args ...]')
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001792def CMDfetch(parser, args):
1793 """Fetches upstream commits for all modules.
1794
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001795 Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
1796 """
davidbarr@chromium.org47ca0ee2012-03-02 16:06:11 +00001797 (options, args) = parser.parse_args(args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00001798 return CMDrecurse(OptionParser(), [
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001799 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
1800
1801
1802def CMDgrep(parser, args):
1803 """Greps through git repos managed by gclient.
1804
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001805 Runs 'git grep [args...]' for each module.
1806 """
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001807 # We can't use optparse because it will try to parse arguments sent
1808 # to git grep and throw an error. :-(
1809 if not args or re.match('(-h|--help)$', args[0]):
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001810 print(
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001811 'Usage: gclient grep [-j <N>] git-grep-args...\n\n'
1812 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep '
1813 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to '
1814 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the'
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00001815 ' end of your query.',
1816 file=sys.stderr)
ilevy@chromium.orgf2ed3fb2012-11-09 23:39:49 +00001817 return 1
1818
1819 jobs_arg = ['--jobs=1']
1820 if re.match(r'(-j|--jobs=)\d+$', args[0]):
1821 jobs_arg, args = args[:1], args[1:]
1822 elif re.match(r'(-j|--jobs)$', args[0]):
1823 jobs_arg, args = args[:2], args[2:]
1824
1825 return CMDrecurse(
1826 parser,
1827 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git',
1828 'git', 'grep', '--null', '--color=Always'] + args)
davidbarr@chromium.org12f944e2012-03-01 02:18:31 +00001829
1830
stip@chromium.orga735da22015-04-29 23:18:20 +00001831def CMDroot(parser, args):
1832 """Outputs the solution root (or current dir if there isn't one)."""
1833 (options, args) = parser.parse_args(args)
1834 client = GClient.LoadCurrentConfig(options)
1835 if client:
1836 print(os.path.abspath(client.root_dir))
1837 else:
1838 print(os.path.abspath('.'))
1839
1840
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001841@subcommand.usage('[url] [safesync url]')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001842def CMDconfig(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001843 """Creates a .gclient file in the current directory.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001844
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001845 This specifies the configuration for further commands. After update/sync,
1846 top-level DEPS files in each module are read to determine dependent
1847 modules to operate on as well. If optional [url] parameter is
1848 provided, then configuration is read from a specified Subversion server
1849 URL.
1850 """
szager@chromium.orge2e03202012-07-31 18:05:16 +00001851 # We do a little dance with the --gclientfile option. 'gclient config' is the
1852 # only command where it's acceptable to have both '--gclientfile' and '--spec'
1853 # arguments. So, we temporarily stash any --gclientfile parameter into
1854 # options.output_config_file until after the (gclientfile xor spec) error
1855 # check.
1856 parser.remove_option('--gclientfile')
1857 parser.add_option('--gclientfile', dest='output_config_file',
1858 help='Specify an alternate .gclient file')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001859 parser.add_option('--name',
1860 help='overrides the default name for the solution')
nsylvain@google.comefc80932011-05-31 21:27:56 +00001861 parser.add_option('--deps-file', default='DEPS',
1862 help='overrides the default name for the DEPS file for the'
1863 'main solutions and all sub-dependencies')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001864 parser.add_option('--unmanaged', action='store_true', default=False,
1865 help='overrides the default behavior to make it possible '
1866 'to have the main solution untouched by gclient '
1867 '(gclient will check out unmanaged dependencies but '
1868 'will never sync them)')
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001869 parser.add_option('--cache-dir',
1870 help='(git only) Cache all git repos into this dir and do '
1871 'shared clones from the cache, instead of cloning '
1872 'directly from the remote. (experimental)')
szager@chromium.orge2e03202012-07-31 18:05:16 +00001873 parser.set_defaults(config_filename=None)
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001874 (options, args) = parser.parse_args(args)
szager@chromium.orge2e03202012-07-31 18:05:16 +00001875 if options.output_config_file:
1876 setattr(options, 'config_filename', getattr(options, 'output_config_file'))
maruel@chromium.org5fc2a332010-05-26 19:37:15 +00001877 if ((options.spec and args) or len(args) > 2 or
1878 (not options.spec and not args)):
1879 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
1880
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001881 client = GClient('.', options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001882 if options.spec:
1883 client.SetConfig(options.spec)
1884 else:
maruel@chromium.org1ab7ffc2009-06-03 17:21:37 +00001885 base_url = args[0].rstrip('/')
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001886 if not options.name:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001887 name = base_url.split('/')[-1]
nsylvain@google.com12649ef2011-06-01 17:11:20 +00001888 if name.endswith('.git'):
1889 name = name[:-4]
iposva@chromium.org8cf7a392010-04-07 17:20:26 +00001890 else:
1891 # specify an alternate relpath for the given URL.
1892 name = options.name
agable@chromium.orgf2214672015-10-27 21:02:48 +00001893 if not os.path.abspath(os.path.join(os.getcwd(), name)).startswith(
1894 os.getcwd()):
1895 parser.error('Do not pass a relative path for --name.')
1896 if any(x in ('..', '.', '/', '\\') for x in name.split(os.sep)):
1897 parser.error('Do not include relative path components in --name.')
1898
nsylvain@google.comefc80932011-05-31 21:27:56 +00001899 deps_file = options.deps_file
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001900 safesync_url = ''
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001901 if len(args) > 1:
1902 safesync_url = args[1]
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +00001903 client.SetDefaultConfig(name, deps_file, base_url, safesync_url,
iannucci@chromium.org53456aa2013-07-03 19:38:34 +00001904 managed=not options.unmanaged,
1905 cache_dir=options.cache_dir)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001906 client.SaveConfig()
maruel@chromium.org79692d62010-05-14 18:57:13 +00001907 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001908
1909
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001910@subcommand.epilog("""Example:
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001911 gclient pack > patch.txt
1912 generate simple patch for configured client and dependences
1913""")
1914def CMDpack(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001915 """Generates a patch which can be applied at the root of the tree.
maruel@chromium.orgddff62d2010-05-17 21:02:36 +00001916
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001917 Internally, runs 'svn diff'/'git diff' on each checked out module and
1918 dependencies, and performs minimal postprocessing of the output. The
1919 resulting patch is printed to stdout and can be applied to a freshly
1920 checked out tree via 'patch -p0 < patchfile'.
1921 """
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001922 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1923 help='override deps for the specified (comma-separated) '
1924 'platform(s); \'all\' will process all deps_os '
1925 'references')
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001926 parser.remove_option('--jobs')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001927 (options, args) = parser.parse_args(args)
iannucci@chromium.org50395ea2013-04-04 04:47:42 +00001928 # Force jobs to 1 so the stdout is not annotated with the thread ids
haitao.feng@intel.com306080c2012-05-04 13:11:29 +00001929 options.jobs = 1
kbr@google.comab318592009-09-04 00:54:55 +00001930 client = GClient.LoadCurrentConfig(options)
1931 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001932 raise gclient_utils.Error('client not configured; see \'gclient config\'')
kbr@google.comab318592009-09-04 00:54:55 +00001933 if options.verbose:
1934 # Print out the .gclient file. This is longer than if we just printed the
1935 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001936 print(client.config_content)
kbr@google.comab318592009-09-04 00:54:55 +00001937 return client.RunOnDeps('pack', args)
1938
1939
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001940def CMDstatus(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001941 """Shows modification status for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001942 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
1943 help='override deps for the specified (comma-separated) '
1944 'platform(s); \'all\' will process all deps_os '
1945 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001946 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00001947 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001948 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001949 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001950 if options.verbose:
1951 # Print out the .gclient file. This is longer than if we just printed the
1952 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00001953 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00001954 return client.RunOnDeps('status', args)
1955
1956
maruel@chromium.org39c0b222013-08-17 16:57:01 +00001957@subcommand.epilog("""Examples:
maruel@chromium.org79692d62010-05-14 18:57:13 +00001958 gclient sync
1959 update files from SCM according to current configuration,
1960 *for modules which have changed since last update or sync*
1961 gclient sync --force
1962 update files from SCM according to current configuration, for
1963 all modules (useful for recovering files deleted from local copy)
1964 gclient sync --revision src@31000
1965 update src directory to r31000
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00001966
1967JSON output format:
1968If the --output-json option is specified, the following document structure will
1969be emitted to the provided file. 'null' entries may occur for subprojects which
1970are present in the gclient solution, but were not processed (due to custom_deps,
1971os_deps, etc.)
1972
1973{
1974 "solutions" : {
1975 "<name>": { # <name> is the posix-normalized path to the solution.
1976 "revision": [<svn rev int>|<git id hex string>|null],
1977 "scm": ["svn"|"git"|null],
1978 }
1979 }
1980}
maruel@chromium.org5ca27692010-05-26 19:32:41 +00001981""")
1982def CMDsync(parser, args):
1983 """Checkout/update all modules."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001984 parser.add_option('-f', '--force', action='store_true',
1985 help='force update even for unchanged modules')
1986 parser.add_option('-n', '--nohooks', action='store_true',
1987 help='don\'t run hooks after the update is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00001988 parser.add_option('-p', '--noprehooks', action='store_true',
1989 help='don\'t run pre-DEPS hooks', default=False)
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00001990 parser.add_option('-r', '--revision', action='append',
1991 dest='revisions', metavar='REV', default=[],
1992 help='Enforces revision/hash for the solutions with the '
1993 'format src@rev. The src@ part is optional and can be '
1994 'skipped. -r can be used multiple times when .gclient '
1995 'has multiple solutions configured and will work even '
joi@chromium.org792ea882010-11-10 02:37:27 +00001996 'if the src@ part is skipped. Note that specifying '
1997 '--revision means your safesync_url gets ignored.')
maruel@chromium.org794207e2013-03-08 15:29:43 +00001998 parser.add_option('--with_branch_heads', action='store_true',
1999 help='Clone git "branch_heads" refspecs in addition to '
2000 'the default refspecs. This adds about 1/2GB to a '
2001 'full checkout. (git only)')
szager@chromium.org8d3348f2014-08-19 22:49:16 +00002002 parser.add_option('--with_tags', action='store_true',
2003 help='Clone git tags in addition to the default refspecs.')
floitsch@google.comeaab7842011-04-28 09:07:58 +00002004 parser.add_option('-t', '--transitive', action='store_true',
2005 help='When a revision is specified (in the DEPS file or '
2006 'with the command-line flag), transitively update '
2007 'the dependencies to the date of the given revision. '
2008 'Only supported for SVN repositories.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002009 parser.add_option('-H', '--head', action='store_true',
2010 help='skips any safesync_urls specified in '
2011 'configured solutions and sync to head instead')
2012 parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002013 help='Deletes from the working copy any dependencies that '
2014 'have been removed since the last sync, as long as '
2015 'there are no local modifications. When used with '
2016 '--force, such dependencies are removed even if they '
2017 'have local modifications. When used with --reset, '
2018 'all untracked directories are removed from the '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002019 'working copy, excluding those which are explicitly '
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002020 'ignored in the repository.')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002021 parser.add_option('-R', '--reset', action='store_true',
2022 help='resets any local changes before updating (git only)')
bauerb@chromium.org2aad1b22011-07-22 12:00:41 +00002023 parser.add_option('-M', '--merge', action='store_true',
2024 help='merge upstream changes instead of trying to '
2025 'fast-forward or rebase')
dnj@chromium.org5b23e872015-02-20 21:25:57 +00002026 parser.add_option('-A', '--auto_rebase', action='store_true',
2027 help='Automatically rebase repositories against local '
2028 'checkout during update (git only).')
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002029 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2030 help='override deps for the specified (comma-separated) '
2031 'platform(s); \'all\' will process all deps_os '
2032 'references')
2033 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
2034 help='Skip svn up whenever possible by requesting '
2035 'actual HEAD revision from the repository')
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002036 parser.add_option('--upstream', action='store_true',
2037 help='Make repo state match upstream branch.')
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002038 parser.add_option('--output-json',
2039 help='Output a json document to this path containing '
2040 'summary information about the sync.')
primiano@chromium.org5439ea52014-08-06 17:18:18 +00002041 parser.add_option('--no-history', action='store_true',
2042 help='GIT ONLY - Reduces the size/time of the checkout at '
2043 'the cost of no history. Requires Git 1.9+')
hinoka@chromium.org46b87412014-05-15 00:42:05 +00002044 parser.add_option('--shallow', action='store_true',
2045 help='GIT ONLY - Do a shallow clone into the cache dir. '
2046 'Requires Git 1.9+')
e.hakkinen@samsung.come8bc1aa2015-04-08 08:00:37 +00002047 parser.add_option('--no_bootstrap', '--no-bootstrap',
2048 action='store_true',
2049 help='Don\'t bootstrap from Google Storage.')
hinoka@chromium.org8a10f6d2014-06-23 18:38:57 +00002050 parser.add_option('--ignore_locks', action='store_true',
2051 help='GIT ONLY - Ignore cache locks.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002052 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002053 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002054
2055 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002056 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002057
maruel@chromium.org307d1792010-05-31 20:03:13 +00002058 if options.revisions and options.head:
2059 # TODO(maruel): Make it a parser.error if it doesn't break any builder.
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002060 print('Warning: you cannot use both --head and --revision')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002061
2062 if options.verbose:
2063 # Print out the .gclient file. This is longer than if we just printed the
2064 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00002065 print(client.config_content)
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002066 ret = client.RunOnDeps('update', args)
2067 if options.output_json:
2068 slns = {}
2069 for d in client.subtree(True):
2070 normed = d.name.replace('\\', '/').rstrip('/') + '/'
2071 slns[normed] = {
2072 'revision': d.got_revision,
2073 'scm': d.used_scm.name if d.used_scm else None,
hinoka@chromium.org17db9052014-05-10 01:11:29 +00002074 'url': str(d.url) if d.url else None,
iannucci@chromium.org2702bcd2013-09-24 19:10:07 +00002075 }
2076 with open(options.output_json, 'wb') as f:
2077 json.dump({'solutions': slns}, f)
2078 return ret
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002079
2080
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002081CMDupdate = CMDsync
2082
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002083
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002084def CMDdiff(parser, args):
2085 """Displays local diff for every dependencies."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002086 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2087 help='override deps for the specified (comma-separated) '
2088 'platform(s); \'all\' will process all deps_os '
2089 'references')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002090 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002091 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002092 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002093 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002094 if options.verbose:
2095 # Print out the .gclient file. This is longer than if we just printed the
2096 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00002097 print(client.config_content)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002098 return client.RunOnDeps('diff', args)
2099
2100
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002101def CMDrevert(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002102 """Reverts all modifications in every dependencies.
maruel@chromium.org28d14bd2010-11-11 20:37:09 +00002103
2104 That's the nuclear option to get back to a 'clean' state. It removes anything
2105 that shows up in svn status."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002106 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2107 help='override deps for the specified (comma-separated) '
2108 'platform(s); \'all\' will process all deps_os '
2109 'references')
2110 parser.add_option('-n', '--nohooks', action='store_true',
2111 help='don\'t run hooks after the revert is complete')
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002112 parser.add_option('-p', '--noprehooks', action='store_true',
2113 help='don\'t run pre-DEPS hooks', default=False)
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +00002114 parser.add_option('--upstream', action='store_true',
2115 help='Make repo state match upstream branch.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002116 (options, args) = parser.parse_args(args)
2117 # --force is implied.
2118 options.force = True
steveblock@chromium.org98e69452012-02-16 16:36:43 +00002119 options.reset = False
2120 options.delete_unversioned_trees = False
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002121 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002122 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002123 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002124 return client.RunOnDeps('revert', args)
2125
2126
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002127def CMDrunhooks(parser, args):
2128 """Runs hooks for files that have been modified in the local working copy."""
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002129 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2130 help='override deps for the specified (comma-separated) '
2131 'platform(s); \'all\' will process all deps_os '
2132 'references')
2133 parser.add_option('-f', '--force', action='store_true', default=True,
2134 help='Deprecated. No effect.')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002135 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002136 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002137 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002138 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002139 if options.verbose:
2140 # Print out the .gclient file. This is longer than if we just printed the
2141 # client dict, but more legible, and it might contain helpful comments.
maruel@chromium.org116704f2010-06-11 17:34:38 +00002142 print(client.config_content)
maruel@chromium.org5df6a462009-08-28 18:52:26 +00002143 options.force = True
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002144 options.nohooks = False
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002145 return client.RunOnDeps('runhooks', args)
2146
2147
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002148def CMDrevinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002149 """Outputs revision info mapping for the client and its dependencies.
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002150
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002151 This allows the capture of an overall 'revision' for the source tree that
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002152 can be used to reproduce the same tree in the future. It is only useful for
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002153 'unpinned dependencies', i.e. DEPS/deps references without a svn revision
2154 number or a git hash. A git branch name isn't 'pinned' since the actual
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002155 commit can change.
2156 """
2157 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
2158 help='override deps for the specified (comma-separated) '
2159 'platform(s); \'all\' will process all deps_os '
2160 'references')
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002161 parser.add_option('-a', '--actual', action='store_true',
2162 help='gets the actual checked out revisions instead of the '
2163 'ones specified in the DEPS and .gclient files')
maruel@chromium.org9eda4112010-06-11 18:56:10 +00002164 parser.add_option('-s', '--snapshot', action='store_true',
2165 help='creates a snapshot .gclient file of the current '
maruel@chromium.orgb1e315f2010-08-11 18:44:50 +00002166 'version of all repositories to reproduce the tree, '
2167 'implies -a')
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002168 (options, args) = parser.parse_args(args)
maruel@chromium.org2806acc2009-05-15 12:33:34 +00002169 client = GClient.LoadCurrentConfig(options)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002170 if not client:
maruel@chromium.org0b6a0842010-06-15 14:34:19 +00002171 raise gclient_utils.Error('client not configured; see \'gclient config\'')
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002172 client.PrintRevInfo()
maruel@chromium.org79692d62010-05-14 18:57:13 +00002173 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002174
2175
szager@google.comb9a78d32012-03-13 18:46:21 +00002176def CMDhookinfo(parser, args):
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002177 """Outputs the hooks that would be run by `gclient runhooks`."""
szager@google.comb9a78d32012-03-13 18:46:21 +00002178 (options, args) = parser.parse_args(args)
2179 options.force = True
2180 client = GClient.LoadCurrentConfig(options)
2181 if not client:
2182 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2183 client.RunOnDeps(None, [])
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002184 print('; '.join(' '.join(hook) for hook in client.GetHooks(options)))
szager@google.comb9a78d32012-03-13 18:46:21 +00002185 return 0
2186
2187
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002188def CMDverify(parser, args):
2189 """Verifies the DEPS file deps are only from allowed_hosts."""
2190 (options, args) = parser.parse_args(args)
2191 client = GClient.LoadCurrentConfig(options)
2192 if not client:
2193 raise gclient_utils.Error('client not configured; see \'gclient config\'')
2194 client.RunOnDeps(None, [])
2195 # Look at each first-level dependency of this gclient only.
2196 for dep in client.dependencies:
2197 bad_deps = dep.findDepsFromNotAllowedHosts()
2198 if not bad_deps:
2199 continue
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002200 print("There are deps from not allowed hosts in file %s" % dep.deps_file)
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002201 for bad_dep in bad_deps:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002202 print("\t%s at %s" % (bad_dep.name, bad_dep.url))
2203 print("allowed_hosts:", ', '.join(dep.allowed_hosts))
tandrii@chromium.orgc137c1a2014-09-23 11:49:52 +00002204 sys.stdout.flush()
2205 raise gclient_utils.Error(
2206 'dependencies from disallowed hosts; check your DEPS file.')
2207 return 0
2208
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002209class OptionParser(optparse.OptionParser):
szager@chromium.orge2e03202012-07-31 18:05:16 +00002210 gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002211
2212 def __init__(self, **kwargs):
2213 optparse.OptionParser.__init__(
2214 self, version='%prog ' + __version__, **kwargs)
2215
2216 # Some arm boards have issues with parallel sync.
2217 if platform.machine().startswith('arm'):
2218 jobs = 1
2219 else:
2220 jobs = max(8, gclient_utils.NumLocalCpus())
2221 # cmp: 2013/06/19
2222 # Temporary workaround to lower bot-load on SVN server.
hinoka@google.com267f33e2014-02-28 22:02:32 +00002223 # Bypassed if a bot_update flag is detected.
2224 if (os.environ.get('CHROME_HEADLESS') == '1' and
2225 not os.path.exists('update.flag')):
xusydoc@chromium.org05028412013-07-29 13:40:10 +00002226 jobs = 1
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002227
2228 self.add_option(
2229 '-j', '--jobs', default=jobs, type='int',
2230 help='Specify how many SCM commands can run in parallel; defaults to '
tnagel@chromium.orga2aaa632014-02-28 21:47:27 +00002231 '%default on this machine')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002232 self.add_option(
2233 '-v', '--verbose', action='count', default=0,
2234 help='Produces additional output for diagnostics. Can be used up to '
2235 'three times for more logging info.')
2236 self.add_option(
2237 '--gclientfile', dest='config_filename',
2238 help='Specify an alternate %s file' % self.gclientfile_default)
2239 self.add_option(
2240 '--spec',
2241 help='create a gclient file containing the provided string. Due to '
2242 'Cygwin/Python brokenness, it can\'t contain any newlines.')
2243 self.add_option(
2244 '--no-nag-max', default=False, action='store_true',
scottmg@chromium.orgf547c802013-09-27 17:55:26 +00002245 help='Ignored for backwards compatibility.')
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002246
2247 def parse_args(self, args=None, values=None):
2248 """Integrates standard options processing."""
2249 options, args = optparse.OptionParser.parse_args(self, args, values)
2250 levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
2251 logging.basicConfig(
2252 level=levels[min(options.verbose, len(levels) - 1)],
maruel@chromium.org0895b752011-08-26 20:40:33 +00002253 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002254 if options.config_filename and options.spec:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002255 self.error('Cannot specifiy both --gclientfile and --spec')
rdsmith@chromium.orgd9591f02014-02-05 19:28:20 +00002256 if (options.config_filename and
2257 options.config_filename != os.path.basename(options.config_filename)):
2258 self.error('--gclientfile target must be a filename, not a path')
szager@chromium.orge2e03202012-07-31 18:05:16 +00002259 if not options.config_filename:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002260 options.config_filename = self.gclientfile_default
maruel@chromium.org0895b752011-08-26 20:40:33 +00002261 options.entries_filename = options.config_filename + '_entries'
2262 if options.jobs < 1:
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002263 self.error('--jobs must be 1 or higher')
maruel@chromium.org0895b752011-08-26 20:40:33 +00002264
2265 # These hacks need to die.
2266 if not hasattr(options, 'revisions'):
2267 # GClient.RunOnDeps expects it even if not applicable.
2268 options.revisions = []
2269 if not hasattr(options, 'head'):
2270 options.head = None
2271 if not hasattr(options, 'nohooks'):
2272 options.nohooks = True
borenet@google.com2d1ee9e2013-10-15 08:13:16 +00002273 if not hasattr(options, 'noprehooks'):
2274 options.noprehooks = True
maruel@chromium.org0895b752011-08-26 20:40:33 +00002275 if not hasattr(options, 'deps_os'):
2276 options.deps_os = None
2277 if not hasattr(options, 'manually_grab_svn_rev'):
2278 options.manually_grab_svn_rev = None
2279 if not hasattr(options, 'force'):
2280 options.force = None
2281 return (options, args)
iannucci@chromium.orgd9c1b202013-07-24 23:52:11 +00002282
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002283
2284def disable_buffering():
2285 # Make stdout auto-flush so buildbot doesn't kill us during lengthy
2286 # operations. Python as a strong tendency to buffer sys.stdout.
2287 sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
2288 # Make stdout annotated with the thread ids.
2289 sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
maruel@chromium.org0895b752011-08-26 20:40:33 +00002290
2291
sbc@chromium.org013731e2015-02-26 18:28:43 +00002292def main(argv):
maruel@chromium.org5ca27692010-05-26 19:32:41 +00002293 """Doesn't parse the arguments here, just find the right subcommand to
2294 execute."""
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002295 if sys.hexversion < 0x02060000:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002296 print(
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002297 '\nYour python version %s is unsupported, please upgrade.\n' %
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002298 sys.version.split(' ', 1)[0],
2299 file=sys.stderr)
maruel@chromium.org82798cb2012-02-23 18:16:12 +00002300 return 2
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002301 if not sys.executable:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002302 print(
2303 '\nPython cannot find the location of it\'s own executable.\n',
2304 file=sys.stderr)
bcwhite@chromium.org6683ab42013-02-11 16:13:47 +00002305 return 2
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002306 fix_encoding.fix_encoding()
2307 disable_buffering()
maruel@chromium.orgda78c6f2011-10-23 00:13:58 +00002308 colorama.init()
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002309 dispatcher = subcommand.CommandDispatcher(__name__)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002310 try:
maruel@chromium.org39c0b222013-08-17 16:57:01 +00002311 return dispatcher.execute(OptionParser(), argv)
xusydoc@chromium.org2fd6c3f2013-05-03 21:57:55 +00002312 except KeyboardInterrupt:
2313 gclient_utils.GClientChildren.KillAllRemainingChildren()
2314 raise
maruel@chromium.org31cb48a2011-04-04 18:01:36 +00002315 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
vapier@chromium.orgbb79bea2015-11-11 07:30:23 +00002316 print('Error: %s' % str(e), file=sys.stderr)
maruel@chromium.org6e29d572010-06-04 17:32:20 +00002317 return 1
borenet@google.com6a9b1682014-03-24 18:35:23 +00002318 finally:
2319 gclient_utils.PrintWarnings()
sbc@chromium.org013731e2015-02-26 18:28:43 +00002320 return 0
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002321
2322
maruel@chromium.orgf0fc9912010-06-11 17:57:33 +00002323if '__main__' == __name__:
sbc@chromium.org013731e2015-02-26 18:28:43 +00002324 try:
2325 sys.exit(main(sys.argv[1:]))
2326 except KeyboardInterrupt:
2327 sys.stderr.write('interrupted\n')
2328 sys.exit(1)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002329
2330# vim: ts=2:sw=2:tw=80:et: