libv8 6.3.292.48.1 → 6.7.288.46.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +10 -34
  3. data/CHANGELOG.md +16 -0
  4. data/README.md +9 -63
  5. data/Rakefile +2 -2
  6. data/ext/libv8/builder.rb +22 -87
  7. data/ext/libv8/extconf.rb +1 -1
  8. data/ext/libv8/paths.rb +5 -18
  9. data/lib/libv8/version.rb +1 -1
  10. data/spec/location_spec.rb +1 -2
  11. data/spec/spec_helper.rb +0 -1
  12. data/vendor/depot_tools/.gitattributes +1 -2
  13. data/vendor/depot_tools/OWNERS +0 -1
  14. data/vendor/depot_tools/PRESUBMIT.py +11 -6
  15. data/vendor/depot_tools/README.md +0 -1
  16. data/vendor/depot_tools/WATCHLISTS +0 -6
  17. data/vendor/depot_tools/auth.py +129 -87
  18. data/vendor/depot_tools/autoninja +11 -1
  19. data/vendor/depot_tools/autoninja.bat +7 -1
  20. data/vendor/depot_tools/autoninja.py +14 -6
  21. data/vendor/depot_tools/bootstrap/win/manifest.txt +1 -1
  22. data/vendor/depot_tools/bootstrap/win/manifest_bleeding_edge.txt +1 -1
  23. data/vendor/depot_tools/cipd +23 -2
  24. data/vendor/depot_tools/cipd.bat +2 -2
  25. data/vendor/depot_tools/cipd_client_version +1 -1
  26. data/vendor/depot_tools/cipd_manifest.txt +17 -7
  27. data/vendor/depot_tools/cit.py +7 -6
  28. data/vendor/depot_tools/cpplint.py +195 -35
  29. data/vendor/depot_tools/detect_host_arch.py +51 -0
  30. data/vendor/depot_tools/download_from_google_storage.py +85 -26
  31. data/vendor/depot_tools/fetch.py +11 -6
  32. data/vendor/depot_tools/fetch_configs/chromium.py +0 -1
  33. data/vendor/depot_tools/fetch_configs/goma_client.py +41 -0
  34. data/vendor/depot_tools/fetch_configs/infra.py +0 -1
  35. data/vendor/depot_tools/fetch_configs/infra_internal.py +0 -1
  36. data/vendor/depot_tools/gclient-new-workdir.py +4 -0
  37. data/vendor/depot_tools/gclient.py +732 -476
  38. data/vendor/depot_tools/gclient_eval.py +569 -58
  39. data/vendor/depot_tools/gclient_scm.py +258 -46
  40. data/vendor/depot_tools/gclient_utils.py +17 -1
  41. data/vendor/depot_tools/gerrit_util.py +46 -13
  42. data/vendor/depot_tools/git_cache.py +0 -2
  43. data/vendor/depot_tools/git_cl.py +176 -335
  44. data/vendor/depot_tools/git_common.py +19 -16
  45. data/vendor/depot_tools/git_footers.py +19 -5
  46. data/vendor/depot_tools/git_hyper_blame.py +9 -3
  47. data/vendor/depot_tools/git_new_branch.py +15 -3
  48. data/vendor/depot_tools/git_upstream_diff.py +7 -2
  49. data/vendor/depot_tools/gsutil.py +1 -1
  50. data/vendor/depot_tools/infra/config/cq.cfg +1 -2
  51. data/vendor/depot_tools/infra/config/recipes.cfg +1 -1
  52. data/vendor/depot_tools/luci-auth +13 -0
  53. data/vendor/depot_tools/luci-auth.bat +8 -0
  54. data/vendor/depot_tools/man/html/depot_tools.html +0 -8
  55. data/vendor/depot_tools/man/html/git-upstream-diff.html +20 -3
  56. data/vendor/depot_tools/man/man1/git-upstream-diff.1 +27 -6
  57. data/vendor/depot_tools/man/man7/depot_tools.7 +0 -5
  58. data/vendor/depot_tools/man/man7/depot_tools_tutorial.7 +2 -2
  59. data/vendor/depot_tools/man/src/git-upstream-diff.txt +21 -3
  60. data/vendor/depot_tools/man/src/make_docs.sh +6 -0
  61. data/vendor/depot_tools/my_activity.py +283 -93
  62. data/vendor/depot_tools/owners.py +9 -4
  63. data/vendor/depot_tools/owners_finder.py +7 -3
  64. data/vendor/depot_tools/post_build_ninja_summary.py +322 -0
  65. data/vendor/depot_tools/presubmit_canned_checks.py +91 -106
  66. data/vendor/depot_tools/presubmit_support.py +219 -157
  67. data/vendor/depot_tools/prpc +13 -0
  68. data/vendor/depot_tools/prpc.bat +8 -0
  69. data/vendor/depot_tools/recipes/OWNERS +3 -1
  70. data/vendor/depot_tools/recipes/README.recipes.md +70 -111
  71. data/vendor/depot_tools/recipes/recipe_modules/bot_update/__init__.py +12 -5
  72. data/vendor/depot_tools/recipes/recipe_modules/bot_update/api.py +36 -68
  73. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/deprecated_got_revision_mapping.json +0 -8
  74. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/{trychange_oauth2_json.json → no_apply_patch_on_gclient.json} +64 -10
  75. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/{no_shallow.json → shallow.json} +1 -1
  76. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob.json +0 -8
  77. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_empty_revision.json +0 -8
  78. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail.json +0 -6
  79. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail_patch.json +0 -7
  80. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail_patch_download.json +0 -6
  81. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_angle_deprecated.json +44 -0
  82. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/{trychange_oauth2_buildbot.json → tryjob_gerrit_branch_heads.json} +51 -5
  83. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_v8.json +0 -8
  84. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_v8_head_by_default.json +48 -8
  85. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.py +19 -26
  86. data/vendor/depot_tools/recipes/recipe_modules/bot_update/resources/bot_update.py +193 -155
  87. data/vendor/depot_tools/recipes/recipe_modules/bot_update/test_api.py +9 -0
  88. data/vendor/depot_tools/recipes/recipe_modules/gclient/api.py +2 -7
  89. data/vendor/depot_tools/recipes/recipe_modules/gclient/config.py +31 -5
  90. data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/basic.json +37 -19
  91. data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/buildbot.json +37 -19
  92. data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/revision.json +37 -19
  93. data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/tryserver.json +37 -23
  94. data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.py +4 -0
  95. data/vendor/depot_tools/recipes/recipe_modules/gerrit/api.py +40 -8
  96. data/vendor/depot_tools/recipes/recipe_modules/gerrit/examples/full.expected/basic.json +3 -3
  97. data/vendor/depot_tools/recipes/recipe_modules/gerrit/examples/full.py +6 -3
  98. data/vendor/depot_tools/recipes/recipe_modules/gitiles/OWNERS +0 -1
  99. data/vendor/depot_tools/recipes/recipe_modules/tryserver/__init__.py +0 -1
  100. data/vendor/depot_tools/recipes/recipe_modules/tryserver/api.py +7 -56
  101. data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_wrong_patch.json +0 -1
  102. data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.py +15 -16
  103. data/vendor/depot_tools/recipes/recipes.py +4 -2
  104. data/vendor/depot_tools/recipes/trigger_recipe_roller.txt +12 -0
  105. data/vendor/depot_tools/roll_dep.py +35 -37
  106. data/vendor/depot_tools/support/chromite_wrapper +1 -1
  107. data/vendor/depot_tools/third_party/logilab/astroid/README.chromium +3 -3
  108. data/vendor/depot_tools/third_party/logilab/astroid/__pkginfo__.py +2 -2
  109. data/vendor/depot_tools/third_party/logilab/astroid/astpeephole.py +86 -0
  110. data/vendor/depot_tools/third_party/logilab/astroid/bases.py +53 -66
  111. data/vendor/depot_tools/third_party/logilab/astroid/brain/py2pytest.py +31 -31
  112. data/vendor/depot_tools/third_party/logilab/astroid/brain/pynose.py +39 -16
  113. data/vendor/depot_tools/third_party/logilab/astroid/brain/pysix_moves.py +225 -189
  114. data/vendor/depot_tools/third_party/logilab/astroid/inference.py +45 -41
  115. data/vendor/depot_tools/third_party/logilab/astroid/manager.py +1 -0
  116. data/vendor/depot_tools/third_party/logilab/astroid/modutils.py +2 -2
  117. data/vendor/depot_tools/third_party/logilab/astroid/node_classes.py +3 -2
  118. data/vendor/depot_tools/third_party/logilab/astroid/nodes.py +1 -0
  119. data/vendor/depot_tools/third_party/logilab/astroid/protocols.py +57 -3
  120. data/vendor/depot_tools/third_party/logilab/astroid/raw_building.py +1 -1
  121. data/vendor/depot_tools/third_party/logilab/astroid/rebuilder.py +21 -1
  122. data/vendor/depot_tools/third_party/logilab/astroid/scoped_nodes.py +58 -33
  123. data/vendor/depot_tools/third_party/pylint/README.chromium +2 -2
  124. data/vendor/depot_tools/third_party/pylint/__pkginfo__.py +3 -3
  125. data/vendor/depot_tools/third_party/pylint/checkers/base.py +6 -18
  126. data/vendor/depot_tools/third_party/pylint/checkers/classes.py +64 -63
  127. data/vendor/depot_tools/third_party/pylint/checkers/design_analysis.py +25 -57
  128. data/vendor/depot_tools/third_party/pylint/checkers/format.py +14 -10
  129. data/vendor/depot_tools/third_party/pylint/checkers/python3.py +142 -37
  130. data/vendor/depot_tools/third_party/pylint/checkers/spelling.py +10 -1
  131. data/vendor/depot_tools/third_party/pylint/checkers/stdlib.py +50 -7
  132. data/vendor/depot_tools/third_party/pylint/checkers/strings.py +1 -1
  133. data/vendor/depot_tools/third_party/pylint/epylint.py +2 -1
  134. data/vendor/depot_tools/third_party/pylint/gui.py +1 -1
  135. data/vendor/depot_tools/third_party/pylint/lint.py +88 -23
  136. data/vendor/depot_tools/third_party/pylint/reporters/html.py +37 -5
  137. data/vendor/depot_tools/third_party/pylint/testutils.py +1 -1
  138. data/vendor/depot_tools/third_party/pylint/utils.py +5 -0
  139. data/vendor/depot_tools/vpython +31 -1
  140. data/vendor/depot_tools/win_toolchain/get_toolchain_if_necessary.py +35 -2
  141. data/vendor/depot_tools/win_toolchain/package_from_installed.py +0 -15
  142. data/vendor/depot_tools/yapf +17 -0
  143. data/vendor/depot_tools/{apply_issue.bat → yapf.bat} +2 -2
  144. metadata +16 -58
  145. data/ext/libv8/compiler.rb +0 -39
  146. data/ext/libv8/compiler/apple_llvm.rb +0 -22
  147. data/ext/libv8/compiler/clang.rb +0 -22
  148. data/ext/libv8/compiler/gcc.rb +0 -22
  149. data/ext/libv8/compiler/generic_compiler.rb +0 -66
  150. data/ext/libv8/make.rb +0 -13
  151. data/ext/libv8/patcher.rb +0 -21
  152. data/patches/0001-Build-a-standalone-static-library.patch +0 -26
  153. data/patches/0002-Don-t-compile-unnecessary-stuff.patch +0 -85
  154. data/patches/0003-Use-the-fPIC-flag-for-the-static-library.patch +0 -25
  155. data/patches/0004-Do-not-embed-debug-symbols-in-macOS-libraries.patch +0 -25
  156. data/patches/0005-Remove-TryInstallOptimizedCode.patch +0 -321
  157. data/patches/mingw-generate-makefiles.sh +0 -97
  158. data/spec/compiler/apple_llvm_spec.rb +0 -37
  159. data/spec/compiler/clang_spec.rb +0 -37
  160. data/spec/compiler/gcc_spec.rb +0 -37
  161. data/spec/compiler/generic_compiler_spec.rb +0 -50
  162. data/spec/compiler_spec.rb +0 -45
  163. data/spec/support/compiler_helpers.rb +0 -47
  164. data/vendor/depot_tools/apply_issue +0 -8
  165. data/vendor/depot_tools/apply_issue.py +0 -315
  166. data/vendor/depot_tools/man/html/git-cherry-pick-upload.html +0 -815
  167. data/vendor/depot_tools/man/man1/git-cherry-pick-upload.1 +0 -80
  168. data/vendor/depot_tools/man/src/_git-cherry-pick-upload_desc.helper.txt +0 -1
  169. data/vendor/depot_tools/man/src/git-cherry-pick-upload.demo.1.sh +0 -17
  170. data/vendor/depot_tools/man/src/git-cherry-pick-upload.txt +0 -35
  171. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/trychange_oauth2.json +0 -8
  172. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/trychange_oauth2_json_win.json +0 -196
  173. data/vendor/depot_tools/recipes/recipe_modules/rietveld/__init__.py +0 -6
  174. data/vendor/depot_tools/recipes/recipe_modules/rietveld/api.py +0 -97
  175. data/vendor/depot_tools/recipes/recipe_modules/rietveld/examples/full.expected/basic.json +0 -8
  176. data/vendor/depot_tools/recipes/recipe_modules/rietveld/examples/full.expected/buildbot.json +0 -30
  177. data/vendor/depot_tools/recipes/recipe_modules/rietveld/examples/full.expected/no_auth.json +0 -27
  178. data/vendor/depot_tools/recipes/recipe_modules/rietveld/examples/full.py +0 -38
  179. data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_rietveld_patch.json +0 -69
  180. data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_rietveld_patch_new.json +0 -69
  181. data/vendor/depot_tools/third_party/cq_client/OWNERS +0 -2
  182. data/vendor/depot_tools/third_party/cq_client/README.depot_tools.md +0 -2
  183. data/vendor/depot_tools/third_party/cq_client/README.md +0 -59
  184. data/vendor/depot_tools/third_party/cq_client/__init__.py +0 -3
  185. data/vendor/depot_tools/third_party/cq_client/v1/__init__.py +0 -3
  186. data/vendor/depot_tools/third_party/cq_client/v1/cq.pb.go +0 -810
  187. data/vendor/depot_tools/third_party/cq_client/v1/cq.proto +0 -281
  188. data/vendor/depot_tools/third_party/cq_client/v1/cq_pb2.py +0 -794
  189. data/vendor/depot_tools/third_party/cq_client/v1/testdata/cq_both.cfg +0 -71
  190. data/vendor/depot_tools/third_party/cq_client/v1/testdata/cq_gerrit.cfg +0 -58
  191. data/vendor/depot_tools/third_party/cq_client/v1/testdata/cq_rietveld.cfg +0 -60
  192. data/vendor/depot_tools/third_party/cq_client/v2/__init__.py +0 -3
  193. data/vendor/depot_tools/third_party/cq_client/v2/cq.pb.go +0 -792
  194. data/vendor/depot_tools/third_party/cq_client/v2/cq.proto +0 -270
  195. data/vendor/depot_tools/third_party/cq_client/v2/cq_pb2.py +0 -841
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env python
2
+ # Copyright 2014 The Chromium Authors. All rights reserved.
3
+ # Use of this source code is governed by a BSD-style license that can be
4
+ # found in the LICENSE file.
5
+
6
+ """Outputs host CPU architecture in format recognized by gyp."""
7
+
8
+ import platform
9
+ import re
10
+ import sys
11
+
12
+
13
+ def HostArch():
14
+ """Returns the host architecture with a predictable string."""
15
+ host_arch = platform.machine()
16
+
17
+ # Convert machine type to format recognized by gyp.
18
+ if re.match(r'i.86', host_arch) or host_arch == 'i86pc':
19
+ host_arch = 'x86'
20
+ elif host_arch in ['x86_64', 'amd64']:
21
+ host_arch = 'x64'
22
+ elif host_arch.startswith('arm'):
23
+ host_arch = 'arm'
24
+ elif host_arch.startswith('aarch64'):
25
+ host_arch = 'arm64'
26
+ elif host_arch.startswith('mips'):
27
+ host_arch = 'mips'
28
+ elif host_arch.startswith('ppc'):
29
+ host_arch = 'ppc'
30
+ elif host_arch.startswith('s390'):
31
+ host_arch = 's390'
32
+
33
+
34
+ # platform.machine is based on running kernel. It's possible to use 64-bit
35
+ # kernel with 32-bit userland, e.g. to give linker slightly more memory.
36
+ # Distinguish between different userland bitness by querying
37
+ # the python binary.
38
+ if host_arch == 'x64' and platform.architecture()[0] == '32bit':
39
+ host_arch = 'x86'
40
+ if host_arch == 'arm64' and platform.architecture()[0] == '32bit':
41
+ host_arch = 'arm'
42
+
43
+ return host_arch
44
+
45
+ def DoMain(_):
46
+ """Hook to be called from gyp without starting a separate python
47
+ interpreter."""
48
+ return HostArch()
49
+
50
+ if __name__ == '__main__':
51
+ print DoMain([])
@@ -21,6 +21,11 @@ import time
21
21
  import subprocess2
