blob: 1bd216391184efe11e37c908bbbd838d94cc77e9 [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
kkinnunenb4ee7ea2015-03-31 00:18:26 -070036The --data_store flag controls where the needed artifacts, such as
37credential files, are downloaded from. It also controls where the
38generated artifacts, such as recorded webpages and resulting skp renderings,
39are uploaded to. URLs with scheme 'gs://' use Google Storage. Otherwise
40use local filesystem.
41
42The --upload=True flag means generated artifacts will be
43uploaded or copied to the location specified by --data_store. (default value is
44False if not specified).
borenetdc89ca52014-10-17 07:37:05 -070045
46The --non-interactive flag controls whether the script will prompt the user
47(default value is False if not specified).
48
49The --skia_tools flag if specified will allow this script to run
50debugger, render_pictures, and render_pdfs on the captured
51SKP(s). The tools are run after all SKPs are succesfully captured to make sure
52they can be added to the buildbots with no breakages.
borenetdc89ca52014-10-17 07:37:05 -070053"""
54
55import glob
56import optparse
57import os
58import posixpath
59import shutil
60import subprocess
61import sys
62import tempfile
63import time
64import traceback
65
66sys.path.insert(0, os.getcwd())
67
68from common.py.utils import gs_utils
69from common.py.utils import shell_utils
70
71ROOT_PLAYBACK_DIR_NAME = 'playback'
72SKPICTURES_DIR_NAME = 'skps'
73
74
75# Local archive and SKP directories.
76LOCAL_PLAYBACK_ROOT_DIR = os.path.join(
77 tempfile.gettempdir(), ROOT_PLAYBACK_DIR_NAME)
78LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
79 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
80TMP_SKP_DIR = tempfile.mkdtemp()
81
rmistryf802f322014-10-22 05:04:43 -070082# Location of the credentials.json file and the string that represents missing
83# passwords.
84CREDENTIALS_FILE_PATH = os.path.join(
85 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
86 'credentials.json'
87)
88
borenetdc89ca52014-10-17 07:37:05 -070089# Name of the SKP benchmark
90SKP_BENCHMARK = 'skpicture_printer'
91
92# The max base name length of Skp files.
93MAX_SKP_BASE_NAME_LEN = 31
94
95# Dictionary of device to platform prefixes for SKP files.
96DEVICE_TO_PLATFORM_PREFIX = {
97 'desktop': 'desk',
98 'galaxynexus': 'mobi',
99 'nexus10': 'tabl'
100}
101
102# How many times the record_wpr binary should be retried.
103RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700104# How many times the run_benchmark binary should be retried.
borenetdc89ca52014-10-17 07:37:05 -0700105RETRY_RUN_MEASUREMENT_COUNT = 5
106
rmistryf802f322014-10-22 05:04:43 -0700107# Location of the credentials.json file in Google Storage.
108CREDENTIALS_GS_PATH = '/playback/credentials/credentials.json'
109
borenetdc89ca52014-10-17 07:37:05 -0700110X11_DISPLAY = os.getenv('DISPLAY', ':0')
111
112GS_PREDEFINED_ACL = gs_utils.GSUtils.PredefinedACL.PRIVATE
113GS_FINE_GRAINED_ACL_LIST = [
114 (gs_utils.GSUtils.IdType.GROUP_BY_DOMAIN, 'google.com',
115 gs_utils.GSUtils.Permission.READ),
116]
117
rmistry49d093c2015-03-31 05:04:29 -0700118# Path to Chromium's page sets.
119CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
120
121# Dictionary of supported Chromium page sets to their file prefixes.
122CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700123 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700124 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700125}
126
127
kkinnunene75d2d22014-12-03 04:38:46 -0800128def remove_prefix(s, prefix):
129 if s.startswith(prefix):
130 return s[len(prefix):]
131 return s
borenetdc89ca52014-10-17 07:37:05 -0700132
rmistry49d093c2015-03-31 05:04:29 -0700133
borenetdc89ca52014-10-17 07:37:05 -0700134class SkPicturePlayback(object):
135 """Class that archives or replays webpages and creates SKPs."""
136
137 def __init__(self, parse_options):
138 """Constructs a SkPicturePlayback BuildStep instance."""
139 assert parse_options.browser_executable, 'Must specify --browser_executable'
140 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700141 self._browser_args = '--disable-setuid-sandbox'
142 if parse_options.browser_extra_args:
143 self._browser_args = '%s %s' % (
144 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700145
rmistry49d093c2015-03-31 05:04:29 -0700146 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
147 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700148 self._all_page_sets_specified = parse_options.page_sets == 'all'
149 self._page_sets = self._ParsePageSets(parse_options.page_sets)
150
borenetdc89ca52014-10-17 07:37:05 -0700151 self._record = parse_options.record
152 self._skia_tools = parse_options.skia_tools
153 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700154 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700155 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700156 data_store_location = parse_options.data_store
157 if data_store_location.startswith(gs_utils.GS_PREFIX):
158 self.gs = GoogleStorageDataStore(data_store_location)
159 else:
160 self.gs = LocalFileSystemDataStore(data_store_location)
borenetdc89ca52014-10-17 07:37:05 -0700161 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700162 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
163 'tools', 'perf')
164
165 self._local_skp_dir = os.path.join(
166 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
167 self._local_record_webpages_archive_dir = os.path.join(
168 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
169
170 # List of SKP files generated by this script.
171 self._skp_files = []
172
173 def _ParsePageSets(self, page_sets):
174 if not page_sets:
175 raise ValueError('Must specify at least one page_set!')
176 elif self._all_page_sets_specified:
177 # Get everything from the page_sets directory.
178 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
179 'page_sets')
180 ps = [os.path.join(page_sets_dir, page_set)
181 for page_set in os.listdir(page_sets_dir)
182 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
183 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700184 chromium_ps = [
185 os.path.join(self._chrome_page_sets_path, cr_page_set)
186 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
187 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700188 elif '*' in page_sets:
189 # Explode and return the glob.
190 ps = glob.glob(page_sets)
191 else:
192 ps = page_sets.split(',')
193 ps.sort()
194 return ps
195
rmistry49d093c2015-03-31 05:04:29 -0700196 def _IsChromiumPageSet(self, page_set):
197 """Returns true if the specified page set is a Chromium page set."""
198 return page_set.startswith(self._chrome_page_sets_path)
199
borenetdc89ca52014-10-17 07:37:05 -0700200 def Run(self):
201 """Run the SkPicturePlayback BuildStep."""
202
rmistryf802f322014-10-22 05:04:43 -0700203 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700204 if not os.path.isfile(CREDENTIALS_FILE_PATH):
205 # Download the credentials.json file from Google Storage.
206 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
207
208 if not os.path.isfile(CREDENTIALS_FILE_PATH):
209 print """\n\nCould not locate credentials file in the storage.
210 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700211 {
212 "google": {
213 "username": "google_testing_account_username",
214 "password": "google_testing_account_password"
215 },
216 "facebook": {
217 "username": "facebook_testing_account_username",
218 "password": "facebook_testing_account_password"
219 }
220 }\n\n""" % CREDENTIALS_FILE_PATH
221 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700222
borenetdc89ca52014-10-17 07:37:05 -0700223 # Delete any left over data files in the data directory.
224 for archive_file in glob.glob(
225 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
226 os.remove(archive_file)
227
228 # Delete the local root directory if it already exists.
229 if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR):
230 shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR)
231
232 # Create the required local storage directories.
233 self._CreateLocalStorageDirs()
234
235 # Start the timer.
236 start_time = time.time()
237
238 # Loop through all page_sets.
239 for page_set in self._page_sets:
240
rmistry7620bf02014-10-27 06:42:11 -0700241 page_set_basename = os.path.basename(page_set).split('.')[0]
242 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700243 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700244 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700245
rmistry49d093c2015-03-31 05:04:29 -0700246 if self._IsChromiumPageSet(page_set):
247 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
248 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700249 # Create an archive of the specified webpages if '--record=True' is
250 # specified.
251 record_wpr_cmd = (
rmistry7620bf02014-10-27 06:42:11 -0700252 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700253 'DISPLAY=%s' % X11_DISPLAY,
254 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700255 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700256 '--browser=exact',
257 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700258 '%s_page_set' % page_set_basename,
259 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700260 )
261 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700262 try:
263 shell_utils.run(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700264
265 # Move over the created archive into the local webpages archive
266 # directory.
267 shutil.move(
268 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
269 self._local_record_webpages_archive_dir)
270 shutil.move(
271 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
272 page_set_json_name),
273 self._local_record_webpages_archive_dir)
274
borenetdc89ca52014-10-17 07:37:05 -0700275 # Break out of the retry loop since there were no errors.
276 break
rmistry0ec28af2014-10-28 14:25:17 -0700277 except Exception:
278 # There was a failure continue with the loop.
279 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700280 else:
281 # If we get here then record_wpr did not succeed and thus did not
282 # break out of the loop.
283 raise Exception('record_wpr failed for page_set: %s' % page_set)
284
285 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700286 # Get the webpages archive so that it can be replayed.
287 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700288
borenet78399152014-10-17 12:15:46 -0700289 run_benchmark_cmd = (
rmistryf802f322014-10-22 05:04:43 -0700290 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700291 'DISPLAY=%s' % X11_DISPLAY,
292 'timeout', '300',
borenet78399152014-10-17 12:15:46 -0700293 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700294 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700295 '--browser=exact',
296 '--browser-executable=%s' % self._browser_executable,
297 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700298 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700299 '--page-set-base-dir=%s' % page_set_dir,
300 '--skp-outdir=%s' % TMP_SKP_DIR,
301 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700302 )
borenetdc89ca52014-10-17 07:37:05 -0700303
304 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
305 try:
306 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet78399152014-10-17 12:15:46 -0700307 shell_utils.run(' '.join(run_benchmark_cmd), shell=True)
borenetdc89ca52014-10-17 07:37:05 -0700308 except shell_utils.CommandFailedException:
309 # skpicture_printer sometimes fails with AssertionError but the
310 # captured SKP is still valid. This is a known issue.
311 pass
312
borenetdc89ca52014-10-17 07:37:05 -0700313 # Rename generated SKP files into more descriptive names.
314 try:
315 self._RenameSkpFiles(page_set)
316 # Break out of the retry loop since there were no errors.
317 break
318 except Exception:
319 # There was a failure continue with the loop.
320 traceback.print_exc()
321 print '\n\n=======Retrying %s=======\n\n' % page_set
322 time.sleep(10)
323 else:
borenet78399152014-10-17 12:15:46 -0700324 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700325 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700326 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700327
borenetdc89ca52014-10-17 07:37:05 -0700328 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
329 time.time() - start_time)
330
331 if self._skia_tools:
332 render_pictures_cmd = [
333 os.path.join(self._skia_tools, 'render_pictures'),
334 '-r', self._local_skp_dir
335 ]
336 render_pdfs_cmd = [
337 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800338 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700339 ]
340
341 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
342 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
343 proc = subprocess.Popen(tools_cmd)
rmistry0ec28af2014-10-28 14:25:17 -0700344 (code, _) = shell_utils.log_process_after_completion(proc, echo=False)
borenetdc89ca52014-10-17 07:37:05 -0700345 if code != 0:
346 raise Exception('%s failed!' % ' '.join(tools_cmd))
347
348 if not self._non_interactive:
349 print '\n\n=======Running debugger======='
350 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800351 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700352
353 print '\n\n'
354
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700355 if self._upload:
356 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700357 # Copy the directory structure in the root directory into Google Storage.
358 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
359 if self._alternate_upload_dir:
360 dest_dir_name = self._alternate_upload_dir
361
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700362 self.gs.upload_dir_contents(
rmistry009b0692015-03-31 05:20:12 -0700363 LOCAL_PLAYBACK_ROOT_DIR, dest_dir=dest_dir_name,
borenetdc89ca52014-10-17 07:37:05 -0700364 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
365 predefined_acl=GS_PREDEFINED_ACL,
366 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
367
368 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700369 posixpath.join(self.gs.target_name(), dest_dir_name,
370 SKPICTURES_DIR_NAME))
borenetdc89ca52014-10-17 07:37:05 -0700371 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700372 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700373 print 'Generated resources are available in %s\n\n' % (
374 LOCAL_PLAYBACK_ROOT_DIR)
375
376 return 0
377
rmistry49d093c2015-03-31 05:04:29 -0700378 def _GetSkiaSkpFileName(self, page_set):
379 """Returns the SKP file name for Skia page sets."""
380 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
381 ps_filename = os.path.basename(page_set)
382 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
383 ps_basename, _ = os.path.splitext(ps_filename)
384 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
385 _, page_name, device = ps_basename.split('_')
386 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
387 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
388
389 def _GetChromiumSkpFileName(self, page_set, site):
390 """Returns the SKP file name for Chromium page sets."""
391 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
392 _, webpage = os.path.split(site)
393 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700394 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700395 if webpage.startswith(prefix):
396 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700397 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
398 ps_filename = os.path.basename(page_set)
399 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
400 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
401 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
402
borenetdc89ca52014-10-17 07:37:05 -0700403 def _RenameSkpFiles(self, page_set):
404 """Rename generated SKP files into more descriptive names.
405
406 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
407 .skp in there to be this page_set's representative .skp.
408 """
borenetdc89ca52014-10-17 07:37:05 -0700409 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700410 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700411 if self._IsChromiumPageSet(page_set):
412 filename = self._GetChromiumSkpFileName(page_set, site)
413 else:
414 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700415 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700416
rmistryaa31ee72015-04-23 12:47:33 -0700417 if self._skp_prefix:
418 filename = '%s%s' % (self._skp_prefix, filename)
419
borenetdc89ca52014-10-17 07:37:05 -0700420 # We choose the largest .skp as the most likely to be interesting.
421 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
422 key=lambda path: os.stat(path).st_size)
423 dest = os.path.join(self._local_skp_dir, filename)
424 print 'Moving', largest_skp, 'to', dest
425 shutil.move(largest_skp, dest)
426 self._skp_files.append(filename)
427 shutil.rmtree(site)
428
429 def _CreateLocalStorageDirs(self):
430 """Creates required local storage directories for this script."""
431 for d in (self._local_record_webpages_archive_dir,
432 self._local_skp_dir):
433 if os.path.exists(d):
434 shutil.rmtree(d)
435 os.makedirs(d)
436
rmistry7620bf02014-10-27 06:42:11 -0700437 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700438 """Downloads the webpages archive and its required page set from GS."""
439 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
440 wpr_data_file)
441 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
442 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700443 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700444 gs = self.gs
445 if (gs.does_storage_object_exist(wpr_source) and
446 gs.does_storage_object_exist(page_set_source)):
447 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700448 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
449 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700450 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700451 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700452 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700453 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700454 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
455 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700456
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700457class DataStore:
458 """An abstract base class for uploading recordings to a data storage.
459 The interface emulates the google storage api."""
460 def target_name(self):
461 raise NotImplementedError()
462 def target_type(self):
463 raise NotImplementedError()
464 def does_storage_object_exist(self, *args):
465 raise NotImplementedError()
466 def download_file(self, *args):
467 raise NotImplementedError()
468 def upload_dir_contents(self, source_dir, **kwargs):
469 raise NotImplementedError()
470
471class GoogleStorageDataStore(DataStore):
472 def __init__(self, data_store_url):
473 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700474 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700475 gs_utils.GS_PREFIX)
476 self.gs = gs_utils.GSUtils()
477 def target_name(self):
478 return self._data_store_url
479 def target_type(self):
480 return 'Google Storage'
481 def does_storage_object_exist(self, *args):
482 return self.gs.does_storage_object_exist(self._bucket, *args)
483 def download_file(self, *args):
484 self.gs.download_file(self._bucket, *args)
485 def upload_dir_contents(self, source_dir, **kwargs):
486 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
487
488class LocalFileSystemDataStore(DataStore):
489 def __init__(self, data_store_location):
490 self._base_dir = data_store_location
491 def target_name(self):
492 return self._base_dir
493 def target_type(self):
494 return self._base_dir
495 def does_storage_object_exist(self, name, *args):
496 return os.path.isfile(os.path.join(self._base_dir, name))
497 def download_file(self, name, local_path, *args):
498 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
499 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
500 def copytree(source_dir, dest_dir):
501 if not os.path.exists(dest_dir):
502 os.makedirs(dest_dir)
503 for item in os.listdir(source_dir):
504 source = os.path.join(source_dir, item)
505 dest = os.path.join(dest_dir, item)
506 if os.path.isdir(source):
507 copytree(source, dest)
508 else:
509 shutil.copy2(source, dest)
510 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700511
512if '__main__' == __name__:
513 option_parser = optparse.OptionParser()
514 option_parser.add_option(
515 '', '--page_sets',
516 help='Specifies the page sets to use to archive. Supports globs.',
517 default='all')
518 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700519 '', '--record', action='store_true',
520 help='Specifies whether a new website archive should be created.',
521 default=False)
522 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700523 '', '--skia_tools',
524 help=('Path to compiled Skia executable tools. '
525 'render_pictures/render_pdfs is run on the set '
526 'after all SKPs are captured. If the script is run without '
527 '--non-interactive then the debugger is also run at the end. Debug '
528 'builds are recommended because they seem to catch more failures '
529 'than Release builds.'),
530 default=None)
531 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700532 '', '--upload', action='store_true',
533 help=('Uploads to Google Storage or copies to local filesystem storage '
534 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700535 default=False)
536 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700537 '', '--data_store',
538 help=('The location of the file storage to use to download and upload '
539 'files. Can be \'gs://<bucket>\' for Google Storage, or '
540 'a directory for local filesystem storage'),
541 default='gs://chromium-skia-gm')
542 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700543 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700544 help= ('Uploads to a different directory in Google Storage or local '
545 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700546 default=None)
547 option_parser.add_option(
548 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700549 help=('Temporary directory where SKPs and webpage archives will be '
550 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700551 default=tempfile.gettempdir())
552 option_parser.add_option(
553 '', '--browser_executable',
554 help='The exact browser executable to run.',
555 default=None)
556 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700557 '', '--browser_extra_args',
558 help='Additional arguments to pass to the browser.',
559 default=None)
560 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700561 '', '--chrome_src_path',
562 help='Path to the chromium src directory.',
563 default=None)
564 option_parser.add_option(
565 '', '--non-interactive', action='store_true',
566 help='Runs the script without any prompts. If this flag is specified and '
567 '--skia_tools is specified then the debugger is not run.',
568 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700569 option_parser.add_option(
570 '', '--skp_prefix',
571 help='Prefix to add to the names of generated SKPs.',
572 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700573 options, unused_args = option_parser.parse_args()
574
575 playback = SkPicturePlayback(options)
576 sys.exit(playback.Run())