Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Unified Diff: tools/gn/bin/roll_gn.py

Issue 1230293003: Add a GN auto-roller script. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix depot_tools_path Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/bin/roll_gn.py
diff --git a/tools/gn/bin/roll_gn.py b/tools/gn/bin/roll_gn.py
new file mode 100755
index 0000000000000000000000000000000000000000..d0d7641a992bd2ba081c8fac41a971fcf02d3ef4
--- /dev/null
+++ b/tools/gn/bin/roll_gn.py
@@ -0,0 +1,349 @@
+#!/usr/bin/python
brettw 2015/07/15 01:59:05 Needs copyright.
Dirk Pranke 2015/07/15 02:40:31 Acknowledged.
+
+from __future__ import print_function
+
+import argparse
+import json
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import time
+import urllib2
+
+depot_tools_path = None
brettw 2015/07/15 01:59:05 Can this have some documentation and an example at
Dirk Pranke 2015/07/15 02:40:32 I assume "this" means the roll_gn script as a whol
brettw 2015/07/15 02:43:37 Correct, you can be assured I did not notice the s
+for p in os.environ['PATH'].split(os.pathsep):
+ if (p.rstrip(os.sep).endswith('depot_tools') and
+ os.path.isfile(os.path.join(p, 'gclient.py'))):
+ depot_tools_path = p
+
+assert depot_tools_path
+
+if not depot_tools_path in sys.path:
+ sys.path.append(depot_tools_path)
Nico 2015/07/15 20:24:25 nit: always prepend to sys.path, else you can end
Dirk Pranke 2015/07/15 20:35:56 true.
+sys.path.append(os.path.join(depot_tools_path, 'third_party'))
+
+import upload
+
+
+CHROMIUM_REPO = 'https://chromium.googlesource.com/chromium/src.git'
+
+CODE_REVIEW_SERVER = 'https://codereview.chromium.org'
+
+
+class GNRoller(object):
+ def __init__(self):
+ self.chromium_src_dir = None
+ self.old_gn_commitish = None
+ self.new_gn_commitish = None
+ self.old_gn_version = None
+ self.new_gn_version = None
+ self.reviewer = 'dpranke@chromium.org'
+ if os.getenv('USER') == 'dpranke':
+ self.reviewer = 'brettw@chromium.org'
+
+ def Roll(self):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('command', nargs='?', default='roll',
+ help='build|roll|roll_buildtools|roll_deps|wait'
+ ' (default is roll)')
+
+ args = parser.parse_args()
+ command = args.command
+ ret = self.SetUp()
+ if not ret and command in ('roll', 'build'):
+ ret = self.TriggerBuild()
+ if not ret and command in ('roll', 'wait'):
+ ret = self.WaitForBuildToFinish()
+ if not ret and command in ('roll', 'roll_buildtools'):
+ ret = self.RollBuildtools()
+ if not ret and command in ('roll', 'roll_deps'):
+ ret = self.RollDEPS()
+
+ return ret
+
+ def SetUp(self):
+ if sys.platform != 'linux2':
+ print('roll_gn is only tested and working on Linux for now.')
+ return 1
+
+ ret, out, _ = call('git config --get remote.origin.url')
+ origin = out.strip()
+ if ret or origin != CHROMIUM_REPO:
+ print('Not in a Chromium repo? git config --get remote.origin.url '
+ 'returned %d: %s' % (ret, origin))
+ return 1
+
+ ret, _, _ = call('git diff -q')
+ if ret:
+ print("Checkout is dirty, exiting")
+ return 1
+
+ _, out, _ = call('git rev-parse --show-toplevel')
+ self.chromium_src_dir = out.strip()
+ os.chdir(self.chromium_src_dir)
Nico 2015/07/15 20:24:25 i always encourage people to make their code not c
Dirk Pranke 2015/07/15 20:35:56 Yeah, I do, too. In this case I was being lazy.
+
+ self.new_gn_commitish, self.new_gn_version = self.GetNewVersions()
+
+ _, out, _ = call('gn --version')
+ self.old_gn_version = out.strip()
+
+ _, out, _ = call('git crrev-parse %s' % self.old_gn_version)
+ self.old_gn_commitish = out.strip()
+ return 0
+
+ def GetNewVersions(self):
+ commit_msg = call('git log -1 --grep Cr-Commit-Position')[1].splitlines()
+ first_line = commit_msg[0]
+ new_gn_commitish = first_line.split()[1]
+
+ last_line = commit_msg[-1]
+ new_gn_version = re.sub('.*master@{#(\d+)}', '\\1', last_line)
+
+ return new_gn_commitish, new_gn_version
+
+ def TriggerBuild(self):
+ os.chdir(self.chromium_src_dir)
+ ret, _, _ = call('git new-branch build_gn_%s' % self.new_gn_version)
+ if ret:
+ print('Failed to create a new branch for build_gn_%s' %
+ self.new_gn_version)
+ return 1
+
+ self.MakeDummyDepsChange()
+
+ ret, out, err = call('git commit -a -m "Build gn at %s"' %
+ self.new_gn_version)
+ if ret:
+ print('git commit failed: %s' % out + err)
+ return 1
+
+ print('Uploading CL to build GN at {#%s} - %s' %
+ (self.new_gn_version, self.new_gn_commitish))
+ ret, out, err = call('git cl upload -f')
+ if ret:
+ print('git-cl upload failed: %s' % out + err)
+ return 1
+
+ print('Starting try jobs')
+ call('git-cl try -b linux_chromium_gn_upload -b mac_chromium_gn_upload '
+ '-b win8_chromium_gn_upload -r %s' % self.new_gn_commitish)
+
+ return 0
+
+ def MakeDummyDepsChange(self):
+ with open('DEPS') as fp:
+ deps_content = fp.read()
+ new_deps = deps_content.replace("'buildtools_revision':",
+ "'buildtools_revision': ")
+
+ with open('DEPS', 'w') as fp:
+ fp.write(new_deps)
+
+ def WaitForBuildToFinish(self):
+ print('Checking build')
+ results = self.CheckBuild()
+ while any(r['state'] == 'pending' for r in results.values()):
+ print()
+ print('Sleeping for 30 seconds')
+ time.sleep(30)
+ print('Checking build')
+ results = self.CheckBuild()
+ return 0 if all(r['state'] == 'success' for r in results.values()) else 1
+
+ def CheckBuild(self):
+ os.chdir(self.chromium_src_dir)
+ _, out, _ = call('git-cl issue')
+
+ issue = int(out.split()[2])
+
+ _, out, _ = call('git config user.email')
+ email = ''
+ rpc_server = upload.GetRpcServer(CODE_REVIEW_SERVER, email)
+ try:
+ props = json.loads(rpc_server.Send('/api/%d' % issue))
+ except Exception as _e:
+ raise
+
+ patchset = int(props['patchsets'][-1])
+
+ try:
+ patchset_data = json.loads(rpc_server.Send('/api/%d/%d' %
+ (issue, patchset)))
+ except Exception as _e:
+ raise
+
+ TRY_JOB_RESULT_STATES = ('success', 'warnings', 'failure', 'skipped',
+ 'exception', 'retry', 'pending')
+ try_job_results = patchset_data['try_job_results']
+ if not try_job_results:
+ print('No try jobs found on most recent patchset')
+ return 1
+
+ results = {}
+ for job in try_job_results:
+ builder = job['builder']
+ if builder == 'linux_chromium_gn_upload':
+ platform = 'linux64'
+ elif builder == 'mac_chromium_gn_upload':
+ platform = 'mac'
+ elif builder == 'win8_chromium_gn_upload':
+ platform = 'win'
+ else:
+ print('Unexpected builder: %s')
+ continue
+
+ state = TRY_JOB_RESULT_STATES[int(job['result'])]
+ url_str = ' %s' % job['url']
+ build = url_str.split('/')[-1]
+
+ sha1 = '-'
+ results.setdefault(platform, {'build': -1, 'sha1': '', 'url': url_str})
+
+ if state == 'success':
+ jsurl = url_str.replace('/builders/', '/json/builders/')
+ fp = urllib2.urlopen(jsurl)
+ js = json.loads(fp.read())
+ fp.close()
+ for step in js['steps']:
+ if step['name'] == 'gn sha1':
+ sha1 = step['text'][1]
+
+ if results[platform]['build'] < build:
+ results[platform]['build'] = build
+ results[platform]['sha1'] = sha1
+ results[platform]['state'] = state
+ results[platform]['url'] = url_str
+
+ for platform, r in results.items():
+ print(platform)
+ print(' sha1: %s' % r['sha1'])
+ print(' state: %s' % r['state'])
+ print(' build: %s' % r['build'])
+ print(' url: %s' % r['url'])
+ print()
+
+ return results
+
+ def RollBuildtools(self):
+ os.chdir(self.chromium_src_dir)
+ results = self.CheckBuild()
+ if not all(r['state'] == 'success' for r in results.values()):
+ print("Roll isn't done or didn't succeed, exiting:")
+ return 1
+
+ desc = self.GetBuildtoolsDesc()
+
+ os.chdir('buildtools')
+ call('git new-branch roll_buildtools_gn_%s' % self.new_gn_version)
+
+ for platform in results:
+ fname = 'gn.exe.sha1' if platform == 'win' else 'gn.sha1'
Nico 2015/07/15 20:24:25 suddenly, 4-space indent?
Dirk Pranke 2015/07/15 20:35:56 bad indent. will fix.
+ with open('%s/%s' % (platform, fname), 'w') as fp:
+ fp.write('%s\n' % results[platform]['sha1'])
+
+ desc_file = tempfile.NamedTemporaryFile(delete=False)
+ try:
+ desc_file.write(desc)
+ desc_file.close()
+ call('git commit -a -F %s' % desc_file.name)
+ call('git-cl upload -f --send-mail')
+ finally:
+ os.remove(desc_file.name)
+
+ call('git cl push')
+ call('git cl fetch')
+ return 0
+
+ def RollDEPS(self):
+ os.chdir(self.chromium_src_dir)
+
+ os.chdir('buildtools')
+ _, out, _ = call('git rev-parse origin/master')
+ new_buildtools_commitish = out.strip()
+
+ new_deps_lines = []
+ os.chdir('..')
+ old_buildtools_commitish = ''
+ with open('DEPS') as fp:
+ for l in fp.readlines():
+ m = re.match(".*'buildtools_revision':.*'(.+)',", l)
+ if m:
+ old_buildtools_commitish = m.group(1)
+ new_deps_lines.append(" 'buildtools_revision': '%s'," %
+ new_buildtools_commitish)
+ else:
+ new_deps_lines.append(l)
+
+ if not old_buildtools_commitish:
+ print('Could not update DEPS properly, exiting')
+ return 1
+
+ with open('DEPS', 'w') as fp:
+ fp.write(''.join(new_deps_lines) + '\n')
+
+ desc = self.GetDEPSRollDesc(old_buildtools_commitish,
+ new_buildtools_commitish)
+ desc_file = tempfile.NamedTemporaryFile(delete=False)
+ try:
+ desc_file.write(desc)
+ desc_file.close()
+ call('git commit -a -F %s' % desc_file.name)
+ call('git-cl upload -f --send-mail --commit-queue')
+ finally:
+ os.remove(desc_file.name)
+ return 0
+
+ def GetBuildtoolsDesc(self):
+ gn_changes = self.GetGNChanges()
+ return (
+ 'Roll gn %s..%s (r%s:%s)\n'
+ '\n'
+ '%s'
+ '\n'
+ 'TBR=%s\n' % (
+ self.old_gn_commitish,
+ self.new_gn_commitish,
+ self.old_gn_version,
+ self.new_gn_version,
+ gn_changes,
+ self.reviewer,
+ ))
+
+ def GetDEPSRollDesc(self, old_buildtools_commitish, new_buildtools_commitish):
+ gn_changes = self.GetGNChanges()
+
+ return (
+ 'Roll DEPS %s..%s\n'
+ '\n'
+ ' in order to roll GN %s..%s (r%s:%s)\n'
+ '\n'
+ '%s'
+ '\n'
+ 'TBR=%s\n' % (
+ old_buildtools_commitish,
+ new_buildtools_commitish,
+ self.old_gn_commitish,
+ self.new_gn_commitish,
+ self.old_gn_version,
+ self.new_gn_version,
+ gn_changes,
+ self.reviewer,
+ ))
+
+ def GetGNChanges(self):
+ _, out, _ = call(
+ "git log --pretty=' %h %s' " +
+ "%s..%s tools/gn" % (self.old_gn_commitish, self.new_gn_commitish))
+ return out
+
+def call(cmd):
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
Nico 2015/07/15 20:24:25 do you need the shell bit? if not, i'd recommend n
Dirk Pranke 2015/07/15 20:35:56 In many cases, I need shell=True. It is possible t
+ out, err = p.communicate()
+ return p.returncode, out, err
+
+
+if __name__ == '__main__':
+ roller = GNRoller()
+ sys.exit(roller.Roll())
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698