22
22
 
23
23
 
24
+ # Env vars that tempdir can be gotten from; minimally, this
25
+ # needs to match python's tempfile module and match normal
26
+ # unix standards.
27
+ _TEMPDIR_ENV_VARS = ('TMPDIR', 'TEMP', 'TMP')
28
+
24
29
  GSUTIL_DEFAULT_PATH = os.path.join(
25
30
  os.path.dirname(os.path.abspath(__file__)), 'gsutil.py')
26
31
  # Maps sys.platform to what we actually want to call them.
@@ -29,6 +34,8 @@ PLATFORM_MAPPING = {
29
34
  'darwin': 'mac',
30
35
  'linux2': 'linux',
31
36
  'win32': 'win',
37
+ 'aix6': 'aix',
38
+ 'aix7': 'aix',
32
39
  }
33
40
 
34
41
 
@@ -54,13 +61,18 @@ def GetNormalizedPlatform():
54
61
  # Common utilities
55
62
  class Gsutil(object):
56
63
  """Call gsutil with some predefined settings. This is a convenience object,
57
- and is also immutable."""
64
+ and is also immutable.
65
+
66
+ HACK: This object is used directly by the external script
67
+ `<depot_tools>/win_toolchain/get_toolchain_if_necessary.py`
68
+ """
58
69
 
59
70
  MAX_TRIES = 5
60
71
  RETRY_BASE_DELAY = 5.0
61
72
  RETRY_DELAY_MULTIPLE = 1.3
73
+ VPYTHON = 'vpython.bat' if GetNormalizedPlatform() == 'win32' else 'vpython'
62
74
 
63
- def __init__(self, path, boto_path=None, timeout=None, version='4.26'):
75
+ def __init__(self, path, boto_path=None, timeout=None, version='4.28'):
64
76
  if not os.path.exists(path):
65
77
  raise FileNotFoundError('GSUtil not found in %s' % path)
66
78
  self.path = path
@@ -77,15 +89,18 @@ class Gsutil(object):
77
89
  env['AWS_CREDENTIAL_FILE'] = self.boto_path
78
90
  env['BOTO_CONFIG'] = self.boto_path
79
91
 
92
+ if PLATFORM_MAPPING[sys.platform] != 'win':
93
+ env.update((x, "/tmp") for x in _TEMPDIR_ENV_VARS)
94
+
80
95
  return env
81
96
 
82
97
  def call(self, *args):
83
- cmd = [sys.executable, self.path, '--force-version', self.version]
98
+ cmd = [self.VPYTHON, self.path, '--force-version', self.version]
84
99
  cmd.extend(args)
85
100
  return subprocess2.call(cmd, env=self.get_sub_env(), timeout=self.timeout)
86
101
 
87
102
  def check_call(self, *args):
88
- cmd = [sys.executable, self.path, '--force-version', self.version]
103
+ cmd = [self.VPYTHON, self.path, '--force-version', self.version]
89
104
  cmd.extend(args)
90
105
  ((out, err), code) = subprocess2.communicate(
91
106
  cmd,
@@ -143,9 +158,8 @@ def get_sha1(filename):
143
158
 
144
159
  # Download-specific code starts here
145
160
 
146
- def enumerate_work_queue(input_filename, work_queue, directory,
147
- recursive, ignore_errors, output, sha1_file,
148
- auto_platform):
161
+ def enumerate_input(input_filename, directory, recursive, ignore_errors, output,
162
+ sha1_file, auto_platform):
149
163
  if sha1_file:
150
164
  if not os.path.exists(input_filename):
151
165
  if not ignore_errors:
@@ -154,18 +168,17 @@ def enumerate_work_queue(input_filename, work_queue, directory,
154
168
  with open(input_filename, 'rb') as f:
155
169
  sha1_match = re.match('^([A-Za-z0-9]{40})$', f.read(1024).rstrip())
156
170
  if sha1_match:
157
- work_queue.put((sha1_match.groups(1)[0], output))
158
- return 1
171
+ yield (sha1_match.groups(1)[0], output)
172
+ return
159
173
  if not ignore_errors:
160
174
  raise InvalidFileError('No sha1 sum found in %s.' % input_filename)
161
175
  print >> sys.stderr, 'No sha1 sum found in %s.' % input_filename
162
- return 0
176
+ return
163
177
 
164
178
  if not directory:
165
- work_queue.put((input_filename, output))
166
- return 1
179
+ yield (input_filename, output)
180
+ return
167
181
 
168
- work_queue_size = 0
169
182
  for root, dirs, files in os.walk(input_filename):
170
183
  if not recursive:
171
184
  for item in dirs[:]:
@@ -194,14 +207,11 @@ def enumerate_work_queue(input_filename, work_queue, directory,
194
207
  with open(full_path, 'rb') as f:
195
208
  sha1_match = re.match('^([A-Za-z0-9]{40})$', f.read(1024).rstrip())
196
209
  if sha1_match:
197
- work_queue.put(
198
- (sha1_match.groups(1)[0], full_path.replace('.sha1', '')))
199
- work_queue_size += 1
210
+ yield (sha1_match.groups(1)[0], full_path.replace('.sha1', ''))
200
211
  else:
201
212
  if not ignore_errors:
202
213
  raise InvalidFileError('No sha1 sum found in %s.' % filename)
203
214
  print >> sys.stderr, 'No sha1 sum found in %s.' % filename
204
- return work_queue_size
205
215
 
206
216
 
207
217
  def _validate_tar_file(tar, prefix):
@@ -209,13 +219,15 @@ def _validate_tar_file(tar, prefix):
209
219
  """Returns false if the tarinfo is something we explicitly forbid."""
210
220
  if tarinfo.issym() or tarinfo.islnk():
211
221
  return False
212
- if '..' in tarinfo.name or not tarinfo.name.startswith(prefix):
222
+ if ('../' in tarinfo.name or
223
+ '..\\' in tarinfo.name or
224
+ not tarinfo.name.startswith(prefix)):
213
225
  return False
214
226
  return True
215
227
  return all(map(_validate, tar.getmembers()))
216
228
 
217
229
  def _downloader_worker_thread(thread_num, q, force, base_url,
218
- gsutil, out_q, ret_codes, verbose, extract,
230
+ gsutil, out_q, ret_codes, _verbose, extract,
219
231
  delete=True):
220
232
  while True:
221
233
  input_sha1_sum, output_filename = q.get()
@@ -228,7 +240,7 @@ def _downloader_worker_thread(thread_num, q, force, base_url,
228
240
  thread_num, output_filename))
229
241
  ret_codes.put((1, '%s is not a tar.gz archive.' % (output_filename)))
230
242
  continue
231
- extract_dir = output_filename[0:len(output_filename)-7]
243
+ extract_dir = output_filename[:-len('.tar.gz')]
232
244
  if os.path.exists(output_filename) and not force:
233
245
  if not extract or os.path.exists(extract_dir):
234
246
  if get_sha1(output_filename) == input_sha1_sum:
@@ -339,9 +351,57 @@ class PrinterThread(threading.Thread):
339
351
  print line
340
352
 
341
353
 
354
+ def _data_exists(input_sha1_sum, output_filename, extract):
355
+ """Returns True if the data exists locally and matches the sha1.
356
+
357
+ This conservatively returns False for error cases.
358
+
359
+ Args:
360
+ input_sha1_sum: Expected sha1 stored on disk.
361
+ output_filename: The file to potentially download later. Its sha1 will be
362
+ compared to input_sha1_sum.
363
+ extract: Wheather or not a downloaded file should be extracted. If the file
364
+ is not extracted, this just compares the sha1 of the file. If the file
365
+ is to be extracted, this only compares the sha1 of the target archive if
366
+ the target directory already exists. The content of the target directory
367
+ is not checked.
368
+ """
369
+ extract_dir = None
370
+ if extract:
371
+ if not output_filename.endswith('.tar.gz'):
372
+ # This will cause an error later. Conservativly return False to not bail
373
+ # out too early.
374
+ return False
375
+ extract_dir = output_filename[:-len('.tar.gz')]
376
+ if os.path.exists(output_filename):
377
+ if not extract or os.path.exists(extract_dir):
378
+ if get_sha1(output_filename) == input_sha1_sum:
379
+ return True
380
+ return False
381
+
382
+
342
383
  def download_from_google_storage(
343
384
  input_filename, base_url, gsutil, num_threads, directory, recursive,
344
385
  force, output, ignore_errors, sha1_file, verbose, auto_platform, extract):
386
+
387
+ # Tuples of sha1s and paths.
388
+ input_data = list(enumerate_input(
389
+ input_filename, directory, recursive, ignore_errors, output, sha1_file,
390
+ auto_platform))
391
+
392
+ # Sequentially check for the most common case and see if we can bail out
393
+ # early before making any slow calls to gsutil.
394
+ if not force and all(
395
+ _data_exists(sha1, path, extract) for sha1, path in input_data):
396
+ return 0
397
+
398
+ # Call this once to ensure gsutil's update routine is called only once. Only
399
+ # needs to be done if we'll process input data in parallel, which can lead to
400
+ # a race in gsutil's self-update on the first call. Note, this causes a
401
+ # network call, therefore any fast bailout should be done before this point.
402
+ if len(input_data) > 1:
403
+ gsutil.check_call('version')
404
+
345
405
  # Start up all the worker threads.
346
406
  all_threads = []
347
407
  download_start = time.time()
@@ -361,10 +421,9 @@ def download_from_google_storage(
361
421
  printer_thread.daemon = True
362
422
  printer_thread.start()
363
423
 
364
- # Enumerate our work queue.
365
- work_queue_size = enumerate_work_queue(
366
- input_filename, work_queue, directory, recursive,
367
- ignore_errors, output, sha1_file, auto_platform)
424
+ # Populate our work queue.
425
+ for sha1, path in input_data:
426
+ work_queue.put((sha1, path))
368
427
  for _ in all_threads:
369
428
  work_queue.put((None, None)) # Used to tell worker threads to stop.
370
429
 
@@ -384,7 +443,7 @@ def download_from_google_storage(
384
443
  # Only print summary if any work was done.
385
444
  if printer_thread.did_print_anything:
386
445
  print 'Downloading %d files took %1f second(s)' % (
387
- work_queue_size, time.time() - download_start)
446
+ len(input_data), time.time() - download_start)
388
447
  return max_ret_code
389
448
 
390
449
 
@@ -488,7 +547,6 @@ def main(args):
488
547
  else:
489
548
  parser.error('gsutil not found in %s, bad depot_tools checkout?' %
490
549
  GSUTIL_DEFAULT_PATH)
491
- gsutil.check_call('version') # Call this once to ensure it exists.
492
550
 
493
551
  # Passing in -g/--config will run our copy of GSUtil, then quit.
494
552
  if options.config:
@@ -496,6 +554,7 @@ def main(args):
496
554
  print 'If you do not have a project ID, enter "0" when asked for one.'
497
555
  print '===End note from depot_tools==='
498
556
  print
557
+ gsutil.check_call('version')
499
558
  return gsutil.call('config')
500
559
 
501
560
  if not args:
@@ -61,11 +61,15 @@ class Checkout(object):
61
61
  def sync(self):
62
62
  pass
63
63
 
64
- def run(self, cmd, **kwargs):
64
+ def run(self, cmd, return_stdout=False, **kwargs):
65
65
  print 'Running: %s' % (' '.join(pipes.quote(x) for x in cmd))
66
66
  if self.options.dry_run:
67
67
  return ''
68
- return subprocess.check_output(cmd, **kwargs)
68
+ if return_stdout:
69
+ return subprocess.check_output(cmd, **kwargs)
70
+ else:
71
+ subprocess.check_call(cmd, **kwargs)
72
+ return ''
69
73
 
70
74
 
71
75
  class GclientCheckout(Checkout):
@@ -79,7 +83,7 @@ class GclientCheckout(Checkout):
79
83
 
80
84
  def exists(self):
81
85
  try:
82
- gclient_root = self.run_gclient('root').strip()
86
+ gclient_root = self.run_gclient('root', return_stdout=True).strip()
83
87
  return (os.path.exists(os.path.join(gclient_root, '.gclient')) or
84
88
  os.path.exists(os.path.join(os.getcwd(), self.root)))
85
89
  except subprocess.CalledProcessError:
@@ -140,9 +144,10 @@ class GclientGitCheckout(GclientCheckout, GitCheckout):
140
144
  'submodule', 'foreach',
141
145
  'git config -f $toplevel/.git/config submodule.$name.ignore all',
142
146
  cwd=wd)
143
- self.run_git(
144
- 'config', '--add', 'remote.origin.fetch',
145
- '+refs/tags/*:refs/tags/*', cwd=wd)
147
+ if not self.options.no_history:
148
+ self.run_git(
149
+ 'config', '--add', 'remote.origin.fetch',
150
+ '+refs/tags/*:refs/tags/*', cwd=wd)
146
151
  self.run_git('config', 'diff.ignoreSubmodules', 'all', cwd=wd)
147
152
 
148
153
 
@@ -17,7 +17,6 @@ class Chromium(config_util.Config):
17
17
  url = 'https://chromium.googlesource.com/chromium/src.git'
18
18
  solution = { 'name' :'src',
19
19
  'url' : url,
20
- 'deps_file': '.DEPS.git',
21
20
  'managed' : False,
22
21
  'custom_deps': {},
23
22
  }
@@ -0,0 +1,41 @@
1
+ # Copyright 2018 The Chromium Authors. All rights reserved.
2
+ # Use of this source code is governed by a BSD-style license that can be
3
+ # found in the LICENSE file.
4
+
5
+ import sys
6
+
7
+ import config_util # pylint: disable=import-error
8
+
9
+
10
+ # This class doesn't need an __init__ method, so we disable the warning
11
+ # pylint: disable=no-init
12
+ class GomaClient(config_util.Config):
13
+ """Basic Config class for Goma client."""
14
+
15
+ @staticmethod
16
+ def fetch_spec(_props):
17
+ return {
18
+ 'type': 'gclient_git',
19
+ 'gclient_git_spec': {
20
+ 'solutions': [
21
+ {
22
+ 'name' : 'client',
23
+ 'url' : 'https://chromium.googlesource.com/infra/goma/client.git',
24
+ 'deps_file': 'DEPS',
25
+ 'managed' : False,
26
+ }
27
+ ],
28
+ },
29
+ }
30
+
31
+ @staticmethod
32
+ def expected_root(_props):
33
+ return 'client'
34
+
35
+
36
+ def main(argv=None):
37
+ return GomaClient().handle_args(argv)
38
+
39
+
40
+ if __name__ == '__main__':
41
+ sys.exit(main(sys.argv))
@@ -21,7 +21,6 @@ class Infra(config_util.Config):
21
21
  {
22
22
  'name' : 'infra',
23
23
  'url' : 'https://chromium.googlesource.com/infra/infra.git',
24
- 'deps_file': '.DEPS.git',
25
24
  'managed' : False,
26
25
  }
27
26
  ],
@@ -22,7 +22,6 @@ class InfraInternal(config_util.Config):
22
22
  {
23
23
  'name': 'infra_internal',
24
24
  'url': url('chrome-internal', 'infra/infra_internal'),
25
- 'deps_file': '.DEPS.git',
26
25
  'managed': False
27
26
  },
28
27
  ],
@@ -50,6 +50,8 @@ def parse_options():
50
50
 
51
51
 
52
52
  def support_cow(src, dest):
53
+ # 'cp --reflink' always succeeds when 'src' is a symlink or a directory
54
+ assert os.path.isfile(src) and not os.path.islink(src)
53
55
  try:
54
56
  subprocess.check_output(['cp', '-a', '--reflink', src, dest],
55
57
  stderr=subprocess.STDOUT)
@@ -74,6 +76,8 @@ def main():
74
76
  args = parse_options()
75
77
 
76
78
  gclient = os.path.join(args.repository, '.gclient')
79
+ if os.path.islink(gclient):
80
+ gclient = os.path.realpath(gclient)
77
81
  new_gclient = os.path.join(args.new_workdir, '.gclient')
78
82
 
79
83
  if try_vol_snapshot(args.repository, args.new_workdir):
@@ -75,6 +75,10 @@
75
75
  # Example:
76
76
  # target_os = [ "ios" ]
77
77
  # target_os_only = True
78
+ #
79
+ # Specifying a target CPU
80
+ # To specify a target CPU, the variables target_cpu and target_cpu_only
81
+ # are available and are analagous to target_os and target_os_only.
78
82
 
79
83
  from __future__ import print_function
80
84
 
@@ -94,6 +98,7 @@ import sys
94
98
  import time
95
99
  import urlparse
96
100
 
101
+ import detect_host_arch
97
102
  import fix_encoding
98
103
  import gclient_eval
99
104
  import gclient_scm
@@ -159,8 +164,10 @@ class Hook(object):
159
164
  self._verbose = verbose
160
165
 
161
166
  @staticmethod
162
- def from_dict(d, variables=None, verbose=False):
167
+ def from_dict(d, variables=None, verbose=False, conditions=None):
163
168
  """Creates a Hook instance from a dict like in the DEPS file."""
169
+ # Merge any local and inherited conditions.
170
+ gclient_eval.UpdateCondition(d, 'and', conditions)
164
171
  return Hook(
165
172
  d['action'],
166
173
  d.get('pattern'),
@@ -200,7 +207,7 @@ class Hook(object):
200
207
  not gclient_eval.EvaluateCondition(self._condition, self._variables)):
201
208
  return
202
209
 
203
- cmd = [arg.format(**self._variables) for arg in self._action]
210
+ cmd = [arg for arg in self._action]
204
211
 
205
212
  if cmd[0] == 'python':
206
213
  # If the hook specified "python" as the first item, the action is a
@@ -233,18 +240,14 @@ class Hook(object):
233
240
  class DependencySettings(object):
234
241
  """Immutable configuration settings."""
235
242
  def __init__(
236
- self, parent, raw_url, url, managed, custom_deps, custom_vars,
237
- custom_hooks, deps_file, should_process, relative,
238
- condition, condition_value):
243
+ self, parent, url, managed, custom_deps, custom_vars,
244
+ custom_hooks, deps_file, should_process, relative, condition):
239
245
  # These are not mutable:
240
246
  self._parent = parent
241
247
  self._deps_file = deps_file
242
- self._raw_url = raw_url
243
248
  self._url = url
244
249
  # The condition as string (or None). Useful to keep e.g. for flatten.
245
250
  self._condition = condition
246
- # Boolean value of the condition. If there's no condition, just True.
247
- self._condition_value = condition_value
248
251
  # 'managed' determines whether or not this dependency is synced/updated by
249
252
  # gclient after gclient checks it out initially. The difference between
250
253
  # 'managed' and 'should_process' is that the user specifies 'managed' via
@@ -268,17 +271,19 @@ class DependencySettings(object):
268
271
  self._custom_hooks = custom_hooks or []
269
272
 
270
273
  # Post process the url to remove trailing slashes.
271
- if isinstance(self._url, basestring):
274
+ if isinstance(self.url, basestring):
272
275
  # urls are sometime incorrectly written as proto://host/path/@rev. Replace
273
276
  # it to proto://host/path@rev.
274
- self._url = self._url.replace('/@', '@')
275
- elif not isinstance(self._url, (None.__class__)):
277
+ self.set_url(self.url.replace('/@', '@'))
278
+ elif not isinstance(self.url, (None.__class__)):
276
279
  raise gclient_utils.Error(
277
280
  ('dependency url must be either string or None, '
278
- 'instead of %s') % self._url.__class__.__name__)
281
+ 'instead of %s') % self.url.__class__.__name__)
282
+
279
283
  # Make any deps_file path platform-appropriate.
280
- for sep in ['/', '\\']:
281
- self._deps_file = self._deps_file.replace(sep, os.sep)
284
+ if self._deps_file:
285
+ for sep in ['/', '\\']:
286
+ self._deps_file = self._deps_file.replace(sep, os.sep)
282
287
 
283
288
  @property
284
289
  def deps_file(self):
@@ -317,11 +322,6 @@ class DependencySettings(object):
317
322
  def custom_hooks(self):
318
323
  return self._custom_hooks[:]
319
324
 
320
- @property
321
- def raw_url(self):
322
- """URL before variable expansion."""
323
- return self._raw_url
324
-
325
325
  @property
326
326
  def url(self):
327
327
  """URL after variable expansion."""
@@ -331,10 +331,6 @@ class DependencySettings(object):
331
331
  def condition(self):
332
332
  return self._condition
333
333
 
334
- @property
335
- def condition_value(self):
336
- return self._condition_value
337
-
338
334
  @property
339
335
  def target_os(self):
340
336
  if self.local_target_os is not None:
@@ -342,6 +338,13 @@ class DependencySettings(object):
342
338
  else:
343
339
  return self.parent.target_os
344
340
 
341
+ @property
342
+ def target_cpu(self):
343
+ return self.parent.target_cpu
344
+
345
+ def set_url(self, url):
346
+ self._url = url
347
+
345
348
  def get_custom_deps(self, name, url):
346
349
  """Returns a custom deps if applicable."""
347
350
  if self.parent:
@@ -353,14 +356,13 @@ class DependencySettings(object):
353
356
  class Dependency(gclient_utils.WorkItem, DependencySettings):
354
357
  """Object that represents a dependency checkout."""
355
358
 
356
- def __init__(self, parent, name, raw_url, url, managed, custom_deps,
359
+ def __init__(self, parent, name, url, managed, custom_deps,
357
360
  custom_vars, custom_hooks, deps_file, should_process,
358
- relative, condition, condition_value):
361
+ should_recurse, relative, condition, print_outbuf=False):
359
362
  gclient_utils.WorkItem.__init__(self, name)
360
363
  DependencySettings.__init__(
361
- self, parent, raw_url, url, managed, custom_deps, custom_vars,
362
- custom_hooks, deps_file, should_process, relative,
363
- condition, condition_value)
364
+ self, parent, url, managed, custom_deps, custom_vars,
365
+ custom_hooks, deps_file, should_process, relative, condition)
364
366
 
365
367
  # This is in both .gclient and DEPS files:
366
368
  self._deps_hooks = []
@@ -368,11 +370,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
368
370
  self._pre_deps_hooks = []
369
371
 
370
372
  # Calculates properties:
371
- self._parsed_url = None
372
373
  self._dependencies = []
373
374
  self._vars = {}
374
- self._os_dependencies = {}
375
- self._os_deps_hooks = {}
376
375
 
377
376
  # A cache of the files affected by the current operation, necessary for
378
377
  # hooks.
@@ -382,6 +381,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
382
381
  # hosts will be allowed. Non-empty set means whitelist of hosts.
383
382
  # allowed_hosts var is scoped to its DEPS file, and so it isn't recursive.
384
383
  self._allowed_hosts = frozenset()
384
+ self._gn_args_from = None
385
385
  # Spec for .gni output to write (if any).
386
386
  self._gn_args_file = None
387
387
  self._gn_args = []
@@ -402,26 +402,84 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
402
402
  # unavailable
403
403
  self._got_revision = None
404
404
 
405
- # This is a mutable value that overrides the normal recursion limit for this
406
- # dependency. It is read from the actual DEPS file so cannot be set on
407
- # class instantiation.
408
- self.recursion_override = None
409
405
  # recursedeps is a mutable value that selectively overrides the default
410
- # 'no recursion' setting on a dep-by-dep basis. It will replace
411
- # recursion_override.
406
+ # 'no recursion' setting on a dep-by-dep basis.
412
407
  #
413
- # It will be a dictionary of {deps_name: {"deps_file": depfile_name}} or
414
- # None.
415
- self.recursedeps = None
408
+ # It will be a dictionary of {deps_name: depfile_namee}
409
+ self.recursedeps = {}
410
+
411
+ # Whether we should process this dependency's DEPS file.
412
+ self._should_recurse = should_recurse
413
+
414
+ self._OverrideUrl()
416
415
  # This is inherited from WorkItem. We want the URL to be a resource.
417
- if url and isinstance(url, basestring):
416
+ if self.url and isinstance(self.url, basestring):
418
417
  # The url is usually given to gclient either as https://blah@123
419
418
  # or just https://blah. The @123 portion is irrelevant.
420
- self.resources.append(url.split('@')[0])
419
+ self.resources.append(self.url.split('@')[0])
420
+
421
+ # Controls whether we want to print git's output when we first clone the
422
+ # dependency
423
+ self.print_outbuf = print_outbuf
421
424
 
422
425
  if not self.name and self.parent:
423
426
  raise gclient_utils.Error('Dependency without name')
424
427
 
428
+ def _OverrideUrl(self):
429
+ """Resolves the parsed url from the parent hierarchy."""
430
+ parsed_url = self.get_custom_deps(self._name, self.url)
431
+ if parsed_url != self.url:
432
+ logging.info('Dependency(%s)._OverrideUrl(%s) -> %s', self._name,
433
+ self.url, parsed_url)
434
+ self.set_url(parsed_url)
435
+
436
+ elif isinstance(self.url, basestring):
437
+ parsed_url = urlparse.urlparse(self.url)
438
+ if (not parsed_url[0] and
439
+ not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
440
+ path = parsed_url[2]
441
+ if not path.startswith('/'):
442
+ raise gclient_utils.Error(
443
+ 'relative DEPS entry \'%s\' must begin with a slash' % self.url)
444
+ # A relative url. Get the parent url, strip from the last '/'
445
+ # (equivalent to unix basename), and append the relative url.
446
+ parent_url = self.parent.url
447
+ parsed_url = parent_url[:parent_url.rfind('/')] + self.url
448
+ logging.info('Dependency(%s)._OverrideUrl(%s) -> %s', self.name,
449
+ self.url, parsed_url)
450
+ self.set_url(parsed_url)
451
+
452
+ elif self.url is None:
453
+ logging.info('Dependency(%s)._OverrideUrl(None) -> None', self._name)
454
+
455
+ else:
456
+ raise gclient_utils.Error('Unknown url type')
457
+
458
+ def PinToActualRevision(self):
459
+ """Updates self.url to the revision checked out on disk."""
460
+ if self.url is None:
461
+ return
462
+ url = None
463
+ scm = self.CreateSCM()
464
+ if os.path.isdir(scm.checkout_path):
465
+ revision = scm.revinfo(None, None, None)
466
+ url = '%s@%s' % (gclient_utils.SplitUrlRevision(self.url)[0], revision)
467
+ self.set_url(url)
468
+
469
+ def ToLines(self):
470
+ s = []
471
+ condition_part = ([' "condition": %r,' % self.condition]
472
+ if self.condition else [])
473
+ s.extend([
474
+ ' # %s' % self.hierarchy(include_url=False),
475
+ ' "%s": {' % (self.name,),
476
+ ' "url": "%s",' % (self.url,),
477
+ ] + condition_part + [
478
+ ' },',
479
+ '',
480
+ ])
481
+ return s
482
+
425
483
  @property
426
484
  def requirements(self):
427
485
  """Calculate the list of requirements."""
@@ -436,9 +494,6 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
436
494
  # This becomes messy for >2 depth as the DEPS file format is a dictionary,
437
495
  # thus unsorted, while the .gclient format is a list thus sorted.
438
496
  #
439
- # * _recursion_limit is hard coded 2 and there is no hope to change this
440
- # value.
441
- #
442
497
  # Interestingly enough, the following condition only works in the case we
443
498
  # want: self is a 2nd level node. 3nd level node wouldn't need this since
444
499
  # they already have their parent as a requirement.
@@ -456,24 +511,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
456
511
  return requirements
457
512
 
458
513
  @property
459
- def try_recursedeps(self):
460
- """Returns False if recursion_override is ever specified."""
461
- if self.recursion_override is not None:
462
- return False
463
- return self.parent.try_recursedeps
464
-
465
- @property
466
- def recursion_limit(self):
467
- """Returns > 0 if this dependency is not too recursed to be processed."""
468
- # We continue to support the absence of recursedeps until tools and DEPS
469
- # using recursion_override are updated.
470
- if self.try_recursedeps and self.parent.recursedeps != None:
471
- if self.name in self.parent.recursedeps:
472
- return 1
473
-
474
- if self.recursion_override is not None:
475
- return self.recursion_override
476
- return max(self.parent.recursion_limit - 1, 0)
514
+ def should_recurse(self):
515
+ return self._should_recurse
477
516
 
478
517
  def verify_validity(self):
479
518
  """Verifies that this Dependency is fine to add as a child of another one.
