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

Side by Side Diff: tools/bisect_repackage/bisect_repackage.py

Issue 2236703003: tool for repackaging chrome.perf builds for manual bisect (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Repackaging tool for linux, mac, and win64 Created 4 years, 3 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 unified diff | Download patch
« no previous file with comments | « no previous file | tools/bisect_repackage/bisect_repackage_utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2016 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 """ Bisect repackage tool for Linux
6
7 This script repacakges chrome builds for manual bisect script.
8 """
9
10 #Declares required files to run manual bisect script on chrome Linux
prasadv 2016/08/30 22:12:20 nit: Need 1 space after #
miimnk 2016/09/01 00:49:11 Done.
11 #builds in perf. Binary files that should be stripped to reduce zip file
12 #size are declared. The file list was gotten from the local chrome
13 #executable path. (This can be retrieved by typing 'chrome://version'
14 #in chrome and following the executable path. The list needs to be updated if
15 #future chrome versions require additional files.
16 CHROME_REQUIRED_FILES = {
17 'linux': [
prasadv 2016/08/30 22:12:20 4 space on hanging indent. repeat same wherever ap
miimnk 2016/09/01 00:49:11 Done.
18 'chrome',
19 'chrome_100_percent.pak',
20 'chrome_200_percent.pak',
21 'default_apps',
22 'icudtl.dat',
23 'libwidevinecdm.so',
24 'locales',
25 'nacl_helper',
26 'nacl_helper_bootstrap',
27 'nacl_irt_x86_64.nexe',
28 'natives_blob.bin',
29 'PepperFlash',
30 'product_logo_48.png',
31 'resources.pak',
32 'snapshot_blob.bin',
33 'xdg-mime',
34 'xdg-settings'
35 ],
36 'win64': [
37 'chrome.dll',
38 'chrome.exe',
39 'chrome_100_percent.pak',
40 'chrome_200_percent.pak',
41 'chrome_child.dll',
42 'chrome_elf.dll',
43 'chrome_watcher.dll',
44 'default_apps',
45 'd3dcompiler_47.dll',
46 'icudtl.dat',
47 'libEGL.dll',
48 'libGLESv2.dll',
49 'locales',
50 'nacl_irt_x86_64.nexe',
51 'natives_blob.bin',
52 'PepperFlash',
53 'resources.pak',
54 'SecondaryTile.png',
55 'snapshot_blob.bin'
56 ],
57 'mac': [
58 'Google Chrome.app'
59 ]
60 }
61
62 CHROME_WHITELIST_FILES = {
63 # ^$ meanse not to include any files from whitelist
64 'linux': '^$',
65 'win64': '^\d+\.\d+\.\d+\.\d+\.manifest$',
66 'mac': '^$'
67 }
68
69 CHROME_STRIP_LIST = {
70 'linux': [
71 'chrome',
72 'nacl_helper'
73 ],
74 'win64': [
75 # No stripping symbols from win64 archives
76 ],
77 'mac': [
78 # No stripping symbols from mac archives
79 ]
80 }
81
82 # API to convert Githash to Commit position number.
83 CHROMIUM_GITHASH_TO_SVN_URL = (
84 'https://cr-rev.appspot.com/_ah/api/crrev/v1/commit/%s')
85
86 REVISION_MAP_FILE = 'revision_map.json'
87
88 BUILDER_NAME = {
89 'linux': 'Linux Builder',
90 'mac': 'Mac Builder',
91 'win32': 'Win Builder',
92 'win64': 'Win x64 Builder'
93 }
94
95 ARCHIVE_PREFIX = {
96 'linux': 'full-build-linux',
97 'mac': 'full-build-mac',
98 'win32': 'full-build-win32',
99 'win64': 'full-build-win32'
100 }
101
102
103 ################################################################################
prasadv 2016/08/30 22:12:20 Remove this # line
miimnk 2016/09/01 00:49:11 Done.
104 import os
prasadv 2016/08/30 22:12:20 Ordering of import is not correct. refer Python st
miimnk 2016/09/01 00:49:11 Done.
105 import bisect_repackage_utils
106 import zipfile
107 import zlib
108 import json
109 import sys
110 import optparse
111 import re
112 import urllib
113 import threading
114 from multiprocessing import Pool
115 from functools import partial
116 import tempfile
117 import logging
118
119 class PathContext(object):
120 """A PathContext is used to carry the information used to construct URLs and
prasadv 2016/08/30 22:12:21 Single line doc string.
miimnk 2016/09/01 00:49:11 Done.
121 paths when dealing with the storage server and archives."""
122 def __init__(self, original_gs_url, repackage_gs_url,
123 archive, revision_file=REVISION_MAP_FILE):
124 super(PathContext, self).__init__()
125 self.original_gs_url = original_gs_url
126 self.repackage_gs_url = repackage_gs_url
127 self.archive = archive
128 self.builder_name = BUILDER_NAME[archive]
129 self.file_prefix = ARCHIVE_PREFIX[archive]
130 self.revision_file = revision_file
131
132
133 def get_cp_from_hash(git_hash):
134 """Converts a GitHash to Commit position number"""
135 json_url = CHROMIUM_GITHASH_TO_SVN_URL % git_hash
136 response = urllib.urlopen(json_url)
137 if response.getcode() == 200:
138 try:
139 data = json.loads(response.read())
140 except ValueError:
141 logging.warning('ValueError for JSON URL: %s' % json_url)
142 raise ValueError('Failed to extract commit position')
143 else:
144 logging.warning('ValueError for JSON URL: %s' % json_url)
145 raise ValueError('Failed to extract commit position')
146 if 'number' in data:
147 return data['number']
148 logging.warning('Failed to get svn revision number for %s' % git_hash)
149 raise ValueError('Failed to extract commit position')
150
151 def create_cp_from_hash_map(hash_list):
152 """Creates a dictionary that maps from Commit positio number
153 to corresponding GitHash"""
154 hash_map = {}
155 count = 0
156 for git_hash in hash_list:
157 try:
158 cp_num = get_cp_from_hash(git_hash)
159 hash_map[cp_num] = git_hash
160 print "Downloaded %s / %s git hash" %(count, len(hash_list))
161 count += 1
162 except ValueError:
163 pass
164 return hash_map
165
166 def get_list_of_suffix(bucket_address, prefix, filter_function):
167 """Gets the list of suffixes in files in a google storage bucket.
168 For example, a google storage bucket containing one file
169 'full-build-linux_20983' will return ['20983'] if prefix is
170 provided as 'full-build-linux'. Google Storage bucket
171 containing multiple files will return multiple suffixes. """
172 file_list = bisect_repackage_utils.GSutilList(bucket_address)
173 suffix_list = []
174 extract_suffix = '.*?%s_(.*?)\.zip' %(prefix)
175 for file in file_list:
176 match = re.match(extract_suffix, file)
177 if match and filter_function(match.groups()[0]):
178 suffix_list.append(match.groups()[0])
179 return suffix_list
180
181 def download_build(cp_num, revision_map, zip_file_name, context):
182 """ Download a single build corresponding to the cp_num and context."""
183 file_url = '%s/%s/%s_%s.zip' %(context.original_gs_url, context.builder_name,
184 context.file_prefix, revision_map[cp_num])
185 bisect_repackage_utils.GSUtilDownloadFile(file_url, zip_file_name)
186
187 def upload_build(zip_file, context):
188 """Uploads a single build in zip_file to the repackage_gs_url in context."""
189 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
190 upload_url = gs_base_url + '/'
191 bisect_repackage_utils.GSUtilCopy(zip_file, upload_url)
192
193 def download_revision_map(context):
194 """Downloads the revision map in original_gs_url in context."""
195 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
196 download_url = gs_base_url + '/' + context.revision_file
197 bisect_repackage_utils.GSUtilDownloadFile(download_url,
198 context.revision_file)
199
200 def get_revision_map(context):
201 """Downloads and returns the revision map in repackage_gs_url in context."""
202 bisect_repackage_utils.RemoveFile(context.revision_file)
203 download_revision_map(context)
204 with open(context.revision_file, 'r') as revision_file:
205 revision_map = json.load(revision_file)
206 bisect_repackage_utils.RemoveFile(context.revision_file)
207 return revision_map
208
209 def upload_revision_map(revision_map, context):
210 """Upload the given revision_map to the repackage_gs_url in context."""
211 with open(context.revision_file, 'w') as revision_file:
212 json.dump(revision_map, revision_file)
213 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
214 upload_url = gs_base_url + '/'
215 bisect_repackage_utils.GSUtilCopy(context.revision_file, upload_url)
216 bisect_repackage_utils.RemoveFile(context.revision_file)
217
218 def create_upload_revision_map(context):
219 """ Creates and uploads a dictionary that maps from GitHash to CP number."""
220 gs_base_url = '%s/%s' %(context.original_gs_url, context.builder_name)
221 hash_list = get_list_of_suffix(gs_base_url, context.file_prefix,
222 bisect_repackage_utils.isGitCommitHash)
223 cp_num_to_hash_map = create_cp_from_hash_map(hash_list)
224 upload_revision_map(cp_num_to_hash_map, context)
225
226 def update_upload_revision_map(context):
227 """ Updates and uploads a dictionary that maps from GitHash to CP number."""
228 gs_base_url = '%s/%s' %(context.original_gs_url, context.builder_name)
229 revision_map = get_revision_map(context)
230 hash_list = get_list_of_suffix(gs_base_url, context.file_prefix,
231 bisect_repackage_utils.isGitCommitHash)
232 hash_list = list(set(hash_list)-set(revision_map.values()))
233 cp_num_to_hash_map = create_cp_from_hash_map(hash_list)
234 merged_dict = dict(cp_num_to_hash_map.items() + revision_map.items())
235 upload_revision_map(merged_dict, context)
236
237 def make_lightweight_archive(file_archive, archive_name, files_to_archive,
238 context, staging_dir):
239 """Repackages and strips the archive according to FILES_TO_ARCHIVE and
240 STRIP_LIST defined on the top."""
241 strip_list = CHROME_STRIP_LIST[context.archive]
242 tmp_archive = os.path.join(staging_dir, 'tmp_%s' % archive_name)
243 (zip_file, zip_dir) = bisect_repackage_utils.MakeZip(tmp_archive,
244 archive_name,
245 files_to_archive,
246 file_archive,
247 raise_error=False,
248 strip_files=strip_list)
249 return (zip_file, zip_dir, tmp_archive)
250
251 def remove_created_files_and_path(files, paths):
252 """ Removes all the files and paths passed in."""
253 for file in files:
254 bisect_repackage_utils.RemoveFile(file)
255 for path in paths:
256 bisect_repackage_utils.RemovePath(path)
257
258 def verify_chrome_run(zip_dir):
259 """This function executes chrome executable in zip_dir. Raises error if the
260 execution fails for any reason."""
261 try:
262 command = [os.path.join(zip_dir, 'chrome')]
263 code = bisect_repackage_utils.RunCommand(command)
264 if code != 0:
265 raise ValueError("Executing Chrome Failed: Need to check ")
266 except Exception, e:
267 raise ValueError("Executing Chrome Failed: Need to check ")
268
269
270 def get_whitelist_files(extracted_folder, archive):
271 """Gets all the files & directories matching whitelisted regex."""
272 whitelist_files = []
273 all_files = os.listdir(extracted_folder)
274 for file in all_files:
275 if re.match(CHROME_WHITELIST_FILES[archive], file):
276 whitelist_files.append(file)
277 return whitelist_files
278
279
280 def repackage_single_revision(revision_map, isForwardArchive, verify_run,
281 staging_dir, context, cp_num):
282 """Repackages a single Chrome build for manual bisect."""
283 archive_name = '%s_%s' %(context.file_prefix, cp_num)
284 file_archive = os.path.join(staging_dir, archive_name)
285 zip_file_name = '%s.zip' % (file_archive)
286 download_build(cp_num, revision_map, zip_file_name, context)
287 extract_dir = os.path.join(staging_dir, archive_name)
288 bisect_repackage_utils.ExtractZip(zip_file_name, extract_dir)
289 extracted_folder = os.path.join(extract_dir, context.file_prefix)
290 if CHROME_WHITELIST_FILES[context.archive]:
291 whitelist_files = get_whitelist_files(extracted_folder, context.archive)
292 files_to_include = whitelist_files + CHROME_REQUIRED_FILES[context.archive]
293 else:
294 files_to_include = CHROME_REQUIRED_FILES[context.archive]
295 (zip_dir, zip_file, tmp_archive) = make_lightweight_archive(extracted_folder,
296 archive_name,
297 files_to_include, context,
298 staging_dir)
299
300 if verify_run:
301 verify_chrome_run(zip_dir)
302
303 upload_build(zip_file, context)
304 # Removed temporary files created during repackaging process.
305 remove_created_files_and_path([zip_file_name],
306 [zip_dir, extract_dir, tmp_archive])
307
308 def repackage_revisions(revisions, revision_map, isForwardArchive, verify_run,
309 staging_dir, context, quit_event=None,
310 progress_event=None):
311 """ Repackages all Chrome builds listed in revisions. This function calls
312 'repackage_single_revision' with multithreading pool.'"""
313 p = Pool(3)
314 func = partial(repackage_single_revision, revision_map, isForwardArchive,
315 verify_run, staging_dir, context)
316 p.imap(func, revisions)
317 p.close()
318 p.join()
319
320 def get_uploaded_builds(context):
321 """Returns already uploaded commit positions numbers in
322 context.repackage_gs_url"""
323 gs_base_url = '%s/%s' %(context.repackage_gs_url, context.builder_name)
324 return get_list_of_suffix(gs_base_url, context.file_prefix,
325 bisect_repackage_utils.isCommitPosition)
326
327 def get_revisions_to_package(revision_map, context):
328 """ Returns revisions that need to be repackaged. The first half of
329 the revisions will be sorted in ascending order and the second half of
330 the revisions will be sorted in desending order.
331
332 The first half will be used for repackaging revisions in forward direction,
333 and the second half will be used for repackaging revisions in backward
334 direction."""
335 already_packaged = get_uploaded_builds(context)
336 not_already_packaged = list(set(revision_map.keys())-set(already_packaged))
337 revisions_to_package = sorted(not_already_packaged)
338 revisions_to_package = filter(lambda a: a != 'null', revisions_to_package)
339
340 forward_rev = revisions_to_package[:len(revisions_to_package)/2]
341 backward_rev = sorted(revisions_to_package[len(revisions_to_package)/2:],
342 reverse=True)
343 return (forward_rev, backward_rev)
344
345 class RepackageJob(object):
346 def __init__(self, name, revisions_to_package, revision_map, isForwardArchive,
347 verify_run, staging_dir, context):
348 super(RepackageJob, self).__init__()
349 self.name = name
350 self.revisions_to_package = revisions_to_package
351 self.revision_map = revision_map
352 self.isForwardArchive = isForwardArchive
353 self.verify_run = verify_run
354 self.staging_dir = staging_dir
355 self.context = context
356 self.quit_event = threading.Event()
357 self.progress_event = threading.Event()
358 self.thread = None
359
360 def Start(self):
361 """Starts the download."""
362 fetchargs = (self.revisions_to_package,
363 self.revision_map,
364 self.isForwardArchive,
365 self.verify_run,
366 self.staging_dir,
367 self.context,
368 self.quit_event,
369 self.progress_event)
370 self.thread = threading.Thread(target=repackage_revisions,
371 name=self.name,
372 args=fetchargs)
373 self.thread.start()
374
375 def Stop(self):
376 """Stops the download which must have been started previously."""
377 assert self.thread, 'DownloadJob must be started before Stop is called.'
378 self.quit_event.set()
379 self.thread.join()
380
381 def WaitFor(self):
382 """Prints a message and waits for the download to complete. The download
383 must have been started previously."""
384 assert self.thread, 'DownloadJob must be started before WaitFor is called.'
385 self.progress_event.set() # Display progress of download. def Stop(self):
386 assert self.thread, 'DownloadJob must be started before Stop is called.'
387 self.quit_event.set()
388 self.thread.join()
389
390 def main(argv):
391 option_parser = optparse.OptionParser()
392
393 choices = ['mac', 'win32', 'win64', 'linux']
394
395 option_parser.add_option('-a', '--archive',
396 choices=choices,
397 help='Builders to repacakge from [%s].' %
398 '|'.join(choices))
399
400 # Verifies that the chrome executable runs
401 option_parser.add_option('-v', '--verify',
402 action='store_true',
403 help='Verifies that the Chrome executes normally'
404 'without errors')
405
406 # This option will update the revision map.
407 option_parser.add_option('-u', '--update',
408 action='store_true',
409 help='Updates the list of revisions to repackage')
410
411 # This option will creates the revision map.
412 option_parser.add_option('-c', '--create',
413 action='store_true',
414 help='Creates the list of revisions to repackage')
415
416 # Original bucket that contains perf builds
417 option_parser.add_option('-o', '--original',
418 type='str',
419 help='Google storage url containing original Chrome builds')
420
421 # Bucket that should archive lightweight perf builds
422 option_parser.add_option('-r', '--repackage',
423 type='str',
424 help='Google storage url to re-archive Chrome builds')
425
426 verify_run = False
427 (opts, args) = option_parser.parse_args()
428 if opts.archive is None:
429 print 'Error: missing required parameter: --archive'
430 parser.print_help()
431 return 1
432 if not opts.original or not opts.repackage:
433 raise ValueError('Need to specify original gs bucket url and'
434 'repackage gs bucket url')
435 context = PathContext(opts.original, opts.repackage, opts.archive)
436
437 if opts.create:
438 create_upload_revision_map(context)
439
440 if opts.update:
441 update_upload_revision_map(context)
442
443 if opts.verify:
444 verify_run = True
445
446 revision_map = get_revision_map(context)
447 (forward_rev, backward_rev) = get_revisions_to_package(revision_map, context)
448 base_dir = os.path.join('.', context.archive)
449 # Clears any uncleared staging directories and create one
450 bisect_repackage_utils.RemovePath(base_dir)
451 bisect_repackage_utils.MaybeMakeDirectory(base_dir)
452 staging_dir = os.path.abspath(tempfile.mkdtemp(prefix='staging_fwd',
453 dir=base_dir))
454 staging_dir_bwd = os.path.abspath(tempfile.mkdtemp(prefix='staging_bwd',
455 dir=base_dir))
456 backward_fetch = RepackageJob('backward_fetch', backward_rev, revision_map,
457 False, verify_run, staging_dir_bwd,
458 context)
459 try:
460 # Only run backward fetch
461 backward_fetch.Start()
462 backward_fetch.WaitFor()
463 except (KeyboardInterrupt, SystemExit):
464 print 'Cleaning up...'
465 bisect_repackage_utils.RemovePath(staging_dir)
466 bisect_repackage_utils.RemovePath(staging_dir_bwd)
467 print 'Cleaning up...'
468 bisect_repackage_utils.RemovePath(staging_dir)
469 bisect_repackage_utils.RemovePath(staging_dir_bwd)
470
471 if '__main__' == __name__:
472 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | tools/bisect_repackage/bisect_repackage_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698