blob: 805b3f3bab3decad466a716b79e3240460819857 [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
Ravi Mistry2b1d1792019-02-06 12:33:44 -050054import datetime
borenetdc89ca52014-10-17 07:37:05 -070055import glob
56import optparse
57import os
58import posixpath
59import shutil
60import subprocess
61import sys
62import tempfile
63import time
64import traceback
65
borenetdc89ca52014-10-17 07:37:05 -070066
67ROOT_PLAYBACK_DIR_NAME = 'playback'
68SKPICTURES_DIR_NAME = 'skps'
69
borenet644638c2016-10-10 05:57:18 -070070GS_PREFIX = 'gs://'
71
rmistry0575c492016-02-01 10:27:05 -080072PARTNERS_GS_BUCKET = 'gs://chrome-partner-telemetry'
borenetdc89ca52014-10-17 07:37:05 -070073
74# Local archive and SKP directories.
borenetdc89ca52014-10-17 07:37:05 -070075LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
76 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
77TMP_SKP_DIR = tempfile.mkdtemp()
78
borenetdc89ca52014-10-17 07:37:05 -070079# Name of the SKP benchmark
80SKP_BENCHMARK = 'skpicture_printer'
81
82# The max base name length of Skp files.
83MAX_SKP_BASE_NAME_LEN = 31
84
85# Dictionary of device to platform prefixes for SKP files.
86DEVICE_TO_PLATFORM_PREFIX = {
87 'desktop': 'desk',
88 'galaxynexus': 'mobi',
89 'nexus10': 'tabl'
90}
91
92# How many times the record_wpr binary should be retried.
93RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -070094# How many times the run_benchmark binary should be retried.
Ravi Mistry0f8e4db2017-11-28 11:24:32 -050095RETRY_RUN_MEASUREMENT_COUNT = 3
borenetdc89ca52014-10-17 07:37:05 -070096
97X11_DISPLAY = os.getenv('DISPLAY', ':0')
98
rmistry49d093c2015-03-31 05:04:29 -070099# Path to Chromium's page sets.
100CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
101
102# Dictionary of supported Chromium page sets to their file prefixes.
103CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700104 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700105 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700106}
107
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500108PAGE_SETS_TO_EXCLUSIONS = {
109 # See skbug.com/7348
Ravi Mistryda17f1e2018-05-20 10:46:04 -0400110 'key_mobile_sites_smooth.py': '"(digg|worldjournal|twitter|espn)"',
Ravi Mistry5b92ce12017-12-18 12:30:04 -0500111 # See skbug.com/7421
112 'top_25_smooth.py': '"(mail\.google\.com)"',
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500113}
114
rmistry49d093c2015-03-31 05:04:29 -0700115
kkinnunene75d2d22014-12-03 04:38:46 -0800116def remove_prefix(s, prefix):
117 if s.startswith(prefix):
118 return s[len(prefix):]
119 return s
borenetdc89ca52014-10-17 07:37:05 -0700120
rmistry49d093c2015-03-31 05:04:29 -0700121
borenetdc89ca52014-10-17 07:37:05 -0700122class SkPicturePlayback(object):
123 """Class that archives or replays webpages and creates SKPs."""
124
125 def __init__(self, parse_options):
126 """Constructs a SkPicturePlayback BuildStep instance."""
127 assert parse_options.browser_executable, 'Must specify --browser_executable'
128 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700129 self._browser_args = '--disable-setuid-sandbox'
130 if parse_options.browser_extra_args:
131 self._browser_args = '%s %s' % (
132 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700133
rmistry49d093c2015-03-31 05:04:29 -0700134 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
135 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700136 self._all_page_sets_specified = parse_options.page_sets == 'all'
137 self._page_sets = self._ParsePageSets(parse_options.page_sets)
138
borenetdc89ca52014-10-17 07:37:05 -0700139 self._record = parse_options.record
140 self._skia_tools = parse_options.skia_tools
141 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700142 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700143 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700144 data_store_location = parse_options.data_store
borenet644638c2016-10-10 05:57:18 -0700145 if data_store_location.startswith(GS_PREFIX):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700146 self.gs = GoogleStorageDataStore(data_store_location)
147 else:
148 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800149 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
borenetdc89ca52014-10-17 07:37:05 -0700150 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700151 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
152 'tools', 'perf')
rmistryafaf4962016-02-27 10:04:57 -0800153 self._catapult_dir = os.path.join(parse_options.chrome_src_path,
154 'third_party', 'catapult')
borenetdc89ca52014-10-17 07:37:05 -0700155
156 self._local_skp_dir = os.path.join(
157 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
158 self._local_record_webpages_archive_dir = os.path.join(
159 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
160
161 # List of SKP files generated by this script.
162 self._skp_files = []
163
164 def _ParsePageSets(self, page_sets):
165 if not page_sets:
166 raise ValueError('Must specify at least one page_set!')
167 elif self._all_page_sets_specified:
168 # Get everything from the page_sets directory.
169 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
170 'page_sets')
171 ps = [os.path.join(page_sets_dir, page_set)
172 for page_set in os.listdir(page_sets_dir)
173 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
174 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700175 chromium_ps = [
176 os.path.join(self._chrome_page_sets_path, cr_page_set)
177 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
178 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700179 elif '*' in page_sets:
180 # Explode and return the glob.
181 ps = glob.glob(page_sets)
182 else:
183 ps = page_sets.split(',')
184 ps.sort()
185 return ps
186
rmistry49d093c2015-03-31 05:04:29 -0700187 def _IsChromiumPageSet(self, page_set):
188 """Returns true if the specified page set is a Chromium page set."""
189 return page_set.startswith(self._chrome_page_sets_path)
190
borenetdc89ca52014-10-17 07:37:05 -0700191 def Run(self):
192 """Run the SkPicturePlayback BuildStep."""
193
194 # Delete any left over data files in the data directory.
195 for archive_file in glob.glob(
196 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
197 os.remove(archive_file)
198
borenetdc89ca52014-10-17 07:37:05 -0700199 # Create the required local storage directories.
200 self._CreateLocalStorageDirs()
201
202 # Start the timer.
203 start_time = time.time()
204
205 # Loop through all page_sets.
206 for page_set in self._page_sets:
207
rmistry7620bf02014-10-27 06:42:11 -0700208 page_set_basename = os.path.basename(page_set).split('.')[0]
209 page_set_json_name = page_set_basename + '.json'
Ravi Mistry0d056ba2017-08-02 11:21:58 -0400210 wpr_data_file = (
211 page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wprgo')
rmistry7620bf02014-10-27 06:42:11 -0700212 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700213
rmistry49d093c2015-03-31 05:04:29 -0700214 if self._IsChromiumPageSet(page_set):
215 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
216 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700217 # Create an archive of the specified webpages if '--record=True' is
218 # specified.
219 record_wpr_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800220 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700221 'DISPLAY=%s' % X11_DISPLAY,
222 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700223 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700224 '--browser=exact',
225 '--browser-executable=%s' % self._browser_executable,
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)
Ravi Mistry2b1d1792019-02-06 12:33:44 -0500343 timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%d')
344 upload_dir = posixpath.join(SKPICTURES_DIR_NAME, timestamp)
345 try:
346 partner_gs.delete_path(upload_dir)
347 except subprocess.CalledProcessError:
348 print 'Cannot delete %s because it does not exist yet.' % upload_dir
349 print 'Uploading %s to %s' % (self._local_skp_dir, upload_dir)
350 partner_gs.upload_dir_contents(self._local_skp_dir, upload_dir)
borenet28698472016-07-25 05:18:15 -0700351 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
Ravi Mistry2b1d1792019-02-06 12:33:44 -0500352 posixpath.join(partner_gs.target_name(), upload_dir))
borenet28698472016-07-25 05:18:15 -0700353
borenetdc89ca52014-10-17 07:37:05 -0700354 return 0
355
rmistry49d093c2015-03-31 05:04:29 -0700356 def _GetSkiaSkpFileName(self, page_set):
357 """Returns the SKP file name for Skia page sets."""
358 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
359 ps_filename = os.path.basename(page_set)
360 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
361 ps_basename, _ = os.path.splitext(ps_filename)
362 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
363 _, page_name, device = ps_basename.split('_')
364 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
365 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
366
367 def _GetChromiumSkpFileName(self, page_set, site):
368 """Returns the SKP file name for Chromium page sets."""
369 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
370 _, webpage = os.path.split(site)
371 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700372 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700373 if webpage.startswith(prefix):
374 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700375 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
376 ps_filename = os.path.basename(page_set)
377 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
378 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
379 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
380
borenetdc89ca52014-10-17 07:37:05 -0700381 def _RenameSkpFiles(self, page_set):
382 """Rename generated SKP files into more descriptive names.
383
384 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
385 .skp in there to be this page_set's representative .skp.
386 """
borenetdc89ca52014-10-17 07:37:05 -0700387 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700388 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700389 if self._IsChromiumPageSet(page_set):
390 filename = self._GetChromiumSkpFileName(page_set, site)
391 else:
392 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700393 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700394
rmistryaa31ee72015-04-23 12:47:33 -0700395 if self._skp_prefix:
396 filename = '%s%s' % (self._skp_prefix, filename)
397
borenetdc89ca52014-10-17 07:37:05 -0700398 # We choose the largest .skp as the most likely to be interesting.
399 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
400 key=lambda path: os.stat(path).st_size)
401 dest = os.path.join(self._local_skp_dir, filename)
402 print 'Moving', largest_skp, 'to', dest
403 shutil.move(largest_skp, dest)
404 self._skp_files.append(filename)
405 shutil.rmtree(site)
406
407 def _CreateLocalStorageDirs(self):
408 """Creates required local storage directories for this script."""
409 for d in (self._local_record_webpages_archive_dir,
410 self._local_skp_dir):
411 if os.path.exists(d):
412 shutil.rmtree(d)
413 os.makedirs(d)
414
rmistry7620bf02014-10-27 06:42:11 -0700415 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700416 """Downloads the webpages archive and its required page set from GS."""
417 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
418 wpr_data_file)
419 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
420 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700421 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700422 gs = self.gs
423 if (gs.does_storage_object_exist(wpr_source) and
424 gs.does_storage_object_exist(page_set_source)):
425 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700426 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
427 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700428 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700429 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700430 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700431 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700432 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
433 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700434
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700435class DataStore:
436 """An abstract base class for uploading recordings to a data storage.
437 The interface emulates the google storage api."""
438 def target_name(self):
439 raise NotImplementedError()
440 def target_type(self):
441 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700442 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700443 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700444 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700445 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700446 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700447 raise NotImplementedError()
448
borenet644638c2016-10-10 05:57:18 -0700449
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700450class GoogleStorageDataStore(DataStore):
451 def __init__(self, data_store_url):
borenet1cebf7b2016-10-10 08:14:11 -0700452 self._url = data_store_url.rstrip('/')
borenet644638c2016-10-10 05:57:18 -0700453
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700454 def target_name(self):
borenet644638c2016-10-10 05:57:18 -0700455 return self._url
456
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700457 def target_type(self):
458 return 'Google Storage'
borenet644638c2016-10-10 05:57:18 -0700459
460 def does_storage_object_exist(self, name):
461 try:
462 output = subprocess.check_output([
463 'gsutil', 'ls', '/'.join((self._url, name))])
464 except subprocess.CalledProcessError:
465 return False
borenet75358672016-10-10 13:20:49 -0700466 if len(output.splitlines()) != 1:
borenet644638c2016-10-10 05:57:18 -0700467 return False
468 return True
469
rmistryc33c79c2016-02-03 04:27:54 -0800470 def delete_path(self, path):
borenet77874632016-10-17 05:12:10 -0700471 subprocess.check_call(['gsutil', 'rm', '-r', '/'.join((self._url, path))])
borenet644638c2016-10-10 05:57:18 -0700472
473 def download_file(self, name, local_path):
474 subprocess.check_call([
475 'gsutil', 'cp', '/'.join((self._url, name)), local_path])
476
477 def upload_dir_contents(self, source_dir, dest_dir):
478 subprocess.check_call([
borenet27f93e02016-10-17 08:09:33 -0700479 'gsutil', 'cp', '-r', source_dir, '/'.join((self._url, dest_dir))])
borenet644638c2016-10-10 05:57:18 -0700480
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700481
482class LocalFileSystemDataStore(DataStore):
483 def __init__(self, data_store_location):
484 self._base_dir = data_store_location
485 def target_name(self):
486 return self._base_dir
487 def target_type(self):
488 return self._base_dir
borenet644638c2016-10-10 05:57:18 -0700489 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700490 return os.path.isfile(os.path.join(self._base_dir, name))
rmistryc33c79c2016-02-03 04:27:54 -0800491 def delete_path(self, path):
492 shutil.rmtree(path)
borenet644638c2016-10-10 05:57:18 -0700493 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700494 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
borenet644638c2016-10-10 05:57:18 -0700495 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700496 def copytree(source_dir, dest_dir):
497 if not os.path.exists(dest_dir):
498 os.makedirs(dest_dir)
499 for item in os.listdir(source_dir):
500 source = os.path.join(source_dir, item)
501 dest = os.path.join(dest_dir, item)
502 if os.path.isdir(source):
503 copytree(source, dest)
504 else:
505 shutil.copy2(source, dest)
506 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700507
508if '__main__' == __name__:
509 option_parser = optparse.OptionParser()
510 option_parser.add_option(
511 '', '--page_sets',
512 help='Specifies the page sets to use to archive. Supports globs.',
513 default='all')
514 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700515 '', '--record', action='store_true',
516 help='Specifies whether a new website archive should be created.',
517 default=False)
518 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700519 '', '--skia_tools',
520 help=('Path to compiled Skia executable tools. '
521 'render_pictures/render_pdfs is run on the set '
522 'after all SKPs are captured. If the script is run without '
523 '--non-interactive then the debugger is also run at the end. Debug '
524 'builds are recommended because they seem to catch more failures '
525 'than Release builds.'),
526 default=None)
527 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700528 '', '--upload', action='store_true',
529 help=('Uploads to Google Storage or copies to local filesystem storage '
530 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700531 default=False)
532 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800533 '', '--upload_to_partner_bucket', action='store_true',
534 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
535 'bucket if true.'),
536 default=False)
537 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700538 '', '--data_store',
539 help=('The location of the file storage to use to download and upload '
540 'files. Can be \'gs://<bucket>\' for Google Storage, or '
541 'a directory for local filesystem storage'),
boreneta86952a2016-10-05 08:23:04 -0700542 default='gs://skia-skps')
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700543 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700544 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700545 help= ('Uploads to a different directory in Google Storage or local '
546 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700547 default=None)
548 option_parser.add_option(
549 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700550 help=('Temporary directory where SKPs and webpage archives will be '
551 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700552 default=tempfile.gettempdir())
553 option_parser.add_option(
554 '', '--browser_executable',
555 help='The exact browser executable to run.',
556 default=None)
557 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700558 '', '--browser_extra_args',
559 help='Additional arguments to pass to the browser.',
560 default=None)
561 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700562 '', '--chrome_src_path',
563 help='Path to the chromium src directory.',
564 default=None)
565 option_parser.add_option(
566 '', '--non-interactive', action='store_true',
567 help='Runs the script without any prompts. If this flag is specified and '
568 '--skia_tools is specified then the debugger is not run.',
569 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700570 option_parser.add_option(
571 '', '--skp_prefix',
572 help='Prefix to add to the names of generated SKPs.',
573 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700574 options, unused_args = option_parser.parse_args()
575
576 playback = SkPicturePlayback(options)
577 sys.exit(playback.Run())