@@ -493,10 +532,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
493
532
  # This require a full tree traversal with locks.
494
533
  siblings = [d for d in self.root.subtree(False) if d.name == self.name]
495
534
  for sibling in siblings:
496
- self_url = self.LateOverride(self.url)
497
- sibling_url = sibling.LateOverride(sibling.url)
498
535
  # Allow to have only one to be None or ''.
499
- if self_url != sibling_url and bool(self_url) == bool(sibling_url):
536
+ if self.url != sibling.url and bool(self.url) == bool(sibling.url):
500
537
  raise gclient_utils.Error(
501
538
  ('Dependency %s specified more than once:\n'
502
539
  ' %s [%s]\n'
@@ -504,99 +541,15 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
504
541
  ' %s [%s]') % (
505
542
  self.name,
506
543
  sibling.hierarchy(),
507
- sibling_url,
544
+ sibling.url,
508
545
  self.hierarchy(),
509
- self_url))
546
+ self.url))
510
547
  # In theory we could keep it as a shadow of the other one. In
511
548
  # practice, simply ignore it.
512
549
  logging.warn('Won\'t process duplicate dependency %s' % sibling)
513
550
  return False
514
551
  return True
515
552
 
516
- def LateOverride(self, url):
517
- """Resolves the parsed url from url."""
518
- assert self.parsed_url == None or not self.should_process, self.parsed_url
519
- parsed_url = self.get_custom_deps(self.name, url)
520
- if parsed_url != url:
521
- logging.info(
522
- 'Dependency(%s).LateOverride(%s) -> %s' %
523
- (self.name, url, parsed_url))
524
- return parsed_url
525
-
526
- if isinstance(url, basestring):
527
- parsed_url = urlparse.urlparse(url)
528
- if (not parsed_url[0] and
529
- not re.match(r'^\w+\@[\w\.-]+\:[\w\/]+', parsed_url[2])):
530
- # A relative url. Fetch the real base.
531
- path = parsed_url[2]
532
- if not path.startswith('/'):
533
- raise gclient_utils.Error(
534
- 'relative DEPS entry \'%s\' must begin with a slash' % url)
535
- # Create a scm just to query the full url.
536
- parent_url = self.parent.parsed_url
537
- scm = gclient_scm.CreateSCM(
538
- parent_url, self.root.root_dir, None, self.outbuf)
539
- parsed_url = scm.FullUrlForRelativeUrl(url)
540
- else:
541
- parsed_url = url
542
- logging.info(
543
- 'Dependency(%s).LateOverride(%s) -> %s' %
544
- (self.name, url, parsed_url))
545
- return parsed_url
546
-
547
- if url is None:
548
- logging.info(
549
- 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
550
- return url
551
-
552
- raise gclient_utils.Error('Unknown url type')
553
-
554
- @staticmethod
555
- def MergeWithOsDeps(deps, deps_os, target_os_list, process_all_deps):
556
- """Returns a new "deps" structure that is the deps sent in updated
557
- with information from deps_os (the deps_os section of the DEPS
558
- file) that matches the list of target os."""
559
- new_deps = deps.copy()
560
- for dep_os, os_deps in deps_os.iteritems():
561
- for key, value in os_deps.iteritems():
562
- if value is None:
563
- # Make this condition very visible, so it's not a silent failure.
564
- # It's unclear how to support None override in deps_os.
565
- logging.error('Ignoring %r:%r in %r deps_os', key, value, dep_os)
566
- continue
567
-
568
- # Normalize value to be a dict which contains |should_process| metadata.
569
- if isinstance(value, basestring):
570
- value = {'url': value}
571
- assert isinstance(value, collections.Mapping), (key, value)
572
- value['should_process'] = dep_os in target_os_list or process_all_deps
573
-
574
- # Handle collisions/overrides.
575
- if key in new_deps and new_deps[key] != value:
576
- # Normalize the existing new_deps entry.
577
- if isinstance(new_deps[key], basestring):
578
- new_deps[key] = {'url': new_deps[key]}
579
- assert isinstance(new_deps[key],
580
- collections.Mapping), (key, new_deps[key])
581
-
582
- # It's OK if the "override" sets the key to the same value.
583
- # This is mostly for legacy reasons to keep existing DEPS files
584
- # working. Often mac/ios and unix/android will do this.
585
- if value['url'] != new_deps[key]['url']:
586
- raise gclient_utils.Error(
587
- ('Value from deps_os (%r; %r: %r) conflicts with existing deps '
588
- 'entry (%r).') % (dep_os, key, value, new_deps[key]))
589
-
590
- # We'd otherwise overwrite |should_process| metadata, but a dep should
591
- # be processed if _any_ of its references call for that.
592
- value['should_process'] = (
593
- value['should_process'] or
594
- new_deps[key].get('should_process', True))
595
-
596
- new_deps[key] = value
597
-
598
- return new_deps
599
-
600
553
  def _postprocess_deps(self, deps, rel_prefix):
601
554
  """Performs post-processing of deps compared to what's in the DEPS file."""
602
555
  # Make sure the dict is mutable, e.g. in case it's frozen.
@@ -604,9 +557,19 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
604
557
 
605
558
  # If a line is in custom_deps, but not in the solution, we want to append
606
559
  # this line to the solution.
607
- for d in self.custom_deps:
608
- if d not in deps:
609
- deps[d] = self.custom_deps[d]
560
+ for dep_name, dep_info in self.custom_deps.iteritems():
561
+ if dep_name not in deps:
562
+ deps[dep_name] = {'url': dep_info, 'dep_type': 'git'}
563
+
564
+ # Make child deps conditional on any parent conditions. This ensures that,
565
+ # when flattened, recursed entries have the correct restrictions, even if
566
+ # not explicitly set in the recursed DEPS file. For instance, if
567
+ # "src/ios_foo" is conditional on "checkout_ios=True", then anything
568
+ # recursively included by "src/ios_foo/DEPS" should also require
569
+ # "checkout_ios=True".
570
+ if self.condition:
571
+ for value in deps.itervalues():
572
+ gclient_eval.UpdateCondition(value, 'and', self.condition)
610
573
 
611
574
  if rel_prefix:
612
575
  logging.warning('use_relative_paths enabled.')
@@ -624,38 +587,51 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
624
587
  """Convert a deps dict to a dict of Dependency objects."""
625
588
  deps_to_add = []
626
589
  for name, dep_value in deps.iteritems():
627
- should_process = self.recursion_limit and self.should_process
628
- deps_file = self.deps_file
629
- if self.recursedeps is not None:
630
- ent = self.recursedeps.get(name)
631
- if ent is not None:
632
- deps_file = ent['deps_file']
590
+ should_process = self.should_process
633
591
  if dep_value is None:
634
592
  continue
635
- condition = None
636
- condition_value = True
637
- if isinstance(dep_value, basestring):
638
- raw_url = dep_value
639
- else:
640
- # This should be guaranteed by schema checking in gclient_eval.
641
- assert isinstance(dep_value, collections.Mapping)
642
- raw_url = dep_value['url']
643
- # Take into account should_process metadata set by MergeWithOsDeps.
644
- should_process = (should_process and
645
- dep_value.get('should_process', True))
646
- condition = dep_value.get('condition')
647
-
648
- url = raw_url.format(**self.get_vars())
649
-
650
- if condition:
651
- condition_value = gclient_eval.EvaluateCondition(
593
+
594
+ condition = dep_value.get('condition')
595
+ dep_type = dep_value.get('dep_type')
596
+
597
+ if condition and not self._get_option('process_all_deps', False):
598
+ should_process = should_process and gclient_eval.EvaluateCondition(
652
599
  condition, self.get_vars())
653
- if not self._get_option('process_all_deps', False):
654
- should_process = should_process and condition_value
655
- deps_to_add.append(Dependency(
656
- self, name, raw_url, url, None, None, self.custom_vars, None,
657
- deps_file, should_process, use_relative_paths, condition,
658
- condition_value))
600
+
601
+ if dep_type == 'cipd':
602
+ cipd_root = self.GetCipdRoot()
603
+ for package in dep_value.get('packages', []):
604
+ if 'version' in package:
605
+ # Matches version to vars value.
606
+ version = package['version']
607
+ package['version'] = version
608
+ deps_to_add.append(
609
+ CipdDependency(
610
+ parent=self,
611
+ name=name,
612
+ dep_value=package,
613
+ cipd_root=cipd_root,
614
+ custom_vars=self.custom_vars,
615
+ should_process=should_process,
616
+ relative=use_relative_paths,
617
+ condition=condition))
618
+ else:
619
+ url = dep_value.get('url')
620
+ deps_to_add.append(
621
+ GitDependency(
622
+ parent=self,
623
+ name=name,
624
+ url=url,
625
+ managed=True,
626
+ custom_deps=None,
627
+ custom_vars=self.custom_vars,
628
+ custom_hooks=None,
629
+ deps_file=self.recursedeps.get(name, self.deps_file),
630
+ should_process=should_process,
631
+ should_recurse=name in self.recursedeps,
632
+ relative=use_relative_paths,
633
+ condition=condition))
634
+
659
635
  deps_to_add.sort(key=lambda x: x.name)
660
636
  return deps_to_add
661
637
 
@@ -688,16 +664,10 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
688
664
 
689
665
  local_scope = {}
690
666
  if deps_content:
691
- global_scope = {
692
- 'Var': lambda var_name: '{%s}' % var_name,
693
- 'deps_os': {},
694
- }
695
- # Eval the content.
696
667
  try:
697
- if self._get_option('validate_syntax', False):
698
- gclient_eval.Exec(deps_content, global_scope, local_scope, filepath)
699
- else:
700
- exec(deps_content, global_scope, local_scope)
668
+ local_scope = gclient_eval.Parse(
669
+ deps_content, self._get_option('validate_syntax', False),
670
+ filepath, self.get_vars())
701
671
  except SyntaxError as e:
702
672
  gclient_utils.SyntaxErrorToError(filepath, e)
703
673
 
@@ -713,8 +683,14 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
713
683
  'ParseDepsFile(%s): allowed_hosts must be absent '
714
684
  'or a non-empty iterable' % self.name)
715
685
 
686
+ self._gn_args_from = local_scope.get('gclient_gn_args_from')
716
687
  self._gn_args_file = local_scope.get('gclient_gn_args_file')
717
688
  self._gn_args = local_scope.get('gclient_gn_args', [])
689
+ # It doesn't make sense to set all of these, since setting gn_args_from to
690
+ # another DEPS will make gclient ignore any other local gn_args* settings.
691
+ assert not (self._gn_args_from and self._gn_args_file), \
692
+ 'Only specify one of "gclient_gn_args_from" or ' \
693
+ '"gclient_gn_args_file + gclient_gn_args".'
718
694
 
719
695
  self._vars = local_scope.get('vars', {})
720
696
  if self.parent:
@@ -740,22 +716,16 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
740
716
  elif self._relative:
741
717
  rel_prefix = os.path.dirname(self.name)
742
718
 
743
- deps = {}
744
- for key, value in local_scope.get('deps', {}).iteritems():
745
- deps[key.format(**self.get_vars())] = value
746
-
747
719
  if 'recursion' in local_scope:
748
- self.recursion_override = local_scope.get('recursion')
749
720
  logging.warning(
750
- 'Setting %s recursion to %d.', self.name, self.recursion_limit)
751
- self.recursedeps = None
721
+ '%s: Ignoring recursion = %d.', self.name, local_scope['recursion'])
722
+
752
723
  if 'recursedeps' in local_scope:
