Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/scripts/roll_chromium_deps.py
1693 views
1
#!/usr/bin/env python
2
# Copyright 2019 The ANGLE project authors. All Rights Reserved.
3
#
4
# Use of this source code is governed by a BSD-style license
5
# that can be found in the LICENSE file in the root of the source
6
# tree. An additional intellectual property rights grant can be found
7
# in the file PATENTS. All contributing project authors may
8
# be found in the AUTHORS file in the root of the source tree.
9
10
# This is a modified copy of the script in
11
# https://webrtc.googlesource.com/src/+/master/tools_webrtc/autoroller/roll_deps.py
12
# customized for ANGLE.
13
"""Script to automatically roll Chromium dependencies in the ANGLE DEPS file."""
14
15
import argparse
16
import base64
17
import collections
18
import logging
19
import os
20
import platform
21
import re
22
import subprocess
23
import sys
24
import urllib2
25
26
27
def FindSrcDirPath():
28
"""Returns the abs path to the root dir of the project."""
29
# Special cased for ANGLE.
30
return os.path.dirname(os.path.abspath(os.path.join(__file__, '..')))
31
32
33
ANGLE_CHROMIUM_DEPS = [
34
'build',
35
'buildtools',
36
'buildtools/clang_format/script',
37
'buildtools/linux64',
38
'buildtools/mac',
39
'buildtools/third_party/libc++/trunk',
40
'buildtools/third_party/libc++abi/trunk',
41
'buildtools/win',
42
'testing',
43
'third_party/abseil-cpp',
44
'third_party/android_build_tools',
45
'third_party/android_build_tools/aapt2',
46
'third_party/android_build_tools/art',
47
'third_party/android_build_tools/bundletool',
48
'third_party/android_deps',
49
'third_party/android_ndk',
50
'third_party/android_platform',
51
'third_party/android_sdk',
52
'third_party/android_sdk/androidx_browser/src',
53
'third_party/android_sdk/public',
54
'third_party/android_system_sdk',
55
'third_party/bazel',
56
'third_party/catapult',
57
'third_party/colorama/src',
58
'third_party/depot_tools',
59
'third_party/ijar',
60
'third_party/jdk',
61
'third_party/jdk/extras',
62
'third_party/jinja2',
63
'third_party/libjpeg_turbo',
64
'third_party/markupsafe',
65
'third_party/nasm',
66
'third_party/proguard',
67
'third_party/protobuf',
68
'third_party/Python-Markdown',
69
'third_party/qemu-linux-x64',
70
'third_party/qemu-mac-x64',
71
'third_party/r8',
72
'third_party/requests/src',
73
'third_party/six',
74
'third_party/turbine',
75
'third_party/zlib',
76
'tools/android/errorprone_plugin',
77
'tools/clang',
78
'tools/clang/dsymutil',
79
'tools/luci-go',
80
'tools/mb',
81
'tools/md_browser',
82
'tools/memory',
83
'tools/perf',
84
'tools/protoc_wrapper',
85
'tools/python',
86
'tools/skia_goldctl/linux',
87
'tools/skia_goldctl/mac',
88
'tools/skia_goldctl/win',
89
]
90
91
ANGLE_URL = 'https://chromium.googlesource.com/angle/angle'
92
CHROMIUM_SRC_URL = 'https://chromium.googlesource.com/chromium/src'
93
CHROMIUM_COMMIT_TEMPLATE = CHROMIUM_SRC_URL + '/+/%s'
94
CHROMIUM_LOG_TEMPLATE = CHROMIUM_SRC_URL + '/+log/%s'
95
CHROMIUM_FILE_TEMPLATE = CHROMIUM_SRC_URL + '/+/%s/%s'
96
97
COMMIT_POSITION_RE = re.compile('^Cr-Commit-Position: .*#([0-9]+).*$')
98
CLANG_REVISION_RE = re.compile(r'^CLANG_REVISION = \'([-0-9a-z]+)\'')
99
ROLL_BRANCH_NAME = 'roll_chromium_revision'
100
101
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
102
CHECKOUT_SRC_DIR = FindSrcDirPath()
103
CHECKOUT_ROOT_DIR = CHECKOUT_SRC_DIR
104
105
# Copied from tools/android/roll/android_deps/.../BuildConfigGenerator.groovy.
106
ANDROID_DEPS_START = r'=== ANDROID_DEPS Generated Code Start ==='
107
ANDROID_DEPS_END = r'=== ANDROID_DEPS Generated Code End ==='
108
# Location of automically gathered android deps.
109
ANDROID_DEPS_PATH = 'src/third_party/android_deps/'
110
111
NOTIFY_EMAIL = '[email protected]'
112
113
CLANG_TOOLS_URL = 'https://chromium.googlesource.com/chromium/src/tools/clang'
114
CLANG_FILE_TEMPLATE = CLANG_TOOLS_URL + '/+/%s/%s'
115
116
CLANG_TOOLS_PATH = 'tools/clang'
117
CLANG_UPDATE_SCRIPT_URL_PATH = 'scripts/update.py'
118
CLANG_UPDATE_SCRIPT_LOCAL_PATH = os.path.join(CHECKOUT_SRC_DIR, 'tools', 'clang', 'scripts',
119
'update.py')
120
121
DepsEntry = collections.namedtuple('DepsEntry', 'path url revision')
122
ChangedDep = collections.namedtuple('ChangedDep', 'path url current_rev new_rev')
123
ClangChange = collections.namedtuple('ClangChange', 'mirror_change clang_change')
124
CipdDepsEntry = collections.namedtuple('CipdDepsEntry', 'path packages')
125
ChangedCipdPackage = collections.namedtuple('ChangedCipdPackage',
126
'path package current_version new_version')
127
128
ChromiumRevisionUpdate = collections.namedtuple('ChromiumRevisionUpdate', ('current_chromium_rev '
129
'new_chromium_rev '))
130
131
132
def AddDepotToolsToPath():
133
sys.path.append(os.path.join(CHECKOUT_SRC_DIR, 'build'))
134
import find_depot_tools
135
find_depot_tools.add_depot_tools_to_path()
136
137
138
class RollError(Exception):
139
pass
140
141
142
def StrExpansion():
143
return lambda str_value: str_value
144
145
146
def VarLookup(local_scope):
147
return lambda var_name: local_scope['vars'][var_name]
148
149
150
def ParseDepsDict(deps_content):
151
local_scope = {}
152
global_scope = {
153
'Str': StrExpansion(),
154
'Var': VarLookup(local_scope),
155
'deps_os': {},
156
}
157
exec (deps_content, global_scope, local_scope)
158
return local_scope
159
160
161
def ParseLocalDepsFile(filename):
162
with open(filename, 'rb') as f:
163
deps_content = f.read()
164
return ParseDepsDict(deps_content)
165
166
167
def ParseCommitPosition(commit_message):
168
for line in reversed(commit_message.splitlines()):
169
m = COMMIT_POSITION_RE.match(line.strip())
170
if m:
171
return int(m.group(1))
172
logging.error('Failed to parse commit position id from:\n%s\n', commit_message)
173
sys.exit(-1)
174
175
176
def _RunCommand(command, working_dir=None, ignore_exit_code=False, extra_env=None,
177
input_data=None):
178
"""Runs a command and returns the output from that command.
179
180
If the command fails (exit code != 0), the function will exit the process.
181
182
Returns:
183
A tuple containing the stdout and stderr outputs as strings.
184
"""
185
working_dir = working_dir or CHECKOUT_SRC_DIR
186
logging.debug('CMD: %s CWD: %s', ' '.join(command), working_dir)
187
env = os.environ.copy()
188
if extra_env:
189
assert all(isinstance(value, str) for value in extra_env.values())
190
logging.debug('extra env: %s', extra_env)
191
env.update(extra_env)
192
p = subprocess.Popen(
193
command,
194
stdin=subprocess.PIPE,
195
stdout=subprocess.PIPE,
196
stderr=subprocess.PIPE,
197
env=env,
198
cwd=working_dir,
199
universal_newlines=True)
200
std_output, err_output = p.communicate(input_data)
201
p.stdout.close()
202
p.stderr.close()
203
if not ignore_exit_code and p.returncode != 0:
204
logging.error('Command failed: %s\n'
205
'stdout:\n%s\n'
206
'stderr:\n%s\n', ' '.join(command), std_output, err_output)
207
sys.exit(p.returncode)
208
return std_output, err_output
209
210
211
def _GetBranches():
212
"""Returns a tuple of active,branches.
213
214
The 'active' is the name of the currently active branch and 'branches' is a
215
list of all branches.
216
"""
217
lines = _RunCommand(['git', 'branch'])[0].split('\n')
218
branches = []
219
active = ''
220
for line in lines:
221
if '*' in line:
222
# The assumption is that the first char will always be the '*'.
223
active = line[1:].strip()
224
branches.append(active)
225
else:
226
branch = line.strip()
227
if branch:
228
branches.append(branch)
229
return active, branches
230
231
232
def _ReadGitilesContent(url):
233
# Download and decode BASE64 content until
234
# https://code.google.com/p/gitiles/issues/detail?id=7 is fixed.
235
logging.debug('Reading gitiles URL %s' % url)
236
base64_content = ReadUrlContent(url + '?format=TEXT')
237
return base64.b64decode(base64_content[0])
238
239
240
def ReadRemoteCrFile(path_below_src, revision):
241
"""Reads a remote Chromium file of a specific revision. Returns a string."""
242
return _ReadGitilesContent(CHROMIUM_FILE_TEMPLATE % (revision, path_below_src))
243
244
245
def ReadRemoteCrCommit(revision):
246
"""Reads a remote Chromium commit message. Returns a string."""
247
return _ReadGitilesContent(CHROMIUM_COMMIT_TEMPLATE % revision)
248
249
250
def ReadRemoteClangFile(path_below_src, revision):
251
"""Reads a remote Clang file of a specific revision. Returns a string."""
252
return _ReadGitilesContent(CLANG_FILE_TEMPLATE % (revision, path_below_src))
253
254
255
def ReadUrlContent(url):
256
"""Connect to a remote host and read the contents. Returns a list of lines."""
257
conn = urllib2.urlopen(url)
258
try:
259
return conn.readlines()
260
except IOError as e:
261
logging.exception('Error connecting to %s. Error: %s', url, e)
262
raise
263
finally:
264
conn.close()
265
266
267
def GetMatchingDepsEntries(depsentry_dict, dir_path):
268
"""Gets all deps entries matching the provided path.
269
270
This list may contain more than one DepsEntry object.
271
Example: dir_path='src/testing' would give results containing both
272
'src/testing/gtest' and 'src/testing/gmock' deps entries for Chromium's DEPS.
273
Example 2: dir_path='src/build' should return 'src/build' but not
274
'src/buildtools'.
275
276
Returns:
277
A list of DepsEntry objects.
278
"""
279
result = []
280
for path, depsentry in depsentry_dict.iteritems():
281
if path == dir_path:
282
result.append(depsentry)
283
else:
284
parts = path.split('/')
285
if all(part == parts[i] for i, part in enumerate(dir_path.split('/'))):
286
result.append(depsentry)
287
return result
288
289
290
def BuildDepsentryDict(deps_dict):
291
"""Builds a dict of paths to DepsEntry objects from a raw parsed deps dict."""
292
result = {}
293
294
def AddDepsEntries(deps_subdict):
295
for path, dep in deps_subdict.iteritems():
296
if path in result:
297
continue
298
if not isinstance(dep, dict):
299
dep = {'url': dep}
300
if dep.get('dep_type') == 'cipd':
301
result[path] = CipdDepsEntry(path, dep['packages'])
302
else:
303
if '@' not in dep['url']:
304
continue
305
url, revision = dep['url'].split('@')
306
result[path] = DepsEntry(path, url, revision)
307
308
AddDepsEntries(deps_dict['deps'])
309
for deps_os in ['win', 'mac', 'unix', 'android', 'ios', 'unix']:
310
AddDepsEntries(deps_dict.get('deps_os', {}).get(deps_os, {}))
311
return result
312
313
314
def _FindChangedCipdPackages(path, old_pkgs, new_pkgs):
315
pkgs_equal = ({p['package'] for p in old_pkgs} == {p['package'] for p in new_pkgs})
316
assert pkgs_equal, ('Old: %s\n New: %s.\nYou need to do a manual roll '
317
'and remove/add entries in DEPS so the old and new '
318
'list match.' % (old_pkgs, new_pkgs))
319
for old_pkg in old_pkgs:
320
for new_pkg in new_pkgs:
321
old_version = old_pkg['version']
322
new_version = new_pkg['version']
323
if (old_pkg['package'] == new_pkg['package'] and old_version != new_version):
324
logging.debug('Roll dependency %s to %s', path, new_version)
325
yield ChangedCipdPackage(path, old_pkg['package'], old_version, new_version)
326
327
328
def _FindNewDeps(old, new):
329
""" Gather dependencies only in |new| and return corresponding paths. """
330
old_entries = set(BuildDepsentryDict(old))
331
new_entries = set(BuildDepsentryDict(new))
332
return [path for path in new_entries - old_entries if path in ANGLE_CHROMIUM_DEPS]
333
334
335
def CalculateChangedDeps(angle_deps, new_cr_deps):
336
"""
337
Calculate changed deps entries based on entries defined in the ANGLE DEPS
338
file:
339
- If a shared dependency with the Chromium DEPS file: roll it to the same
340
revision as Chromium (i.e. entry in the new_cr_deps dict)
341
- If it's a Chromium sub-directory, roll it to the HEAD revision (notice
342
this means it may be ahead of the chromium_revision, but generally these
343
should be close).
344
- If it's another DEPS entry (not shared with Chromium), roll it to HEAD
345
unless it's configured to be skipped.
346
347
Returns:
348
A list of ChangedDep objects representing the changed deps.
349
"""
350
351
def ChromeURL(angle_deps_entry):
352
# Perform variable substitutions.
353
# This is a hack to get around the unsupported way this script parses DEPS.
354
# A better fix would be to use the gclient APIs to query and update DEPS.
355
# However this is complicated by how this script downloads DEPS remotely.
356
return angle_deps_entry.url.replace('{chromium_git}', 'https://chromium.googlesource.com')
357
358
result = []
359
angle_entries = BuildDepsentryDict(angle_deps)
360
new_cr_entries = BuildDepsentryDict(new_cr_deps)
361
for path, angle_deps_entry in angle_entries.iteritems():
362
if path not in ANGLE_CHROMIUM_DEPS:
363
continue
364
365
# All ANGLE Chromium dependencies are located in src/.
366
chrome_path = 'src/%s' % path
367
cr_deps_entry = new_cr_entries.get(chrome_path)
368
369
if cr_deps_entry:
370
assert type(cr_deps_entry) is type(angle_deps_entry)
371
372
if isinstance(cr_deps_entry, CipdDepsEntry):
373
result.extend(
374
_FindChangedCipdPackages(path, angle_deps_entry.packages,
375
cr_deps_entry.packages))
376
continue
377
378
# Use the revision from Chromium's DEPS file.
379
new_rev = cr_deps_entry.revision
380
assert ChromeURL(angle_deps_entry) == cr_deps_entry.url, (
381
'ANGLE DEPS entry %s has a different URL (%s) than Chromium (%s).' %
382
(path, ChromeURL(angle_deps_entry), cr_deps_entry.url))
383
else:
384
if isinstance(angle_deps_entry, DepsEntry):
385
# Use the HEAD of the deps repo.
386
stdout, _ = _RunCommand(['git', 'ls-remote', ChromeURL(angle_deps_entry), 'HEAD'])
387
new_rev = stdout.strip().split('\t')[0]
388
else:
389
# The dependency has been removed from chromium.
390
# This is handled by FindRemovedDeps.
391
continue
392
393
# Check if an update is necessary.
394
if angle_deps_entry.revision != new_rev:
395
logging.debug('Roll dependency %s to %s', path, new_rev)
396
result.append(
397
ChangedDep(path, ChromeURL(angle_deps_entry), angle_deps_entry.revision, new_rev))
398
return sorted(result)
399
400
401
def CalculateChangedClang(changed_deps, autoroll):
402
mirror_change = [change for change in changed_deps if change.path == CLANG_TOOLS_PATH]
403
if not mirror_change:
404
return None
405
406
mirror_change = mirror_change[0]
407
408
def GetClangRev(lines):
409
for line in lines:
410
match = CLANG_REVISION_RE.match(line)
411
if match:
412
return match.group(1)
413
raise RollError('Could not parse Clang revision!')
414
415
old_clang_update_py = ReadRemoteClangFile(CLANG_UPDATE_SCRIPT_URL_PATH,
416
mirror_change.current_rev).splitlines()
417
old_clang_rev = GetClangRev(old_clang_update_py)
418
logging.debug('Found old clang rev: %s' % old_clang_rev)
419
420
new_clang_update_py = ReadRemoteClangFile(CLANG_UPDATE_SCRIPT_URL_PATH,
421
mirror_change.new_rev).splitlines()
422
new_clang_rev = GetClangRev(new_clang_update_py)
423
logging.debug('Found new clang rev: %s' % new_clang_rev)
424
clang_change = ChangedDep(CLANG_UPDATE_SCRIPT_LOCAL_PATH, None, old_clang_rev, new_clang_rev)
425
return ClangChange(mirror_change, clang_change)
426
427
428
def GenerateCommitMessage(
429
rev_update,
430
current_commit_pos,
431
new_commit_pos,
432
changed_deps_list,
433
autoroll,
434
clang_change,
435
):
436
current_cr_rev = rev_update.current_chromium_rev[0:10]
437
new_cr_rev = rev_update.new_chromium_rev[0:10]
438
rev_interval = '%s..%s' % (current_cr_rev, new_cr_rev)
439
git_number_interval = '%s:%s' % (current_commit_pos, new_commit_pos)
440
441
commit_msg = []
442
# Autoroll already adds chromium_revision changes to commit message
443
if not autoroll:
444
commit_msg.extend([
445
'Roll chromium_revision %s (%s)\n' % (rev_interval, git_number_interval),
446
'Change log: %s' % (CHROMIUM_LOG_TEMPLATE % rev_interval),
447
'Full diff: %s\n' % (CHROMIUM_COMMIT_TEMPLATE % rev_interval)
448
])
449
450
def Section(adjective, deps):
451
noun = 'dependency' if len(deps) == 1 else 'dependencies'
452
commit_msg.append('%s %s' % (adjective, noun))
453
454
tbr_authors = ''
455
if changed_deps_list:
456
Section('Changed', changed_deps_list)
457
458
for c in changed_deps_list:
459
if isinstance(c, ChangedCipdPackage):
460
commit_msg.append('* %s: %s..%s' % (c.path, c.current_version, c.new_version))
461
else:
462
commit_msg.append('* %s: %s/+log/%s..%s' %
463
(c.path, c.url, c.current_rev[0:10], c.new_rev[0:10]))
464
465
if changed_deps_list:
466
# rev_interval is empty for autoroll, since we are starting from a state
467
# in which chromium_revision is already modified in DEPS
468
if not autoroll:
469
change_url = CHROMIUM_FILE_TEMPLATE % (rev_interval, 'DEPS')
470
commit_msg.append('DEPS diff: %s\n' % change_url)
471
else:
472
commit_msg.append('No dependencies changed.')
473
474
c = clang_change
475
if (c and (c.clang_change.current_rev != c.clang_change.new_rev)):
476
commit_msg.append('Clang version changed %s:%s' %
477
(c.clang_change.current_rev, c.clang_change.new_rev))
478
479
rev_clang = rev_interval = '%s..%s' % (c.mirror_change.current_rev,
480
c.mirror_change.new_rev)
481
change_url = CLANG_FILE_TEMPLATE % (rev_clang, CLANG_UPDATE_SCRIPT_URL_PATH)
482
commit_msg.append('Details: %s\n' % change_url)
483
else:
484
commit_msg.append('No update to Clang.\n')
485
486
# Autoroll takes care of BUG and TBR in commit message
487
if not autoroll:
488
# TBR needs to be non-empty for Gerrit to process it.
489
git_author = _RunCommand(['git', 'config', 'user.email'],
490
working_dir=CHECKOUT_SRC_DIR)[0].splitlines()[0]
491
tbr_authors = git_author + ',' + tbr_authors
492
493
commit_msg.append('TBR=%s' % tbr_authors)
494
commit_msg.append('BUG=None')
495
496
return '\n'.join(commit_msg)
497
498
499
def UpdateDepsFile(deps_filename, rev_update, changed_deps, new_cr_content, autoroll):
500
"""Update the DEPS file with the new revision."""
501
502
with open(deps_filename, 'rb') as deps_file:
503
deps_content = deps_file.read()
504
# Autoroll takes care of updating 'chromium_revision', thus we don't need to.
505
if not autoroll:
506
# Update the chromium_revision variable.
507
deps_content = deps_content.replace(rev_update.current_chromium_rev,
508
rev_update.new_chromium_rev)
509
510
# Add and remove dependencies. For now: only generated android deps.
511
# Since gclient cannot add or remove deps, we rely on the fact that
512
# these android deps are located in one place to copy/paste.
513
deps_re = re.compile(ANDROID_DEPS_START + '.*' + ANDROID_DEPS_END, re.DOTALL)
514
new_deps = deps_re.search(new_cr_content)
515
old_deps = deps_re.search(deps_content)
516
if not new_deps or not old_deps:
517
faulty = 'Chromium' if not new_deps else 'ANGLE'
518
raise RollError('Was expecting to find "%s" and "%s"\n'
519
'in %s DEPS' % (ANDROID_DEPS_START, ANDROID_DEPS_END, faulty))
520
521
replacement = new_deps.group(0).replace('src/third_party/android_deps',
522
'third_party/android_deps')
523
replacement = replacement.replace('checkout_android',
524
'checkout_android and not build_with_chromium')
525
526
deps_content = deps_re.sub(replacement, deps_content)
527
528
with open(deps_filename, 'wb') as deps_file:
529
deps_file.write(deps_content)
530
531
# Update each individual DEPS entry.
532
for dep in changed_deps:
533
# We don't sync deps on autoroller, so ignore missing local deps
534
if not autoroll:
535
local_dep_dir = os.path.join(CHECKOUT_ROOT_DIR, dep.path)
536
if not os.path.isdir(local_dep_dir):
537
raise RollError('Cannot find local directory %s. Either run\n'
538
'gclient sync --deps=all\n'
539
'or make sure the .gclient file for your solution contains all '
540
'platforms in the target_os list, i.e.\n'
541
'target_os = ["android", "unix", "mac", "ios", "win"];\n'
542
'Then run "gclient sync" again.' % local_dep_dir)
543
if isinstance(dep, ChangedCipdPackage):
544
package = dep.package.format() # Eliminate double curly brackets
545
update = '%s:%s@%s' % (dep.path, package, dep.new_version)
546
else:
547
update = '%s@%s' % (dep.path, dep.new_rev)
548
gclient_cmd = 'gclient'
549
if platform.system() == 'Windows':
550
gclient_cmd += '.bat'
551
_RunCommand([gclient_cmd, 'setdep', '--revision', update], working_dir=CHECKOUT_SRC_DIR)
552
553
554
def _IsTreeClean():
555
stdout, _ = _RunCommand(['git', 'status', '--porcelain'])
556
if len(stdout) == 0:
557
return True
558
559
logging.error('Dirty/unversioned files:\n%s', stdout)
560
return False
561
562
563
def _EnsureUpdatedMasterBranch(dry_run):
564
current_branch = _RunCommand(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])[0].splitlines()[0]
565
if current_branch != 'master':
566
logging.error('Please checkout the master branch and re-run this script.')
567
if not dry_run:
568
sys.exit(-1)
569
570
logging.info('Updating master branch...')
571
_RunCommand(['git', 'pull'])
572
573
574
def _CreateRollBranch(dry_run):
575
logging.info('Creating roll branch: %s', ROLL_BRANCH_NAME)
576
if not dry_run:
577
_RunCommand(['git', 'checkout', '-b', ROLL_BRANCH_NAME])
578
579
580
def _RemovePreviousRollBranch(dry_run):
581
active_branch, branches = _GetBranches()
582
if active_branch == ROLL_BRANCH_NAME:
583
active_branch = 'master'
584
if ROLL_BRANCH_NAME in branches:
585
logging.info('Removing previous roll branch (%s)', ROLL_BRANCH_NAME)
586
if not dry_run:
587
_RunCommand(['git', 'checkout', active_branch])
588
_RunCommand(['git', 'branch', '-D', ROLL_BRANCH_NAME])
589
590
591
def _LocalCommit(commit_msg, dry_run):
592
logging.info('Committing changes locally.')
593
if not dry_run:
594
_RunCommand(['git', 'add', '--update', '.'])
595
_RunCommand(['git', 'commit', '-m', commit_msg])
596
597
598
def _LocalCommitAmend(commit_msg, dry_run):
599
logging.info('Amending changes to local commit.')
600
if not dry_run:
601
old_commit_msg = _RunCommand(['git', 'log', '-1', '--pretty=%B'])[0].strip()
602
logging.debug('Existing commit message:\n%s\n', old_commit_msg)
603
604
bug_index = old_commit_msg.rfind('Bug:')
605
if bug_index == -1:
606
logging.error('"Bug:" not found in commit message.')
607
if not dry_run:
608
sys.exit(-1)
609
new_commit_msg = old_commit_msg[:bug_index] + commit_msg + '\n' + old_commit_msg[bug_index:]
610
611
_RunCommand(['git', 'commit', '-a', '--amend', '-m', new_commit_msg])
612
613
614
def ChooseCQMode(skip_cq, cq_over, current_commit_pos, new_commit_pos):
615
if skip_cq:
616
return 0
617
if (new_commit_pos - current_commit_pos) < cq_over:
618
return 1
619
return 2
620
621
622
def _UploadCL(commit_queue_mode):
623
"""Upload the committed changes as a changelist to Gerrit.
624
625
commit_queue_mode:
626
- 2: Submit to commit queue.
627
- 1: Run trybots but do not submit to CQ.
628
- 0: Skip CQ, upload only.
629
"""
630
cmd = ['git', 'cl', 'upload', '--force', '--bypass-hooks', '--send-mail']
631
cmd.extend(['--cc', NOTIFY_EMAIL])
632
if commit_queue_mode >= 2:
633
logging.info('Sending the CL to the CQ...')
634
cmd.extend(['--use-commit-queue'])
635
elif commit_queue_mode >= 1:
636
logging.info('Starting CQ dry run...')
637
cmd.extend(['--cq-dry-run'])
638
extra_env = {
639
'EDITOR': 'true',
640
'SKIP_GCE_AUTH_FOR_GIT': '1',
641
}
642
stdout, stderr = _RunCommand(cmd, extra_env=extra_env)
643
logging.debug('Output from "git cl upload":\nstdout:\n%s\n\nstderr:\n%s', stdout, stderr)
644
645
646
def GetRollRevisionRanges(opts, angle_deps):
647
current_cr_rev = angle_deps['vars']['chromium_revision']
648
new_cr_rev = opts.revision
649
if not new_cr_rev:
650
stdout, _ = _RunCommand(['git', 'ls-remote', CHROMIUM_SRC_URL, 'HEAD'])
651
head_rev = stdout.strip().split('\t')[0]
652
logging.info('No revision specified. Using HEAD: %s', head_rev)
653
new_cr_rev = head_rev
654
655
return ChromiumRevisionUpdate(current_cr_rev, new_cr_rev)
656
657
658
def main():
659
p = argparse.ArgumentParser()
660
p.add_argument(
661
'--clean',
662
action='store_true',
663
default=False,
664
help='Removes any previous local roll branch.')
665
p.add_argument(
666
'-r',
667
'--revision',
668
help=('Chromium Git revision to roll to. Defaults to the '
669
'Chromium HEAD revision if omitted.'))
670
p.add_argument(
671
'--dry-run',
672
action='store_true',
673
default=False,
674
help=('Calculate changes and modify DEPS, but don\'t create '
675
'any local branch, commit, upload CL or send any '
676
'tryjobs.'))
677
p.add_argument(
678
'-i',
679
'--ignore-unclean-workdir',
680
action='store_true',
681
default=False,
682
help=('Ignore if the current branch is not master or if there '
683
'are uncommitted changes (default: %(default)s).'))
684
grp = p.add_mutually_exclusive_group()
685
grp.add_argument(
686
'--skip-cq',
687
action='store_true',
688
default=False,
689
help='Skip sending the CL to the CQ (default: %(default)s)')
690
grp.add_argument(
691
'--cq-over',
692
type=int,
693
default=1,
694
help=('Commit queue dry run if the revision difference '
695
'is below this number (default: %(default)s)'))
696
grp.add_argument(
697
'--autoroll',
698
action='store_true',
699
default=False,
700
help='Autoroller mode - amend existing commit, '
701
'do not create nor upload a CL (default: %(default)s)')
702
p.add_argument(
703
'-v',
704
'--verbose',
705
action='store_true',
706
default=False,
707
help='Be extra verbose in printing of log messages.')
708
opts = p.parse_args()
709
710
if opts.verbose:
711
logging.basicConfig(level=logging.DEBUG)
712
else:
713
logging.basicConfig(level=logging.INFO)
714
715
# We don't have locally sync'ed deps on autoroller,
716
# so trust it to have depot_tools in path
717
if not opts.autoroll:
718
AddDepotToolsToPath()
719
720
if not opts.ignore_unclean_workdir and not _IsTreeClean():
721
logging.error('Please clean your local checkout first.')
722
return 1
723
724
if opts.clean:
725
_RemovePreviousRollBranch(opts.dry_run)
726
727
if not opts.ignore_unclean_workdir:
728
_EnsureUpdatedMasterBranch(opts.dry_run)
729
730
deps_filename = os.path.join(CHECKOUT_SRC_DIR, 'DEPS')
731
angle_deps = ParseLocalDepsFile(deps_filename)
732
733
rev_update = GetRollRevisionRanges(opts, angle_deps)
734
735
current_commit_pos = ParseCommitPosition(ReadRemoteCrCommit(rev_update.current_chromium_rev))
736
new_commit_pos = ParseCommitPosition(ReadRemoteCrCommit(rev_update.new_chromium_rev))
737
738
new_cr_content = ReadRemoteCrFile('DEPS', rev_update.new_chromium_rev)
739
new_cr_deps = ParseDepsDict(new_cr_content)
740
changed_deps = CalculateChangedDeps(angle_deps, new_cr_deps)
741
clang_change = CalculateChangedClang(changed_deps, opts.autoroll)
742
commit_msg = GenerateCommitMessage(rev_update, current_commit_pos, new_commit_pos,
743
changed_deps, opts.autoroll, clang_change)
744
logging.debug('Commit message:\n%s', commit_msg)
745
746
# We are updating a commit that autoroll has created, using existing branch
747
if not opts.autoroll:
748
_CreateRollBranch(opts.dry_run)
749
750
if not opts.dry_run:
751
UpdateDepsFile(deps_filename, rev_update, changed_deps, new_cr_content, opts.autoroll)
752
753
if opts.autoroll:
754
_LocalCommitAmend(commit_msg, opts.dry_run)
755
else:
756
if _IsTreeClean():
757
logging.info("No DEPS changes detected, skipping CL creation.")
758
else:
759
_LocalCommit(commit_msg, opts.dry_run)
760
commit_queue_mode = ChooseCQMode(opts.skip_cq, opts.cq_over, current_commit_pos,
761
new_commit_pos)
762
logging.info('Uploading CL...')
763
if not opts.dry_run:
764
_UploadCL(commit_queue_mode)
765
return 0
766
767
768
if __name__ == '__main__':
769
sys.exit(main())
770
771