blob: 54b2480e2295305b4a8147c838b668d515f5648b [file] [log] [blame]
borenetdc89ca52014-10-17 07:37:05 -07001#!/usr/bin/env python
2# Copyright (c) 2012 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"""Archives or replays webpages and creates SKPs in a Google Storage location.
7
8To archive webpages and store SKP files (archives should be rarely updated):
9
kkinnunenfcf35c52014-12-03 05:51:24 -080010cd skia
kkinnunenb4ee7ea2015-03-31 00:18:26 -070011python tools/skp/webpages_playback.py --data_store=gs://rmistry --record \
borenetdc89ca52014-10-17 07:37:05 -070012--page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
13--browser_executable=/tmp/chromium/out/Release/chrome
14
kkinnunenb4ee7ea2015-03-31 00:18:26 -070015The above command uses Google Storage bucket 'rmistry' to download needed files.
borenetdc89ca52014-10-17 07:37:05 -070016
17To replay archived webpages and re-generate SKP files (should be run whenever
18SkPicture.PICTURE_VERSION changes):
19
kkinnunenfcf35c52014-12-03 05:51:24 -080020cd skia
kkinnunenb4ee7ea2015-03-31 00:18:26 -070021python tools/skp/webpages_playback.py --data_store=gs://rmistry \
borenetdc89ca52014-10-17 07:37:05 -070022--page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
23--browser_executable=/tmp/chromium/out/Release/chrome
24
25
26Specify the --page_sets flag (default value is 'all') to pick a list of which
27webpages should be archived and/or replayed. Eg:
28
kkinnunenfcf35c52014-12-03 05:51:24 -080029--page_sets=tools/skp/page_sets/skia_yahooanswers_desktop.py,\
30tools/skp/page_sets/skia_googlecalendar_nexus10.py
borenetdc89ca52014-10-17 07:37:05 -070031
32The --browser_executable flag should point to the browser binary you want to use
33to capture archives and/or capture SKP files. Majority of the time it should be
34a newly built chrome binary.
35
Ravi Mistry97477112017-12-13 10:39:20 -050036The --data_store flag controls where the needed artifacts are downloaded from.
37It also controls where the generated artifacts, such as recorded webpages and
38resulting skp renderings, are uploaded to. URLs with scheme 'gs://' use Google
39Storage. Otherwise use local filesystem.
kkinnunenb4ee7ea2015-03-31 00:18:26 -070040
41The --upload=True flag means generated artifacts will be
42uploaded or copied to the location specified by --data_store. (default value is
43False if not specified).
borenetdc89ca52014-10-17 07:37:05 -070044
45The --non-interactive flag controls whether the script will prompt the user
46(default value is False if not specified).
47
48The --skia_tools flag if specified will allow this script to run
49debugger, render_pictures, and render_pdfs on the captured
50SKP(s). The tools are run after all SKPs are succesfully captured to make sure
51they can be added to the buildbots with no breakages.
borenetdc89ca52014-10-17 07:37:05 -070052"""
53
54import glob
55import optparse
56import os
57import posixpath
58import shutil
59import subprocess
60import sys
61import tempfile
62import time
63import traceback
64
borenetdc89ca52014-10-17 07:37:05 -070065
66ROOT_PLAYBACK_DIR_NAME = 'playback'
67SKPICTURES_DIR_NAME = 'skps'
68
borenet644638c2016-10-10 05:57:18 -070069GS_PREFIX = 'gs://'
70
rmistry0575c492016-02-01 10:27:05 -080071PARTNERS_GS_BUCKET = 'gs://chrome-partner-telemetry'
borenetdc89ca52014-10-17 07:37:05 -070072
73# Local archive and SKP directories.
borenetdc89ca52014-10-17 07:37:05 -070074LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
75 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
76TMP_SKP_DIR = tempfile.mkdtemp()
77
borenetdc89ca52014-10-17 07:37:05 -070078# Name of the SKP benchmark
79SKP_BENCHMARK = 'skpicture_printer'
80
81# The max base name length of Skp files.
82MAX_SKP_BASE_NAME_LEN = 31
83
84# Dictionary of device to platform prefixes for SKP files.
85DEVICE_TO_PLATFORM_PREFIX = {
86 'desktop': 'desk',
87 'galaxynexus': 'mobi',
88 'nexus10': 'tabl'
89}
90
91# How many times the record_wpr binary should be retried.
92RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -070093# How many times the run_benchmark binary should be retried.
Ravi Mistry0f8e4db2017-11-28 11:24:32 -050094RETRY_RUN_MEASUREMENT_COUNT = 3
borenetdc89ca52014-10-17 07:37:05 -070095
96X11_DISPLAY = os.getenv('DISPLAY', ':0')
97
rmistry49d093c2015-03-31 05:04:29 -070098# Path to Chromium's page sets.
99CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
100
101# Dictionary of supported Chromium page sets to their file prefixes.
102CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700103 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700104 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700105}
106
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500107PAGE_SETS_TO_EXCLUSIONS = {
108 # See skbug.com/7348
Ravi Mistryda17f1e2018-05-20 10:46:04 -0400109 'key_mobile_sites_smooth.py': '"(digg|worldjournal|twitter|espn)"',
Ravi Mistry5b92ce12017-12-18 12:30:04 -0500110 # See skbug.com/7421
111 'top_25_smooth.py': '"(mail\.google\.com)"',
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500112}
113
rmistry49d093c2015-03-31 05:04:29 -0700114
kkinnunene75d2d22014-12-03 04:38:46 -0800115def remove_prefix(s, prefix):
116 if s.startswith(prefix):
117 return s[len(prefix):]
118 return s
borenetdc89ca52014-10-17 07:37:05 -0700119
rmistry49d093c2015-03-31 05:04:29 -0700120
borenetdc89ca52014-10-17 07:37:05 -0700121class SkPicturePlayback(object):
122 """Class that archives or replays webpages and creates SKPs."""
123
124 def __init__(self, parse_options):
125 """Constructs a SkPicturePlayback BuildStep instance."""
126 assert parse_options.browser_executable, 'Must specify --browser_executable'
127 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700128 self._browser_args = '--disable-setuid-sandbox'
129 if parse_options.browser_extra_args:
130 self._browser_args = '%s %s' % (
131 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700132
rmistry49d093c2015-03-31 05:04:29 -0700133 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
134 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700135 self._all_page_sets_specified = parse_options.page_sets == 'all'
136 self._page_sets = self._ParsePageSets(parse_options.page_sets)
137
borenetdc89ca52014-10-17 07:37:05 -0700138 self._record = parse_options.record
139 self._skia_tools = parse_options.skia_tools
140 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700141 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700142 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700143 data_store_location = parse_options.data_store
borenet644638c2016-10-10 05:57:18 -0700144 if data_store_location.startswith(GS_PREFIX):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700145 self.gs = GoogleStorageDataStore(data_store_location)
146 else:
147 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800148 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
borenetdc89ca52014-10-17 07:37:05 -0700149 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700150 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
151 'tools', 'perf')
rmistryafaf4962016-02-27 10:04:57 -0800152 self._catapult_dir = os.path.join(parse_options.chrome_src_path,
153 'third_party', 'catapult')
borenetdc89ca52014-10-17 07:37:05 -0700154
155 self._local_skp_dir = os.path.join(
156 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
157 self._local_record_webpages_archive_dir = os.path.join(
158 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
159
160 # List of SKP files generated by this script.
161 self._skp_files = []
162
163 def _ParsePageSets(self, page_sets):
164 if not page_sets:
165 raise ValueError('Must specify at least one page_set!')
166 elif self._all_page_sets_specified:
167 # Get everything from the page_sets directory.
168 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
169 'page_sets')
170 ps = [os.path.join(page_sets_dir, page_set)
171 for page_set in os.listdir(page_sets_dir)
172 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
173 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700174 chromium_ps = [
175 os.path.join(self._chrome_page_sets_path, cr_page_set)
176 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
177 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700178 elif '*' in page_sets:
179 # Explode and return the glob.
180 ps = glob.glob(page_sets)
181 else:
182 ps = page_sets.split(',')
183 ps.sort()
184 return ps
185
rmistry49d093c2015-03-31 05:04:29 -0700186 def _IsChromiumPageSet(self, page_set):
187 """Returns true if the specified page set is a Chromium page set."""
188 return page_set.startswith(self._chrome_page_sets_path)
189
borenetdc89ca52014-10-17 07:37:05 -0700190 def Run(self):
191 """Run the SkPicturePlayback BuildStep."""
192
193 # Delete any left over data files in the data directory.
194 for archive_file in glob.glob(
195 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
196 os.remove(archive_file)
197
borenetdc89ca52014-10-17 07:37:05 -0700198 # Create the required local storage directories.
199 self._CreateLocalStorageDirs()
200
201 # Start the timer.
202 start_time = time.time()
203
204 # Loop through all page_sets.
205 for page_set in self._page_sets:
206
rmistry7620bf02014-10-27 06:42:11 -0700207 page_set_basename = os.path.basename(page_set).split('.')[0]
208 page_set_json_name = page_set_basename + '.json'
Ravi Mistry0d056ba2017-08-02 11:21:58 -0400209 wpr_data_file = (
210 page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wprgo')
rmistry7620bf02014-10-27 06:42:11 -0700211 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700212
rmistry49d093c2015-03-31 05:04:29 -0700213 if self._IsChromiumPageSet(page_set):
214 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
215 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700216 # Create an archive of the specified webpages if '--record=True' is
217 # specified.
218 record_wpr_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800219 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700220 'DISPLAY=%s' % X11_DISPLAY,
221 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700222 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700223 '--browser=exact',
224 '--browser-executable=%s' % self._browser_executable,
Ravi Mistry0d056ba2017-08-02 11:21:58 -0400225 '--use-wpr-go',
rmistry7620bf02014-10-27 06:42:11 -0700226 '%s_page_set' % page_set_basename,
227 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700228 )
229 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700230 try:
borenet644638c2016-10-10 05:57:18 -0700231 subprocess.check_call(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700232
Ravi Mistry863ffef2017-07-18 13:13:53 -0400233 # Copy over the created archive into the local webpages archive
kkinnunenf9310fe2015-03-29 22:33:16 -0700234 # directory.
Ravi Mistry863ffef2017-07-18 13:13:53 -0400235 shutil.copy(
kkinnunenf9310fe2015-03-29 22:33:16 -0700236 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
237 self._local_record_webpages_archive_dir)
Ravi Mistry863ffef2017-07-18 13:13:53 -0400238 shutil.copy(
kkinnunenf9310fe2015-03-29 22:33:16 -0700239 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
240 page_set_json_name),
241 self._local_record_webpages_archive_dir)
242
borenetdc89ca52014-10-17 07:37:05 -0700243 # Break out of the retry loop since there were no errors.
244 break
rmistry0ec28af2014-10-28 14:25:17 -0700245 except Exception:
246 # There was a failure continue with the loop.
247 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700248 else:
249 # If we get here then record_wpr did not succeed and thus did not
250 # break out of the loop.
251 raise Exception('record_wpr failed for page_set: %s' % page_set)
252
253 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700254 # Get the webpages archive so that it can be replayed.
255 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700256
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500257 run_benchmark_cmd = [
rmistryafaf4962016-02-27 10:04:57 -0800258 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700259 'DISPLAY=%s' % X11_DISPLAY,
Ravi Mistry57d69652016-09-08 09:08:44 -0400260 'timeout', '1800',
borenet78399152014-10-17 12:15:46 -0700261 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700262 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700263 '--browser=exact',
264 '--browser-executable=%s' % self._browser_executable,
265 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700266 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700267 '--page-set-base-dir=%s' % page_set_dir,
268 '--skp-outdir=%s' % TMP_SKP_DIR,
Ravi Mistry0f8e4db2017-11-28 11:24:32 -0500269 '--also-run-disabled-tests',
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500270 ]
271
272 exclusions = PAGE_SETS_TO_EXCLUSIONS.get(os.path.basename(page_set))
273 if exclusions:
274 run_benchmark_cmd.append('--story-filter-exclude=' + exclusions)
borenetdc89ca52014-10-17 07:37:05 -0700275
276 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
277 try:
278 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet644638c2016-10-10 05:57:18 -0700279 subprocess.check_call(' '.join(run_benchmark_cmd), shell=True)
280 except subprocess.CalledProcessError:
borenetdc89ca52014-10-17 07:37:05 -0700281 # There was a failure continue with the loop.
282 traceback.print_exc()
283 print '\n\n=======Retrying %s=======\n\n' % page_set
284 time.sleep(10)
Ravi Mistry0f8e4db2017-11-28 11:24:32 -0500285 continue
286
287 # Rename generated SKP files into more descriptive names.
288 self._RenameSkpFiles(page_set)
289 # Break out of the retry loop since there were no errors.
290 break
borenetdc89ca52014-10-17 07:37:05 -0700291 else:
borenet78399152014-10-17 12:15:46 -0700292 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700293 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700294 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700295
borenetdc89ca52014-10-17 07:37:05 -0700296 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
297 time.time() - start_time)
298
299 if self._skia_tools:
300 render_pictures_cmd = [
301 os.path.join(self._skia_tools, 'render_pictures'),
302 '-r', self._local_skp_dir
303 ]
304 render_pdfs_cmd = [
305 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800306 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700307 ]
308
309 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
310 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
borenet644638c2016-10-10 05:57:18 -0700311 subprocess.check_call(tools_cmd)
borenetdc89ca52014-10-17 07:37:05 -0700312
313 if not self._non_interactive:
314 print '\n\n=======Running debugger======='
315 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800316 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700317
318 print '\n\n'
319
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700320 if self._upload:
321 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700322 # Copy the directory structure in the root directory into Google Storage.
323 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
324 if self._alternate_upload_dir:
325 dest_dir_name = self._alternate_upload_dir
326
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700327 self.gs.upload_dir_contents(
borenet644638c2016-10-10 05:57:18 -0700328 self._local_skp_dir, dest_dir=dest_dir_name)
borenetdc89ca52014-10-17 07:37:05 -0700329
330 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700331 posixpath.join(self.gs.target_name(), dest_dir_name,
332 SKPICTURES_DIR_NAME))
rmistry0575c492016-02-01 10:27:05 -0800333
borenetdc89ca52014-10-17 07:37:05 -0700334 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700335 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700336 print 'Generated resources are available in %s\n\n' % (
borenet1413d522016-08-08 06:26:00 -0700337 self._local_skp_dir)
borenetdc89ca52014-10-17 07:37:05 -0700338
borenet28698472016-07-25 05:18:15 -0700339 if self._upload_to_partner_bucket:
340 print '\n\n=======Uploading to Partner bucket %s =======\n\n' % (
341 PARTNERS_GS_BUCKET)
342 partner_gs = GoogleStorageDataStore(PARTNERS_GS_BUCKET)
343 partner_gs.delete_path(SKPICTURES_DIR_NAME)
borenet1413d522016-08-08 06:26:00 -0700344 print 'Uploading %s to %s' % (self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet644638c2016-10-10 05:57:18 -0700345 partner_gs.upload_dir_contents(self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet28698472016-07-25 05:18:15 -0700346 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
347 posixpath.join(partner_gs.target_name(), SKPICTURES_DIR_NAME))
348
borenetdc89ca52014-10-17 07:37:05 -0700349 return 0
350
rmistry49d093c2015-03-31 05:04:29 -0700351 def _GetSkiaSkpFileName(self, page_set):
352 """Returns the SKP file name for Skia page sets."""
353 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
354 ps_filename = os.path.basename(page_set)
355 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
356 ps_basename, _ = os.path.splitext(ps_filename)
357 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
358 _, page_name, device = ps_basename.split('_')
359 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
360 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
361
362 def _GetChromiumSkpFileName(self, page_set, site):
363 """Returns the SKP file name for Chromium page sets."""
364 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
365 _, webpage = os.path.split(site)
366 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700367 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700368 if webpage.startswith(prefix):
369 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700370 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
371 ps_filename = os.path.basename(page_set)
372 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
373 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
374 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
375
borenetdc89ca52014-10-17 07:37:05 -0700376 def _RenameSkpFiles(self, page_set):
377 """Rename generated SKP files into more descriptive names.
378
379 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
380 .skp in there to be this page_set's representative .skp.
381 """
borenetdc89ca52014-10-17 07:37:05 -0700382 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700383 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700384 if self._IsChromiumPageSet(page_set):
385 filename = self._GetChromiumSkpFileName(page_set, site)
386 else:
387 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700388 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700389
rmistryaa31ee72015-04-23 12:47:33 -0700390 if self._skp_prefix:
391 filename = '%s%s' % (self._skp_prefix, filename)
392
borenetdc89ca52014-10-17 07:37:05 -0700393 # We choose the largest .skp as the most likely to be interesting.
394 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
395 key=lambda path: os.stat(path).st_size)
396 dest = os.path.join(self._local_skp_dir, filename)
397 print 'Moving', largest_skp, 'to', dest
398 shutil.move(largest_skp, dest)
399 self._skp_files.append(filename)
400 shutil.rmtree(site)
401
402 def _CreateLocalStorageDirs(self):
403 """Creates required local storage directories for this script."""
404 for d in (self._local_record_webpages_archive_dir,
405 self._local_skp_dir):
406 if os.path.exists(d):
407 shutil.rmtree(d)
408 os.makedirs(d)
409
rmistry7620bf02014-10-27 06:42:11 -0700410 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700411 """Downloads the webpages archive and its required page set from GS."""
412 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
413 wpr_data_file)
414 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
415 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700416 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700417 gs = self.gs
418 if (gs.does_storage_object_exist(wpr_source) and
419 gs.does_storage_object_exist(page_set_source)):
420 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700421 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
422 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700423 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700424 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700425 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700426 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700427 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
428 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700429
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700430class DataStore:
431 """An abstract base class for uploading recordings to a data storage.
432 The interface emulates the google storage api."""
433 def target_name(self):
434 raise NotImplementedError()
435 def target_type(self):
436 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700437 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700438 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700439 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700440 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700441 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700442 raise NotImplementedError()
443
borenet644638c2016-10-10 05:57:18 -0700444
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700445class GoogleStorageDataStore(DataStore):
446 def __init__(self, data_store_url):
borenet1cebf7b2016-10-10 08:14:11 -0700447 self._url = data_store_url.rstrip('/')
borenet644638c2016-10-10 05:57:18 -0700448
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700449 def target_name(self):
borenet644638c2016-10-10 05:57:18 -0700450 return self._url
451
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700452 def target_type(self):
453 return 'Google Storage'
borenet644638c2016-10-10 05:57:18 -0700454
455 def does_storage_object_exist(self, name):
456 try:
457 output = subprocess.check_output([
458 'gsutil', 'ls', '/'.join((self._url, name))])
459 except subprocess.CalledProcessError:
460 return False
borenet75358672016-10-10 13:20:49 -0700461 if len(output.splitlines()) != 1:
borenet644638c2016-10-10 05:57:18 -0700462 return False
463 return True
464
rmistryc33c79c2016-02-03 04:27:54 -0800465 def delete_path(self, path):
borenet77874632016-10-17 05:12:10 -0700466 subprocess.check_call(['gsutil', 'rm', '-r', '/'.join((self._url, path))])
borenet644638c2016-10-10 05:57:18 -0700467
468 def download_file(self, name, local_path):
469 subprocess.check_call([
470 'gsutil', 'cp', '/'.join((self._url, name)), local_path])
471
472 def upload_dir_contents(self, source_dir, dest_dir):
473 subprocess.check_call([
borenet27f93e02016-10-17 08:09:33 -0700474 'gsutil', 'cp', '-r', source_dir, '/'.join((self._url, dest_dir))])
borenet644638c2016-10-10 05:57:18 -0700475
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700476
477class LocalFileSystemDataStore(DataStore):
478 def __init__(self, data_store_location):
479 self._base_dir = data_store_location
480 def target_name(self):
481 return self._base_dir
482 def target_type(self):
483 return self._base_dir
borenet644638c2016-10-10 05:57:18 -0700484 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700485 return os.path.isfile(os.path.join(self._base_dir, name))
rmistryc33c79c2016-02-03 04:27:54 -0800486 def delete_path(self, path):
487 shutil.rmtree(path)
borenet644638c2016-10-10 05:57:18 -0700488 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700489 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
borenet644638c2016-10-10 05:57:18 -0700490 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700491 def copytree(source_dir, dest_dir):
492 if not os.path.exists(dest_dir):
493 os.makedirs(dest_dir)
494 for item in os.listdir(source_dir):
495 source = os.path.join(source_dir, item)
496 dest = os.path.join(dest_dir, item)
497 if os.path.isdir(source):
498 copytree(source, dest)
499 else:
500 shutil.copy2(source, dest)
501 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700502
503if '__main__' == __name__:
504 option_parser = optparse.OptionParser()
505 option_parser.add_option(
506 '', '--page_sets',
507 help='Specifies the page sets to use to archive. Supports globs.',
508 default='all')
509 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700510 '', '--record', action='store_true',
511 help='Specifies whether a new website archive should be created.',
512 default=False)
513 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700514 '', '--skia_tools',
515 help=('Path to compiled Skia executable tools. '
516 'render_pictures/render_pdfs is run on the set '
517 'after all SKPs are captured. If the script is run without '
518 '--non-interactive then the debugger is also run at the end. Debug '
519 'builds are recommended because they seem to catch more failures '
520 'than Release builds.'),
521 default=None)
522 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700523 '', '--upload', action='store_true',
524 help=('Uploads to Google Storage or copies to local filesystem storage '
525 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700526 default=False)
527 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800528 '', '--upload_to_partner_bucket', action='store_true',
529 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
530 'bucket if true.'),
531 default=False)
532 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700533 '', '--data_store',
534 help=('The location of the file storage to use to download and upload '
535 'files. Can be \'gs://<bucket>\' for Google Storage, or '
536 'a directory for local filesystem storage'),
boreneta86952a2016-10-05 08:23:04 -0700537 default='gs://skia-skps')
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700538 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700539 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700540 help= ('Uploads to a different directory in Google Storage or local '
541 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700542 default=None)
543 option_parser.add_option(
544 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700545 help=('Temporary directory where SKPs and webpage archives will be '
546 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700547 default=tempfile.gettempdir())
548 option_parser.add_option(
549 '', '--browser_executable',
550 help='The exact browser executable to run.',
551 default=None)
552 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700553 '', '--browser_extra_args',
554 help='Additional arguments to pass to the browser.',
555 default=None)
556 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700557 '', '--chrome_src_path',
558 help='Path to the chromium src directory.',
559 default=None)
560 option_parser.add_option(
561 '', '--non-interactive', action='store_true',
562 help='Runs the script without any prompts. If this flag is specified and '
563 '--skia_tools is specified then the debugger is not run.',
564 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700565 option_parser.add_option(
566 '', '--skp_prefix',
567 help='Prefix to add to the names of generated SKPs.',
568 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700569 options, unused_args = option_parser.parse_args()
570
571 playback = SkPicturePlayback(options)
572 sys.exit(playback.Run())