753
- self.recursedeps = {}
754
724
  for ent in local_scope['recursedeps']:
755
725
  if isinstance(ent, basestring):
756
- self.recursedeps[ent] = {"deps_file": self.deps_file}
726
+ self.recursedeps[ent] = self.deps_file
757
727
  else: # (depname, depsfilename)
758
- self.recursedeps[ent[0]] = {"deps_file": ent[1]}
728
+ self.recursedeps[ent[0]] = ent[1]
759
729
  logging.warning('Found recursedeps %r.', repr(self.recursedeps))
760
730
 
761
731
  if rel_prefix:
@@ -765,23 +735,16 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
765
735
  rel_deps[
766
736
  os.path.normpath(os.path.join(rel_prefix, depname))] = options
767
737
  self.recursedeps = rel_deps
738
+ # To get gn_args from another DEPS, that DEPS must be recursed into.
739
+ if self._gn_args_from:
740
+ assert self.recursedeps and self._gn_args_from in self.recursedeps, \
741
+ 'The "gclient_gn_args_from" value must be in recursedeps.'
768
742
 
769
743
  # If present, save 'target_os' in the local_target_os property.
770
744
  if 'target_os' in local_scope:
771
745
  self.local_target_os = local_scope['target_os']
772
- # load os specific dependencies if defined. these dependencies may
773
- # override or extend the values defined by the 'deps' member.
774
- target_os_list = self.target_os
775
- if 'deps_os' in local_scope:
776
- for dep_os, os_deps in local_scope['deps_os'].iteritems():
777
- self._os_dependencies[dep_os] = self._deps_to_objects(
778
- self._postprocess_deps(os_deps, rel_prefix), use_relative_paths)
779
- if target_os_list and not self._get_option(
780
- 'do_not_merge_os_specific_entries', False):
781
- deps = self.MergeWithOsDeps(
782
- deps, local_scope['deps_os'], target_os_list,
783
- self._get_option('process_all_deps', False))
784
746
 
747
+ deps = local_scope.get('deps', {})
785
748
  deps_to_add = self._deps_to_objects(
786
749
  self._postprocess_deps(deps, rel_prefix), use_relative_paths)
787
750
 
@@ -791,29 +754,16 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
791
754
  for hook in local_scope.get('hooks', []):
792
755
  if hook.get('name', '') not in hook_names_to_suppress:
793
756
  hooks_to_run.append(hook)
794
- if 'hooks_os' in local_scope and target_os_list:
795
- hooks_os = local_scope['hooks_os']
796
-
797
- # Keep original contents of hooks_os for flatten.
798
- for hook_os, os_hooks in hooks_os.iteritems():
799
- self._os_deps_hooks[hook_os] = [
800
- Hook.from_dict(hook, variables=self.get_vars(), verbose=True)
801
- for hook in os_hooks]
802
-
803
- # Specifically append these to ensure that hooks_os run after hooks.
804
- if not self._get_option('do_not_merge_os_specific_entries', False):
805
- for the_target_os in target_os_list:
806
- the_target_os_hooks = hooks_os.get(the_target_os, [])
807
- hooks_to_run.extend(the_target_os_hooks)
808
757
 
809
758
  # add the replacements and any additions
810
759
  for hook in self.custom_hooks:
811
760
  if 'action' in hook:
812
761
  hooks_to_run.append(hook)
813
762
 
814
- if self.recursion_limit:
763
+ if self.should_recurse:
815
764
  self._pre_deps_hooks = [
816
- Hook.from_dict(hook, variables=self.get_vars(), verbose=True)
765
+ Hook.from_dict(hook, variables=self.get_vars(), verbose=True,
766
+ conditions=self.condition)
817
767
  for hook in local_scope.get('pre_deps_hooks', [])
818
768
  ]
819
769
 
@@ -833,7 +783,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
833
783
  self.add_dependency(dep)
834
784
  self._mark_as_parsed([
835
785
  Hook.from_dict(
836
- h, variables=self.get_vars(), verbose=self.root._options.verbose)
786
+ h, variables=self.get_vars(), verbose=self.root._options.verbose,
787
+ conditions=self.condition)
837
788
  for h in hooks
838
789
  ])
839
790
 
@@ -855,9 +806,42 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
855
806
  bad_deps.append(dep)
856
807
  return bad_deps
857
808
 
809
+ def FuzzyMatchUrl(self, candidates):
810
+ """Attempts to find this dependency in the list of candidates.
811
+
812
+ It looks first for the URL of this dependency in the list of
813
+ candidates. If it doesn't succeed, and the URL ends in '.git', it will try
814
+ looking for the URL minus '.git'. Finally it will try to look for the name
815
+ of the dependency.
816
+
817
+ Args:
818
+ candidates: list, dict. The list of candidates in which to look for this
819
+ dependency. It can contain URLs as above, or dependency names like
820
+ "src/some/dep".
821
+
822
+ Returns:
823
+ If this dependency is not found in the list of candidates, returns None.
824
+ Otherwise, it returns under which name did we find this dependency:
825
+ - Its parsed url: "https://example.com/src.git'
826
+ - Its parsed url minus '.git': "https://example.com/src"
827
+ - Its name: "src"
828
+ """
829
+ if self.url:
830
+ origin, _ = gclient_utils.SplitUrlRevision(self.url)
831
+ if origin in candidates:
832
+ return origin
833
+ if origin.endswith('.git') and origin[:-len('.git')] in candidates:
834
+ return origin[:-len('.git')]
835
+ if origin + '.git' in candidates:
836
+ return origin + '.git'
837
+ if self.name in candidates:
838
+ return self.name
839
+ return None
840
+
858
841
  # Arguments number differs from overridden method
859
842
  # pylint: disable=arguments-differ
860
- def run(self, revision_overrides, command, args, work_queue, options):
843
+ def run(self, revision_overrides, command, args, work_queue, options,
844
+ patch_refs):
861
845
  """Runs |command| then parse the DEPS file."""
862
846
  logging.info('Dependency(%s).run()' % self.name)
863
847
  assert self._file_list == []
@@ -868,21 +852,26 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
868
852
  # copy state, so skip the SCM status check.
869
853
  run_scm = command not in (
870
854
  'flatten', 'runhooks', 'recurse', 'validate', None)
871
- parsed_url = self.LateOverride(self.url)
872
855
  file_list = [] if not options.nohooks else None
873
- revision_override = revision_overrides.pop(self.name, None)
874
- if not revision_override and parsed_url:
875
- revision_override = revision_overrides.get(parsed_url.split('@')[0], None)
876
- if run_scm and parsed_url:
856
+ revision_override = revision_overrides.pop(
857
+ self.FuzzyMatchUrl(revision_overrides), None)
858
+ if not revision_override and not self.managed:
859
+ revision_override = 'unmanaged'
860
+ if run_scm and self.url:
877
861
  # Create a shallow copy to mutate revision.
878
862
  options = copy.copy(options)
879
863
  options.revision = revision_override
880
864
  self._used_revision = options.revision
881
- self._used_scm = gclient_scm.CreateSCM(
882
- parsed_url, self.root.root_dir, self.name, self.outbuf,
883
- out_cb=work_queue.out_cb)
865
+ self._used_scm = self.CreateSCM(out_cb=work_queue.out_cb)
884
866
  self._got_revision = self._used_scm.RunCommand(command, options, args,
885
867
  file_list)
868
+
869
+ patch_repo = self.url.split('@')[0]
870
+ patch_ref = patch_refs.pop(self.FuzzyMatchUrl(patch_refs), None)
871
+ if command == 'update' and patch_ref is not None:
872
+ self._used_scm.apply_patch_ref(patch_repo, patch_ref, options,
873
+ file_list)
874
+
886
875
  if file_list:
887
876
  file_list = [os.path.join(self.name, f.strip()) for f in file_list]
888
877
 
@@ -899,13 +888,14 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
899
888
  while file_list[i].startswith(('\\', '/')):
900
889
  file_list[i] = file_list[i][1:]
901
890
 
902
- # Always parse the DEPS file.
903
- self.ParseDepsFile()
904
- self._run_is_done(file_list or [], parsed_url)
905
- if command in ('update', 'revert') and not options.noprehooks:
906
- self.RunPreDepsHooks()
891
+ if self.should_recurse:
892
+ self.ParseDepsFile()
893
+
894
+ self._run_is_done(file_list or [])
907
895
 
908
- if self.recursion_limit:
896
+ if self.should_recurse:
897
+ if command in ('update', 'revert') and not options.noprehooks:
898
+ self.RunPreDepsHooks()
909
899
  # Parse the dependencies of this dependency.
910
900
  for s in self.dependencies:
911
901
  if s.should_process:
@@ -913,7 +903,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
913
903
 
914
904
  if command == 'recurse':
915
905
  # Skip file only checkout.
916
- scm = gclient_scm.GetScmName(parsed_url)
906
+ scm = self.GetScmName()
917
907
  if not options.scm or scm in options.scm:
918
908
  cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name))
919
909
  # Pass in the SCM type as an env variable. Make sure we don't put
@@ -921,8 +911,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
921
911
  env = os.environ.copy()
922
912
  if scm:
923
913
  env['GCLIENT_SCM'] = str(scm)
924
- if parsed_url:
925
- env['GCLIENT_URL'] = str(parsed_url)
914
+ if self.url:
915
+ env['GCLIENT_URL'] = str(self.url)
926
916
  env['GCLIENT_DEP_PATH'] = str(self.name)
927
917
  if options.prepend_dir and scm == 'git':
928
918
  print_stdout = False
@@ -953,7 +943,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
953
943
  print_stdout = True
954
944
  filter_fn = None
955
945
 
956
- if parsed_url is None:
946
+ if self.url is None:
957
947
  print('Skipped omitted dependency %s' % cwd, file=sys.stderr)
958
948
  elif os.path.isdir(cwd):
959
949
  try:
@@ -967,6 +957,12 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
967
957
  else:
968
958
  print('Skipped missing %s' % cwd, file=sys.stderr)
969
959
 
960
+ def GetScmName(self):
961
+ raise NotImplementedError()
962
+
963
+ def CreateSCM(self, out_cb=None):
964
+ raise NotImplementedError()
965
+
970
966
  def HasGNArgsFile(self):
971
967
  return self._gn_args_file is not None
972
968
 
@@ -982,10 +978,9 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
982
978
  f.write('\n'.join(lines))
983
979
 
984
980
  @gclient_utils.lockedmethod
985
- def _run_is_done(self, file_list, parsed_url):
981
+ def _run_is_done(self, file_list):
986
982
  # Both these are kept for hooks that are run as a separate tree traversal.
987
983
  self._file_list = file_list
988
- self._parsed_url = parsed_url
989
984
  self._processed = True
990
985
 
991
986
  def GetHooks(self, options):
@@ -994,7 +989,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
994
989
  RunOnDeps() must have been called before to load the DEPS.
995
990
  """
996
991
  result = []
997
- if not self.should_process or not self.recursion_limit:
992
+ if not self.should_process or not self.should_recurse:
998
993
  # Don't run the hook when it is above recursion_limit.
999
994
  return result
1000
995
  # If "--force" was specified, run all hooks regardless of what files have
@@ -1003,24 +998,11 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
1003
998
  # TODO(maruel): If the user is using git, then we don't know
1004
999
  # what files have changed so we always run all hooks. It'd be nice to fix
1005
1000
  # that.
1006
- if (options.force or
1007
- gclient_scm.GetScmName(self.parsed_url) in ('git', None) or
1008
- os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))):
1009
- result.extend(self.deps_hooks)
1010
- else:
1011
- for hook in self.deps_hooks:
1012
- if hook.matches(self.file_list_and_children):
1013
- result.append(hook)
1001
+ result.extend(self.deps_hooks)
1014
1002
  for s in self.dependencies:
1015
1003
  result.extend(s.GetHooks(options))
1016
1004
  return result
1017
1005
 
1018
- def WriteGNArgsFilesRecursively(self, dependencies):
1019
- for dep in dependencies:
1020
- if dep.HasGNArgsFile():
1021
- dep.WriteGNArgsFile()
1022
- self.WriteGNArgsFilesRecursively(dep.dependencies)
1023
-
1024
1006
  def RunHooksRecursively(self, options, progress):
1025
1007
  assert self.hooks_ran == False
1026
1008
  self._hooks_ran = True
@@ -1045,6 +1027,13 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
1045
1027
  for hook in self.pre_deps_hooks:
1046
1028
  hook.run(self.root.root_dir)
1047
1029
 
1030
+ def GetCipdRoot(self):
1031
+ if self.root is self:
1032
+ # Let's not infinitely recurse. If this is root and isn't an
1033
+ # instance of GClient, do nothing.
1034
+ return None
1035
+ return self.root.GetCipdRoot()
1036
+
1048
1037
  def subtree(self, include_all):
1049
1038
  """Breadth first recursion excluding root node."""
1050
1039
  dependencies = self.dependencies
@@ -1069,31 +1058,16 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
1069
1058
  def dependencies(self):
1070
1059
  return tuple(self._dependencies)
1071
1060
 
1072
- @property
1073
- @gclient_utils.lockedmethod
1074
- def os_dependencies(self):
1075
- return dict(self._os_dependencies)
1076
-
1077
1061
  @property
1078
1062
  @gclient_utils.lockedmethod
1079
1063
  def deps_hooks(self):
1080
1064
  return tuple(self._deps_hooks)
1081
1065
 
1082
- @property
1083
- @gclient_utils.lockedmethod
1084
- def os_deps_hooks(self):
1085
- return dict(self._os_deps_hooks)
1086
-
1087
1066
  @property
1088
1067
  @gclient_utils.lockedmethod
1089
1068
  def pre_deps_hooks(self):
1090
1069
  return tuple(self._pre_deps_hooks)
1091
1070
 
1092
- @property
1093
- @gclient_utils.lockedmethod
1094
- def parsed_url(self):
1095
- return self._parsed_url
1096
-
1097
1071
  @property
1098
1072
  @gclient_utils.lockedmethod
1099
1073
  def deps_parsed(self):
@@ -1144,7 +1118,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
1144
1118
 
1145
1119
  def __str__(self):
1146
1120
  out = []
1147
- for i in ('name', 'url', 'parsed_url', 'custom_deps',
1121
+ for i in ('name', 'url', 'custom_deps',
1148
1122
  'custom_vars', 'deps_hooks', 'file_list', 'should_process',
1149
1123
  'processed', 'hooks_ran', 'deps_parsed', 'requirements',
1150
1124
  'allowed_hosts'):
@@ -1177,21 +1151,47 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
1177
1151
  i = i.parent
1178
1152
  return out
1179
1153
 
1154
+ def hierarchy_data(self):
1155
+ """Returns a machine-readable hierarchical reference to a Dependency."""
1156
+ d = self
1157
+ out = []
1158
+ while d and d.name:
1159
+ out.insert(0, (d.name, d.url))
1160
+ d = d.parent
1161
+ return tuple(out)
1162
+
1180
1163
  def get_vars(self):
1181
1164
  """Returns a dictionary of effective variable values
1182
1165
  (DEPS file contents with applied custom_vars overrides)."""
1183
1166
  # Provide some built-in variables.
1184
1167
  result = {
1185
1168
  'checkout_android': 'android' in self.target_os,
1169
+ 'checkout_chromeos': 'chromeos' in self.target_os,
1186
1170
  'checkout_fuchsia': 'fuchsia' in self.target_os,
1187
1171
  'checkout_ios': 'ios' in self.target_os,
1188
1172
  'checkout_linux': 'unix' in self.target_os,
1189
1173
  'checkout_mac': 'mac' in self.target_os,
1190
1174
  'checkout_win': 'win' in self.target_os,
1191
1175
  'host_os': _detect_host_os(),
1176
+
1177
+ 'checkout_arm': 'arm' in self.target_cpu,
1178
+ 'checkout_arm64': 'arm64' in self.target_cpu,
1179
+ 'checkout_x86': 'x86' in self.target_cpu,
1180
+ 'checkout_mips': 'mips' in self.target_cpu,
1181
+ 'checkout_ppc': 'ppc' in self.target_cpu,
1182
+ 'checkout_s390': 's390' in self.target_cpu,
1183
+ 'checkout_x64': 'x64' in self.target_cpu,
1184
+ 'host_cpu': detect_host_arch.HostArch(),
1192
1185
  }
1193
- # Variables defined in DEPS file override built-in ones.
1186
+ # Variable precedence:
1187
+ # - built-in
1188
+ # - DEPS vars
1189
+ # - parents, from first to last
1190
+ # - custom_vars overrides
1194
1191
  result.update(self._vars)
1192
+ if self.parent:
1193
+ parent_vars = self.parent.get_vars()
1194
+ result.update(parent_vars)
1195
1195
  result.update(self.custom_vars or {})
1196
1196
  return result
1197
1197
 
@@ -1209,7 +1209,23 @@ def _detect_host_os():
1209
1209
  return _PLATFORM_MAPPING[sys.platform]
1210
1210
 
1211
1211
 
1212
- class GClient(Dependency):
1212
+ class GitDependency(Dependency):
1213
+ """A Dependency object that represents a single git checkout."""
1214
+
1215
+ #override
1216
+ def GetScmName(self):
1217
+ """Always 'git'."""
1218
+ return 'git'
1219
+
1220
+ #override
1221
+ def CreateSCM(self, out_cb=None):
1222
+ """Create a Wrapper instance suitable for handling this git dependency."""
1223
+ return gclient_scm.GitWrapper(
1224
+ self.url, self.root.root_dir, self.name, self.outbuf, out_cb,
1225
+ print_outbuf=self.print_outbuf)
1226
+
1227
+
1228
+ class GClient(GitDependency):
1213
1229
  """Object that represent a gclient checkout. A tree of Dependency(), one per
1214
1230
  solution or DEPS entry."""
1215
1231
 
@@ -1226,6 +1242,7 @@ class GClient(Dependency):
1226
1242
  "linux3": "unix",
1227
1243
  "android": "android",
1228
1244
  "ios": "ios",
1245
+ "fuchsia": "fuchsia",
1229
1246
  }
1230
1247
 
1231
1248
  DEFAULT_CLIENT_FILE_TEXT = ("""\
@@ -1242,28 +1259,30 @@ solutions = [
1242
1259
  cache_dir = %(cache_dir)r
1243
1260
  """)
1244
1261
 
1245
- DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
1246
- { "name" : "%(solution_name)s",
1247
- "url" : "%(solution_url)s",
1248
- "deps_file" : "%(deps_file)s",
1249
- "managed" : %(managed)s,
1250
- "custom_deps" : {
1251
- %(solution_deps)s },
1252
- },
1253
- """)
1254
-
1255
1262
  DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
1256
1263
  # Snapshot generated with gclient revinfo --snapshot
1257
- solutions = [
1258
- %(solution_list)s]
1264
+ solutions = %(solution_list)s
1259
1265
  """)
1260
1266
 
1261
1267
  def __init__(self, root_dir, options):
1262
1268
  # Do not change previous behavior. Only solution level and immediate DEPS
1263
1269
  # are processed.
1264
1270
  self._recursion_limit = 2
1265
- Dependency.__init__(self, None, None, None, None, True, None, None, None,
1266
- 'unused', True, None, None, True)
1271
+ super(GClient, self).__init__(
1272
+ parent=None,
1273
+ name=None,
1274
+ url=None,
1275
+ managed=True,
1276
+ custom_deps=None,
1277
+ custom_vars=None,
1278
+ custom_hooks=None,
1279
+ deps_file='unused',
1280
+ should_process=True,
1281
+ should_recurse=True,
1282
+ relative=None,
1283
+ condition=None,
1284
+ print_outbuf=True)
1285
+
1267
1286
  self._options = options
1268
1287
  if options.deps_os:
1269
1288
  enforced_os = options.deps_os.split(',')
@@ -1272,7 +1291,9 @@ solutions = [
1272
1291
  if 'all' in enforced_os:
1273
1292
  enforced_os = self.DEPS_OS_CHOICES.itervalues()
1274
1293
  self._enforced_os = tuple(set(enforced_os))
1294
+ self._enforced_cpu = detect_host_arch.HostArch(),
1275
1295
  self._root_dir = root_dir
1296
+ self._cipd_root = None
1276
1297
  self.config_content = None
1277
1298
 
1278
1299
  def _CheckConfig(self):
@@ -1280,8 +1301,7 @@ solutions = [
1280
1301
  solutions."""
1281
1302
  for dep in self.dependencies:
1282
1303
  if dep.managed and dep.url:
1283
- scm = gclient_scm.CreateSCM(
1284
- dep.url, self.root_dir, dep.name, self.outbuf)
1304
+ scm = dep.CreateSCM()
1285
1305
  actual_url = scm.GetActualRemoteURL(self._options)
1286
1306
  if actual_url and not scm.DoesRemoteURLMatch(self._options):
1287
1307
  mirror = scm.GetCacheMirror()
@@ -1305,10 +1325,10 @@ You should ensure that the URL listed in .gclient is correct and either change
1305
1325
  it or fix the checkout.
1306
1326
  ''' % {'checkout_path': os.path.join(self.root_dir, dep.name),
1307
1327
  'expected_url': dep.url,
1308
- 'expected_scm': gclient_scm.GetScmName(dep.url),
1328
+ 'expected_scm': dep.GetScmName(),
1309
1329
  'mirror_string' : mirror_string,
1310
1330
  'actual_url': actual_url,
1311
- 'actual_scm': gclient_scm.GetScmName(actual_url)})
1331
+ 'actual_scm': dep.GetScmName()})
1312
1332
 
1313
1333
  def SetConfig(self, content):
1314
1334
  assert not self.dependencies
@@ -1326,6 +1346,13 @@ it or fix the checkout.
1326
1346
  else:
1327
1347
  self._enforced_os = tuple(set(self._enforced_os).union(target_os))
1328
1348
 
1349
+ # Append any target CPU that is not already being enforced to the tuple.
1350
+ target_cpu = config_dict.get('target_cpu', [])
1351
+ if config_dict.get('target_cpu_only', False):
1352
+ self._enforced_cpu = tuple(set(target_cpu))
1353
+ else:
1354
+ self._enforced_cpu = tuple(set(self._enforced_cpu).union(target_cpu))
1355
+
1329
1356
  cache_dir = config_dict.get('cache_dir', self._options.cache_dir)
1330
1357
  if cache_dir:
1331
1358
  cache_dir = os.path.join(self.root_dir, cache_dir)
@@ -1338,20 +1365,27 @@ it or fix the checkout.
1338
1365
  raise gclient_utils.Error('Can\'t use target_os_only if target_os is '
1339
1366
  'not specified')
1340
1367
 
1368
+ if not target_cpu and config_dict.get('target_cpu_only', False):
1369
+ raise gclient_utils.Error('Can\'t use target_cpu_only if target_cpu is '
1370
+ 'not specified')
1371
+
1341
1372
  deps_to_add = []
1342
1373
  for s in config_dict.get('solutions', []):
1343
1374
  try:
1344
- deps_to_add.append(Dependency(
1345
- self, s['name'], s['url'], s['url'],
1346
- s.get('managed', True),
1347
- s.get('custom_deps', {}),
1348
- s.get('custom_vars', {}),
1349
- s.get('custom_hooks', []),
1350
- s.get('deps_file', 'DEPS'),
1351
- True,
1352
- None,
1353
- None,
1354
- True))
1375
+ deps_to_add.append(GitDependency(
1376
+ parent=self,
1377
+ name=s['name'],
1378
+ url=s['url'],
1379
+ managed=s.get('managed', True),
1380
+ custom_deps=s.get('custom_deps', {}),
1381
+ custom_vars=s.get('custom_vars', {}),
1382
+ custom_hooks=s.get('custom_hooks', []),
1383
+ deps_file=s.get('deps_file', 'DEPS'),
1384
+ should_process=True,
1385
+ should_recurse=True,
1386
+ relative=None,
1387
+ condition=None,
1388
+ print_outbuf=True))
1355
1389
  except KeyError:
1356
1390
  raise gclient_utils.Error('Invalid .gclient file. Solution is '
1357
1391
  'incomplete: %s' % s)
@@ -1417,7 +1451,7 @@ it or fix the checkout.
1417
1451
  result = 'entries = {\n'
1418
1452
  for entry in self.root.subtree(False):
1419
1453
  result += ' %s: %s,\n' % (pprint.pformat(entry.name),
1420
- pprint.pformat(entry.parsed_url))
1454
+ pprint.pformat(entry.url))
1421
1455
  result += '}\n'
1422
1456
  file_path = os.path.join(self.root_dir, self._options.entries_filename)
1423
1457
  logging.debug(result)
@@ -1445,10 +1479,6 @@ it or fix the checkout.
1445
1479
  revision_overrides = {}
1446
1480
  if self._options.head:
1447
1481
  return revision_overrides
1448
- if not self._options.revisions:
1449
- for s in self.dependencies:
1450
- if not s.managed:
1451
- self._options.revisions.append('%s@unmanaged' % s.name)
1452
1482
  if not self._options.revisions:
1453
1483
  return revision_overrides
1454
1484
  solutions_names = [s.name for s in self.dependencies]
@@ -1462,6 +1492,20 @@ it or fix the checkout.
1462
1492
  index += 1
1463
1493
  return revision_overrides
1464
1494
 
1495
+ def _EnforcePatchRefs(self):
1496
+ """Checks for patch refs."""
1497
+ patch_refs = {}
1498
+ if not self._options.patch_refs:
1499
+ return patch_refs
1500
+ for given_patch_ref in self._options.patch_refs:
1501
+ patch_repo, _, patch_ref = given_patch_ref.partition('@')
1502
+ if not patch_repo or not patch_ref:
1503
+ raise gclient_utils.Error(
1504
+ 'Wrong revision format: %s should be of the form '
1505
+ 'patch_repo@patch_ref.' % given_patch_ref)
1506
+ patch_refs[patch_repo] = patch_ref
1507
+ return patch_refs
1508
+
1465
1509
  def RunOnDeps(self, command, args, ignore_requirements=False, progress=True):
1466
1510
  """Runs a command on each dependency in a client and its dependencies.
1467
1511
 
@@ -1473,12 +1517,16 @@ it or fix the checkout.
1473
1517
  raise gclient_utils.Error('No solution specified')
1474
1518
 
1475
1519
  revision_overrides = {}
1520
+ patch_refs = {}
1476
1521
  # It's unnecessary to check for revision overrides for 'recurse'.
1477
1522
  # Save a few seconds by not calling _EnforceRevisions() in that case.
1478
1523
  if command not in ('diff', 'recurse', 'runhooks', 'status', 'revert',
1479
1524
  'validate'):
1480
1525
  self._CheckConfig()
1481
1526
  revision_overrides = self._EnforceRevisions()
1527
+
1528
+ if command == 'update':
1529
+ patch_refs = self._EnforcePatchRefs()
1482
1530
  # Disable progress for non-tty stdout.
1483
1531
  should_show_progress = (
1484
1532
  setup_color.IS_TTY and not self._options.verbose and progress)
@@ -1494,15 +1542,32 @@ it or fix the checkout.
1494
1542
  for s in self.dependencies:
1495
1543
  if s.should_process:
1496
1544
  work_queue.enqueue(s)
1497
- work_queue.flush(revision_overrides, command, args, options=self._options)
1545
+ work_queue.flush(revision_overrides, command, args, options=self._options,
1546
+ patch_refs=patch_refs)
1547
+
1498
1548
  if revision_overrides:
1499
1549
  print('Please fix your script, having invalid --revision flags will soon '
1500
- 'considered an error.', file=sys.stderr)
1550
+ 'be considered an error.', file=sys.stderr)
1551
+
1552
+ if patch_refs:
1553
+ raise gclient_utils.Error(
1554
+ 'The following --patch-ref flags were not used. Please fix it:\n%s' %
1555
+ ('\n'.join(
1556
+ patch_repo + '@' + patch_ref
1557
+ for patch_repo, patch_ref in patch_refs.iteritems())))
1558
+
1559
+ if self._cipd_root:
1560
+ self._cipd_root.run(command)
1501
1561
 
1502
1562
  # Once all the dependencies have been processed, it's now safe to write
1503
- # out any gn_args_files and run the hooks.
1563
+ # out the gn_args_file and run the hooks.
1504
1564
  if command == 'update':
1505
- self.WriteGNArgsFilesRecursively(self.dependencies)
1565
+ gn_args_dep = self.dependencies[0]
1566
+ if gn_args_dep._gn_args_from:
1567
+ deps_map = dict([(dep.name, dep) for dep in gn_args_dep.dependencies])
1568
+ gn_args_dep = deps_map.get(gn_args_dep._gn_args_from)
1569
+ if gn_args_dep and gn_args_dep.HasGNArgsFile():
1570
+ gn_args_dep.WriteGNArgsFile()
1506
1571
 
1507
1572
  if not self._options.nohooks:
1508
1573
  if should_show_progress:
@@ -1529,7 +1594,7 @@ it or fix the checkout.
1529
1594
  (not any(path.startswith(entry + '/') for path in entries)) and
1530
1595
  os.path.exists(e_dir)):
1531
1596
  # The entry has been removed from DEPS.
1532
- scm = gclient_scm.CreateSCM(
1597
+ scm = gclient_scm.GitWrapper(
1533
1598
  prev_url, self.root_dir, entry_fixed, self.outbuf)
1534
1599
 
1535
1600
  # Check to see if this directory is now part of a higher-up checkout.
@@ -1612,56 +1677,66 @@ it or fix the checkout.
1612
1677
  for s in self.dependencies:
1613
1678
  if s.should_process:
1614
1679
  work_queue.enqueue(s)
1615
- work_queue.flush({}, None, [], options=self._options)
1680
+ work_queue.flush({}, None, [], options=self._options, patch_refs=None)
1616
1681
 
1617
- def GetURLAndRev(dep):
1618
- """Returns the revision-qualified SCM url for a Dependency."""
1619
- if dep.parsed_url is None:
1620
- return None
1621
- url, _ = gclient_utils.SplitUrlRevision(dep.parsed_url)
1622
- scm = gclient_scm.CreateSCM(
1623
- dep.parsed_url, self.root_dir, dep.name, self.outbuf)
1624
- if not os.path.isdir(scm.checkout_path):
1625
- return None
1626
- return '%s@%s' % (url, scm.revinfo(self._options, [], None))
1682
+ def ShouldPrintRevision(dep):
1683
+ return (not self._options.filter
1684
+ or dep.FuzzyMatchUrl(self._options.filter))
1627
1685
 
1628
1686
  if self._options.snapshot:
1629
- new_gclient = ''
1687
+ json_output = []
1630
1688
  # First level at .gclient
1631
1689
  for d in self.dependencies:
1632
1690
  entries = {}
1633
1691
  def GrabDeps(dep):
1634
1692
  """Recursively grab dependencies."""
1635
1693
  for d in dep.dependencies:
1636
- entries[d.name] = GetURLAndRev(d)
1694
+ d.PinToActualRevision()
1695
+ if ShouldPrintRevision(d):
1696
+ entries[d.name] = d.url
1637
1697
  GrabDeps(d)
1638
1698
  GrabDeps(d)
1639
- custom_deps = []
1640
- for k in sorted(entries.keys()):
1641
- if entries[k]:
1642
- # Quotes aren't escaped...
1643
- custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
1644
- else:
1645
- custom_deps.append(' \"%s\": None,\n' % k)
1646
- new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
1647
- 'solution_name': d.name,
1699
+ json_output.append({
1700
+ 'name': d.name,
1648
1701
  'solution_url': d.url,
1649
1702
  'deps_file': d.deps_file,
1650
1703
  'managed': d.managed,
1651
- 'solution_deps': ''.join(custom_deps),
1652
- }
1653
- # Print the snapshot configuration file
1654
- print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
1704
+ 'custom_deps': entries,
1705
+ })
1706
+ if self._options.output_json == '-':
1707
+ print(json.dumps(json_output, indent=2, separators=(',', ': ')))
1708
+ elif self._options.output_json:
1709
+ with open(self._options.output_json, 'w') as f:
1710
+ json.dump(json_output, f)
1711
+ else:
1712
+ # Print the snapshot configuration file
1713
+ print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {
1714
+ 'solution_list': pprint.pformat(json_output, indent=2),
1715
+ })
1655
1716
  else:
1656
1717
  entries = {}
1657
1718
  for d in self.root.subtree(False):
1658
1719
  if self._options.actual:
1659
- entries[d.name] = GetURLAndRev(d)
1720
+ d.PinToActualRevision()
1721
+ if ShouldPrintRevision(d):
1722
+ entries[d.name] = d.url
1723
+ if self._options.output_json:
1724
+ json_output = {
1725
+ name: {
1726
+ 'url': rev.split('@')[0] if rev else None,
1727
+ 'rev': rev.split('@')[1] if rev and '@' in rev else None,
1728
+ }
1729
+ for name, rev in entries.iteritems()
1730
+ }
1731
+ if self._options.output_json == '-':
1732
+ print(json.dumps(json_output, indent=2, separators=(',', ': ')))
1660
1733
  else:
1661
- entries[d.name] = d.parsed_url
1662
- keys = sorted(entries.keys())
1663
- for x in keys:
1664
- print('%s: %s' % (x, entries[x]))
1734
+ with open(self._options.output_json, 'w') as f:
1735
+ json.dump(json_output, f)
1736
+ else:
1737
+ keys = sorted(entries.keys())
1738
+ for x in keys:
1739
+ print('%s: %s' % (x, entries[x]))
1665
1740
  logging.info(str(self))
1666
1741
 
1667
1742
  def ParseDepsFile(self):
@@ -1674,6 +1749,17 @@ it or fix the checkout.
1674
1749
  print('Loaded .gclient config in %s:\n%s' % (
1675
1750
  self.root_dir, self.config_content))
1676
1751
 
1752
+ def GetCipdRoot(self):
1753
+ if not self._cipd_root:
1754
+ self._cipd_root = gclient_scm.CipdRoot(
1755
+ self.root_dir,
1756
+ # TODO(jbudorick): Support other service URLs as necessary.
1757
+ # Service URLs should be constant over the scope of a cipd
1758
+ # root, so a var per DEPS file specifying the service URL
1759
+ # should suffice.
1760
+ 'https://chrome-infra-packages.appspot.com')
1761
+ return self._cipd_root
1762
+
1677
1763
  @property
1678
1764
  def root_dir(self):
1679
1765
  """Root directory of gclient checkout."""
@@ -1685,18 +1771,122 @@ it or fix the checkout.
1685
1771
  return self._enforced_os
1686
1772
 
1687
1773
  @property
1688
- def recursion_limit(self):
1689
- """How recursive can each dependencies in DEPS file can load DEPS file."""
1690
- return self._recursion_limit
1774
+ def target_os(self):
1775
+ return self._enforced_os
1691
1776
 
1692
1777
  @property
1693
- def try_recursedeps(self):
1694
- """Whether to attempt using recursedeps-style recursion processing."""
1778
+ def target_cpu(self):
1779
+ return self._enforced_cpu
1780
+
1781
+
1782
+ class CipdDependency(Dependency):
1783
+ """A Dependency object that represents a single CIPD package."""
1784
+
1785
+ def __init__(
1786
+ self, parent, name, dep_value, cipd_root,
1787
+ custom_vars, should_process, relative, condition):
1788
+ package = dep_value['package']
1789
+ version = dep_value['version']
1790
+ url = urlparse.urljoin(
1791
+ cipd_root.service_url, '%s@%s' % (package, version))
1792
+ super(CipdDependency, self).__init__(
1793
+ parent=parent,
1794
+ name=name + ':' + package,
1795
+ url=url,
1796
+ managed=None,
1797
+ custom_deps=None,
1798
+ custom_vars=custom_vars,
1799
+ custom_hooks=None,
1800
+ deps_file=None,
1801
+ should_process=should_process,
1802
+ should_recurse=False,
1803
+ relative=relative,
1804
+ condition=condition)
1805
+ if relative:
1806
+ # TODO(jbudorick): Implement relative if necessary.
1807
+ raise gclient_utils.Error(
1808
+ 'Relative CIPD dependencies are not currently supported.')
1809
+ self._cipd_package = None
1810
+ self._cipd_root = cipd_root
1811
+ # CIPD wants /-separated paths, even on Windows.
1812
+ native_subdir_path = os.path.relpath(
1813
+ os.path.join(self.root.root_dir, name), cipd_root.root_dir)
1814
+ self._cipd_subdir = posixpath.join(*native_subdir_path.split(os.sep))
1815
+ self._package_name = package
1816
+ self._package_version = version
1817
+
1818
+ #override
1819
+ def run(self, revision_overrides, command, args, work_queue, options,
1820
+ patch_refs):
1821
+ """Runs |command| then parse the DEPS file."""
1822
+ logging.info('CipdDependency(%s).run()' % self.name)
1823
+ if not self.should_process:
1824
+ return
1825
+ self._CreatePackageIfNecessary()
1826
+ super(CipdDependency, self).run(revision_overrides, command, args,
1827
+ work_queue, options, patch_refs)
1828
+
1829
+ def _CreatePackageIfNecessary(self):
1830
+ # We lazily create the CIPD package to make sure that only packages
1831
+ # that we want (as opposed to all packages defined in all DEPS files
1832
+ # we parse) get added to the root and subsequently ensured.
1833
+ if not self._cipd_package:
1834
+ self._cipd_package = self._cipd_root.add_package(
1835
+ self._cipd_subdir, self._package_name, self._package_version)
1836
+
1837
+ def ParseDepsFile(self):
1838
+ """CIPD dependencies are not currently allowed to have nested deps."""
1839
+ self.add_dependencies_and_close([], [])
1840
+
1841
+ #override
1842
+ def verify_validity(self):
1843
+ """CIPD dependencies allow duplicate name for packages in same directory."""
1844
+ logging.info('Dependency(%s).verify_validity()' % self.name)
1695
1845
  return True
1696
1846
 
1697
- @property
1698
- def target_os(self):
1699
- return self._enforced_os
1847
+ #override
1848
+ def GetScmName(self):
1849
+ """Always 'cipd'."""
1850
+ return 'cipd'
1851
+
1852
+ #override
1853
+ def CreateSCM(self, out_cb=None):
1854
+ """Create a Wrapper instance suitable for handling this CIPD dependency."""
1855
+ self._CreatePackageIfNecessary()
1856
+ return gclient_scm.CipdWrapper(
1857
+ self.url, self.root.root_dir, self.name, self.outbuf, out_cb,
1858
+ root=self._cipd_root, package=self._cipd_package)
1859
+
1860
+ def ToLines(self):
1861
+ """Return a list of lines representing this in a DEPS file."""
1862
+ s = []
1863
+ self._CreatePackageIfNecessary()
1864
+ if self._cipd_package.authority_for_subdir:
1865
+ condition_part = ([' "condition": %r,' % self.condition]
1866
+ if self.condition else [])
1867
+ s.extend([
1868
+ ' # %s' % self.hierarchy(include_url=False),
1869
+ ' "%s": {' % (self.name.split(':')[0],),
1870
+ ' "packages": [',
1871
+ ])
1872
+ for p in sorted(
1873
+ self._cipd_root.packages(self._cipd_subdir),
1874
+ cmp=lambda x, y: cmp(x.name, y.name)):
1875
+ s.extend([
1876
+ ' {',
1877
+ ' "package": "%s",' % p.name,
1878
+ ' "version": "%s",' % p.version,
1879
+ ' },',
1880
+ ])
1881
+
1882
+ s.extend([
1883
+ ' ],',
1884
+ ' "dep_type": "cipd",',
1885
+ ] + condition_part + [
1886
+ ' },',
1887
+ '',
1888
+ ])
1889
+ return s
1700
1890
 
1701
1891
 
1702
1892
  #### gclient commands.
@@ -1775,9 +1965,7 @@ class Flattener(object):
1775
1965
 
1776
1966
  self._allowed_hosts = set()
1777
1967
  self._deps = {}
1778
- self._deps_os = {}
1779
1968
  self._hooks = []
1780
- self._hooks_os = {}
1781
1969
  self._pre_deps_hooks = []
1782
1970
  self._vars = {}
1783
1971
 
@@ -1798,24 +1986,16 @@ class Flattener(object):
1798
1986
  Arguments:
1799
1987
  dep (Dependency): dependency to process
1800
1988
  """
1801
- if dep.parsed_url is None:
1989
+ if dep.url is None:
1802
1990
  return
1803
1991
 
1804
1992
  # Make sure the revision is always fully specified (a hash),
1805
1993
  # as opposed to refs or tags which might change. Similarly,
1806
1994
  # shortened shas might become ambiguous; make sure to always
1807
1995
  # use full one for pinning.
1808
- url, revision = gclient_utils.SplitUrlRevision(dep.parsed_url)
1809
- if revision and gclient_utils.IsFullGitSha(revision):
1810
- return
1811
-
1812
- scm = gclient_scm.CreateSCM(
1813
- dep.parsed_url, self._client.root_dir, dep.name, dep.outbuf)
1814
- revinfo = scm.revinfo(self._client._options, [], None)
1815
-
1816
- dep._parsed_url = dep._url = '%s@%s' % (url, revinfo)
1817
- raw_url, _ = gclient_utils.SplitUrlRevision(dep._raw_url)
1818
- dep._raw_url = '%s@%s' % (raw_url, revinfo)
1996
+ revision = gclient_utils.SplitUrlRevision(dep.url)[1]
1997
+ if not revision or not gclient_utils.IsFullGitSha(revision):
1998
+ dep.PinToActualRevision()
1819
1999
 
1820
2000
  def _flatten(self, pin_all_deps=False):
1821
2001
  """Runs the flattener. Saves resulting DEPS string.
@@ -1832,14 +2012,9 @@ class Flattener(object):
1832
2012
  for dep in self._deps.itervalues():
1833
2013
  self._pin_dep(dep)
1834
2014
 
1835
- for os_deps in self._deps_os.itervalues():
1836
- for dep in os_deps.itervalues():
1837
- self._pin_dep(dep)
1838
-
1839
2015
  def add_deps_file(dep):
1840
2016
  # Only include DEPS files referenced by recursedeps.
1841
- if not (dep.parent is None or
1842
- (dep.name in (dep.parent.recursedeps or {}))):
2017
+ if not dep.should_recurse:
1843
2018
  return
1844
2019
  deps_file = dep.deps_file
1845
2020
  deps_path = os.path.join(self._client.root_dir, dep.name, deps_file)
@@ -1850,27 +2025,22 @@ class Flattener(object):
1850
2025
  deps_path = os.path.join(self._client.root_dir, dep.name, deps_file)
1851
2026
  if not os.path.exists(deps_path):
1852
2027
  return
1853
- assert dep.parsed_url
1854
- self._deps_files.add((dep.parsed_url, deps_file))
2028
+ assert dep.url
2029
+ self._deps_files.add((dep.url, deps_file, dep.hierarchy_data()))
1855
2030
  for dep in self._deps.itervalues():
1856
2031
  add_deps_file(dep)
1857
- for os_deps in self._deps_os.itervalues():
1858
- for dep in os_deps.itervalues():
1859
- add_deps_file(dep)
1860
2032
 
2033
+ gn_args_dep = self._deps.get(self._client.dependencies[0]._gn_args_from,
2034
+ self._client.dependencies[0])
1861
2035
  self._deps_string = '\n'.join(
1862
- _GNSettingsToLines(
1863
- self._client.dependencies[0]._gn_args_file,
1864
- self._client.dependencies[0]._gn_args) +
2036
+ _GNSettingsToLines(gn_args_dep._gn_args_file, gn_args_dep._gn_args) +
1865
2037
  _AllowedHostsToLines(self._allowed_hosts) +
1866
2038
  _DepsToLines(self._deps) +
1867
- _DepsOsToLines(self._deps_os) +
1868
2039
  _HooksToLines('hooks', self._hooks) +
1869
2040
  _HooksToLines('pre_deps_hooks', self._pre_deps_hooks) +
1870
- _HooksOsToLines(self._hooks_os) +
1871
2041
  _VarsToLines(self._vars) +
1872
2042
  ['# %s, %s' % (url, deps_file)
1873
- for url, deps_file in sorted(self._deps_files)] +
2043
+ for url, deps_file, _ in sorted(self._deps_files)] +
1874
2044
  ['']) # Ensure newline at end of file.
1875
2045
 
1876
2046
  def _add_dep(self, dep):
@@ -1884,81 +2054,50 @@ class Flattener(object):
1884
2054
  if dep.url:
1885
2055
  self._deps[dep.name] = dep
1886
2056
 
1887
- def _add_os_dep(self, os_dep, dep_os):
1888
- """Helper to add an OS-specific dependency to flattened DEPS.
1889
-
1890
- Arguments:
1891
- os_dep (Dependency): dependency to add
1892
- dep_os (str): name of the OS
1893
- """
1894
- assert (
1895
- os_dep.name not in self._deps_os.get(dep_os, {}) or
1896
- self._deps_os.get(dep_os, {}).get(os_dep.name) == os_dep), (
1897
- os_dep.name, self._deps_os.get(dep_os, {}).get(os_dep.name))
1898
- if os_dep.url:
1899
- # OS-specific deps need to have their full URL resolved manually.
1900
- assert not os_dep.parsed_url, (os_dep, os_dep.parsed_url)
1901
- os_dep._parsed_url = os_dep.LateOverride(os_dep.url)
1902
-
1903
- self._deps_os.setdefault(dep_os, {})[os_dep.name] = os_dep
1904
-
1905
- def _flatten_dep(self, dep, dep_os=None):
2057
+ def _flatten_dep(self, dep):
1906
2058
  """Visits a dependency in order to flatten it (see CMDflatten).
1907
2059
 
1908
2060
  Arguments:
1909
2061
  dep (Dependency): dependency to process
1910
- dep_os (str or None): name of the OS |dep| is specific to
1911
2062
  """
1912
- logging.debug('_flatten_dep(%s, %s)', dep.name, dep_os)
2063
+ logging.debug('_flatten_dep(%s)', dep.name)
1913
2064
 
1914
- if not dep.deps_parsed:
1915
- dep.ParseDepsFile()
2065
+ assert dep.deps_parsed, (
2066
+ "Attempted to flatten %s but it has not been processed." % dep.name)
1916
2067
 
1917
2068
  self._allowed_hosts.update(dep.allowed_hosts)
1918
2069
 
1919
- # Only include vars listed in the DEPS files, not possible local overrides.
2070
+ # Only include vars explicitly listed in the DEPS files or gclient solution,
2071
+ # not automatic, local overrides (i.e. not all of dep.get_vars()).
2072
+ hierarchy = dep.hierarchy(include_url=False)
1920
2073
  for key, value in dep._vars.iteritems():
1921
2074
  # Make sure there are no conflicting variables. It is fine however
1922
2075
  # to use same variable name, as long as the value is consistent.
1923
2076
  assert key not in self._vars or self._vars[key][1] == value
1924
- self._vars[key] = (dep, value)
2077
+ self._vars[key] = (hierarchy, value)
2078
+ # Override explicit custom variables.
2079
+ for key, value in dep.custom_vars.iteritems():
2080
+ # Do custom_vars that don't correspond to DEPS vars ever make sense? DEPS
2081
+ # conditionals shouldn't be using vars that aren't also defined in the
2082
+ # DEPS (presubmit actually disallows this), so any new custom_var must be
2083
+ # unused in the DEPS, so no need to add it to the flattened output either.
2084
+ if key not in self._vars:
2085
+ continue
2086
+ # Don't "override" existing vars if it's actually the same value.
2087
+ elif self._vars[key][1] == value:
2088
+ continue
2089
+ # Anything else is overriding a default value from the DEPS.
2090
+ self._vars[key] = (hierarchy + ' [custom_var override]', value)
1925
2091
 
1926
2092
  self._pre_deps_hooks.extend([(dep, hook) for hook in dep.pre_deps_hooks])
1927
-
1928
- if dep_os:
1929
- if dep.deps_hooks:
1930
- self._hooks_os.setdefault(dep_os, []).extend(
1931
- [(dep, hook) for hook in dep.deps_hooks])
1932
- else:
1933
- self._hooks.extend([(dep, hook) for hook in dep.deps_hooks])
2093
+ self._hooks.extend([(dep, hook) for hook in dep.deps_hooks])
1934
2094
 
1935
2095
  for sub_dep in dep.dependencies:
1936
- if dep_os:
1937
- self._add_os_dep(sub_dep, dep_os)
1938
- else:
1939
- self._add_dep(sub_dep)
1940
-
1941
- for hook_os, os_hooks in dep.os_deps_hooks.iteritems():
1942
- self._hooks_os.setdefault(hook_os, []).extend(
1943
- [(dep, hook) for hook in os_hooks])
1944
-
1945
- for sub_dep_os, os_deps in dep.os_dependencies.iteritems():
1946
- for os_dep in os_deps:
1947
- self._add_os_dep(os_dep, sub_dep_os)
1948
-
1949
- # Process recursedeps. |deps_by_name| is a map where keys are dependency
1950
- # names, and values are maps of OS names to |Dependency| instances.
1951
- # |None| in place of OS name means the dependency is not OS-specific.
1952
- deps_by_name = dict((d.name, {None: d}) for d in dep.dependencies)
1953
- for sub_dep_os, os_deps in dep.os_dependencies.iteritems():
1954
- for os_dep in os_deps:
1955
- assert sub_dep_os not in deps_by_name.get(os_dep.name, {}), (
1956
- os_dep.name, sub_dep_os)
1957
- deps_by_name.setdefault(os_dep.name, {})[sub_dep_os] = os_dep
1958
- for recurse_dep_name in (dep.recursedeps or []):
1959
- dep_info = deps_by_name[recurse_dep_name]
1960
- for sub_dep_os, os_dep in dep_info.iteritems():
1961
- self._flatten_dep(os_dep, dep_os=(sub_dep_os or dep_os))
2096
+ self._add_dep(sub_dep)
2097
+
2098
+ for d in dep.dependencies:
2099
+ if d.should_recurse:
2100
+ self._flatten_dep(d)
1962
2101
 
1963
2102
 
1964
2103
  def CMDflatten(parser, args):
@@ -1974,7 +2113,6 @@ def CMDflatten(parser, args):
1974
2113
  'for checked out deps, NOT deps_os.'))
1975
2114
  options, args = parser.parse_args(args)
1976
2115
 
1977
- options.do_not_merge_os_specific_entries = True
1978
2116
  options.nohooks = True
1979
2117
  options.process_all_deps = True
1980
2118
  client = GClient.LoadCurrentConfig(options)
@@ -1993,7 +2131,7 @@ def CMDflatten(parser, args):
1993
2131
  else:
1994
2132
  print(flattener.deps_string)
1995
2133
 
1996
- deps_files = [{'url': d[0], 'deps_file': d[1]}
2134
+ deps_files = [{'url': d[0], 'deps_file': d[1], 'hierarchy': d[2]}
1997
2135
  for d in sorted(flattener.deps_files)]
1998
2136
  if options.output_deps_files:
1999
2137
  with open(options.output_deps_files, 'w') as f:
@@ -2028,17 +2166,8 @@ def _DepsToLines(deps):
2028
2166
  if not deps:
2029
2167
  return []
2030
2168
  s = ['deps = {']
2031
- for name, dep in sorted(deps.iteritems()):
2032
- condition_part = ([' "condition": %r,' % dep.condition]
2033
- if dep.condition else [])
2034
- s.extend([
2035
- ' # %s' % dep.hierarchy(include_url=False),
2036
- ' "%s": {' % (name,),
2037
- ' "url": "%s",' % (dep.raw_url,),
2038
- ] + condition_part + [
2039
- ' },',
2040
- '',
2041
- ])
2169
+ for _, dep in sorted(deps.iteritems()):
2170
+ s.extend(dep.ToLines())
2042
2171
  s.extend(['}', ''])
2043
2172
  return s
2044
2173
 
@@ -2056,7 +2185,7 @@ def _DepsOsToLines(deps_os):
2056
2185
  s.extend([
2057
2186
  ' # %s' % dep.hierarchy(include_url=False),
2058
2187
  ' "%s": {' % (name,),
2059
- ' "url": "%s",' % (dep.raw_url,),
2188
+ ' "url": "%s",' % (dep.url,),
2060
2189
  ] + condition_part + [
2061
2190
  ' },',
2062
2191
  '',
@@ -2129,9 +2258,9 @@ def _VarsToLines(variables):
2129
2258
  return []
2130
2259
  s = ['vars = {']
2131
2260
  for key, tup in sorted(variables.iteritems()):
2132
- dep, value = tup
2261
+ hierarchy, value = tup
2133
2262
  s.extend([
2134
- ' # %s' % dep.hierarchy(include_url=False),
2263
+ ' # %s' % hierarchy,
2135
2264
  ' "%s": %r,' % (key, value),
2136
2265
  '',
2137
2266
  ])
@@ -2331,9 +2460,21 @@ def CMDsync(parser, args):
2331
2460
  dest='revisions', metavar='REV', default=[],
2332
2461
  help='Enforces revision/hash for the solutions with the '
2333
2462
  'format src@rev. The src@ part is optional and can be '
2334
- 'skipped. -r can be used multiple times when .gclient '
2335
- 'has multiple solutions configured and will work even '
2336
- 'if the src@ part is skipped.')
2463
+ 'skipped. You can also specify URLs instead of paths '
2464
+ 'and gclient will find the solution corresponding to '
2465
+ 'the given URL. If a path is also specified, the URL '
2466
+ 'takes precedence. -r can be used multiple times when '
2467
+ '.gclient has multiple solutions configured, and will '
2468
+ 'work even if the src@ part is skipped.')
2469
+ parser.add_option('--patch-ref', action='append',
2470
+ dest='patch_refs', metavar='GERRIT_REF', default=[],
2471
+ help='Patches the given reference with the format dep@ref. '
2472
+ 'For dep, you can specify URLs as well as paths, with '
2473
+ 'URLs taking preference. The reference will be '
2474
+ 'applied to the necessary path, will be rebased on '
2475
+ 'top what the dep was synced to, and then will do a '
2476
+ 'soft reset. Use --no-rebase-patch-ref and '
2477
+ '--reset-patch-ref to disable this behavior.')
2337
2478
  parser.add_option('--with_branch_heads', action='store_true',
2338
2479
  help='Clone git "branch_heads" refspecs in addition to '
2339
2480
  'the default refspecs. This adds about 1/2GB to a '
@@ -2363,10 +2504,6 @@ def CMDsync(parser, args):
2363
2504
  help='override deps for the specified (comma-separated) '
2364
2505
  'platform(s); \'all\' will process all deps_os '
2365
2506
  'references')
2366
- # TODO(phajdan.jr): use argparse.SUPPRESS to hide internal flags.
2367
- parser.add_option('--do-not-merge-os-specific-entries', action='store_true',
2368
- help='INTERNAL ONLY - disables merging of deps_os and '
2369
- 'hooks_os to dependencies and hooks')
2370
2507
  parser.add_option('--process-all-deps', action='store_true',
2371
2508
  help='Check out all deps, even for different OS-es, '
2372
2509
  'or with conditions evaluating to false')
@@ -2405,6 +2542,12 @@ def CMDsync(parser, args):
2405
2542
  parser.add_option('--disable-syntax-validation', action='store_false',
2406
2543
  dest='validate_syntax',
2407
2544
  help='Disable validation of .gclient and DEPS syntax.')
2545
+ parser.add_option('--no-rebase-patch-ref', action='store_false',
2546
+ dest='rebase_patch_ref', default=True,
2547
+ help='Bypass rebase of the patch ref after checkout.')
2548
+ parser.add_option('--no-reset-patch-ref', action='store_false',
2549
+ dest='reset_patch_ref', default=True,
2550
+ help='Bypass calling reset after patching the ref.')
2408
2551
  (options, args) = parser.parse_args(args)
2409
2552
  client = GClient.LoadCurrentConfig(options)
2410
2553
 
@@ -2422,6 +2565,10 @@ def CMDsync(parser, args):
2422
2565
  slns = {}
2423
2566
  for d in client.subtree(True):
2424
2567
  normed = d.name.replace('\\', '/').rstrip('/') + '/'
2568
+ if normed in slns and not d.should_process:
2569
+ # If an unprocessed dependency would override an existing dependency,
2570
+ # ignore it.
2571
+ continue
2425
2572
  slns[normed] = {
2426
2573
  'revision': d.got_revision,
2427
2574
  'scm': d.used_scm.name if d.used_scm else None,
@@ -2533,6 +2680,12 @@ def CMDrevinfo(parser, args):
2533
2680
  help='creates a snapshot .gclient file of the current '
2534
2681
  'version of all repositories to reproduce the tree, '
2535
2682
  'implies -a')
2683
+ parser.add_option('--filter', action='append', dest='filter',
2684
+ help='Display revision information only for the specified '
2685
+ 'dependencies (filtered by URL or path).')
2686
+ parser.add_option('--output-json',
2687
+ help='Output a json document to this path containing '
2688
+ 'information about the revisions.')
2536
2689
  (options, args) = parser.parse_args(args)
2537
2690
  client = GClient.LoadCurrentConfig(options)
2538
2691
  if not client:
@@ -2541,6 +2694,109 @@ def CMDrevinfo(parser, args):
2541
2694
  return 0
2542
2695
 
2543
2696
 
2697
+ def CMDgetdep(parser, args):
2698
+ """Gets revision information and variable values from a DEPS file."""
2699
+ parser.add_option('--var', action='append',
2700
+ dest='vars', metavar='VAR', default=[],
2701
+ help='Gets the value of a given variable.')
2702
+ parser.add_option('-r', '--revision', action='append',
2703
+ dest='revisions', metavar='DEP', default=[],
2704
+ help='Gets the revision/version for the given dependency. '
2705
+ 'If it is a git dependency, dep must be a path. If it '
2706
+ 'is a CIPD dependency, dep must be of the form '
2707
+ 'path:package.')
2708
+ parser.add_option('--deps-file', default='DEPS',
2709
+ # TODO(ehmaldonado): Try to find the DEPS file pointed by
2710
+ # .gclient first.
2711
+ help='The DEPS file to be edited. Defaults to the DEPS '
2712
+ 'file in the current directory.')
2713
+ (options, args) = parser.parse_args(args)
2714
+
2715
+ if not os.path.isfile(options.deps_file):
2716
+ raise gclient_utils.Error(
2717
+ 'DEPS file %s does not exist.' % options.deps_file)
2718
+ with open(options.deps_file) as f:
2719
+ contents = f.read()
2720
+ local_scope = gclient_eval.Exec(contents, options.deps_file)
2721
+
2722
+ for var in options.vars:
2723
+ print(gclient_eval.GetVar(local_scope, var))
2724
+
2725
+ for name in options.revisions:
2726
+ if ':' in name:
2727
+ name, _, package = name.partition(':')
2728
+ if not name or not package:
2729
+ parser.error(
2730
+ 'Wrong CIPD format: %s:%s should be of the form path:pkg.'
2731
+ % (name, package))
2732
+ print(gclient_eval.GetCIPD(local_scope, name, package))
2733
+ else:
2734
+ print(gclient_eval.GetRevision(local_scope, name))
2735
+
2736
+
2737
+ def CMDsetdep(parser, args):
2738
+ """Modifies dependency revisions and variable values in a DEPS file"""
2739
+ parser.add_option('--var', action='append',
2740
+ dest='vars', metavar='VAR=VAL', default=[],
2741
+ help='Sets a variable to the given value with the format '
2742
+ 'name=value.')
2743
+ parser.add_option('-r', '--revision', action='append',
2744
+ dest='revisions', metavar='DEP@REV', default=[],
2745
+ help='Sets the revision/version for the dependency with '
2746
+ 'the format dep@rev. If it is a git dependency, dep '
2747
+ 'must be a path and rev must be a git hash or '
2748
+ 'reference (e.g. src/dep@deadbeef). If it is a CIPD '
2749
+ 'dependency, dep must be of the form path:package and '
2750
+ 'rev must be the package version '
2751
+ '(e.g. src/pkg:chromium/pkg@2.1-cr0).')
2752
+ parser.add_option('--deps-file', default='DEPS',
2753
+ # TODO(ehmaldonado): Try to find the DEPS file pointed by
2754
+ # .gclient first.
2755
+ help='The DEPS file to be edited. Defaults to the DEPS '
2756
+ 'file in the current directory.')
2757
+ (options, args) = parser.parse_args(args)
2758
+ if args:
2759
+ parser.error('Unused arguments: "%s"' % '" "'.join(args))
2760
+ if not options.revisions and not options.vars:
2761
+ parser.error(
2762
+ 'You must specify at least one variable or revision to modify.')
2763
+
2764
+ if not os.path.isfile(options.deps_file):
2765
+ raise gclient_utils.Error(
2766
+ 'DEPS file %s does not exist.' % options.deps_file)
2767
+ with open(options.deps_file) as f:
2768
+ contents = f.read()
2769
+ local_scope = gclient_eval.Exec(contents, options.deps_file)
2770
+
2771
+ for var in options.vars:
2772
+ name, _, value = var.partition('=')
2773
+ if not name or not value:
2774
+ parser.error(
2775
+ 'Wrong var format: %s should be of the form name=value.' % var)
2776
+ if name in local_scope['vars']:
2777
+ gclient_eval.SetVar(local_scope, name, value)
2778
+ else:
2779
+ gclient_eval.AddVar(local_scope, name, value)
2780
+
2781
+ for revision in options.revisions:
2782
+ name, _, value = revision.partition('@')
2783
+ if not name or not value:
2784
+ parser.error(
2785
+ 'Wrong dep format: %s should be of the form dep@rev.' % revision)
2786
+ if ':' in name:
2787
+ name, _, package = name.partition(':')
2788
+ if not name or not package:
2789
+ parser.error(
2790
+ 'Wrong CIPD format: %s:%s should be of the form path:pkg@version.'
2791
+ % (name, package))
2792
+ gclient_eval.SetCIPD(local_scope, name, package, value)
2793
+ else:
2794
+ gclient_eval.SetRevision(local_scope, name, value)
2795
+
2796
+ with open(options.deps_file, 'w') as f:
2797
+ f.write(gclient_eval.RenderDEPSFile(local_scope))
2798
+
2799
+
2544
2800
  def CMDverify(parser, args):
2545
2801
  """Verifies the DEPS file deps are only from allowed_hosts."""
2546
2802
  (options, args) = parser.parse_args(args)