blob: 67c84ae38ba672e04b67c1f921215808d5ddda19 [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',
rmistry49d093c2015-03-31 05:04:29 -0700124 # TODO(rmistry): Uncomment the below after it is verified that the above
125 # works.
126 # 'top_25_smooth.py': 'top25desk',
127}
128
129
kkinnunene75d2d22014-12-03 04:38:46 -0800130def remove_prefix(s, prefix):
131 if s.startswith(prefix):
132 return s[len(prefix):]
133 return s
borenetdc89ca52014-10-17 07:37:05 -0700134
rmistry49d093c2015-03-31 05:04:29 -0700135
borenetdc89ca52014-10-17 07:37:05 -0700136class SkPicturePlayback(object):
137 """Class that archives or replays webpages and creates SKPs."""
138
139 def __init__(self, parse_options):
140 """Constructs a SkPicturePlayback BuildStep instance."""
141 assert parse_options.browser_executable, 'Must specify --browser_executable'
142 self._browser_executable = parse_options.browser_executable
143
rmistry49d093c2015-03-31 05:04:29 -0700144 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
145 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700146 self._all_page_sets_specified = parse_options.page_sets == 'all'
147 self._page_sets = self._ParsePageSets(parse_options.page_sets)
148
borenetdc89ca52014-10-17 07:37:05 -0700149 self._record = parse_options.record
150 self._skia_tools = parse_options.skia_tools
151 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700152 self._upload = parse_options.upload
153 data_store_location = parse_options.data_store
154 if data_store_location.startswith(gs_utils.GS_PREFIX):
155 self.gs = GoogleStorageDataStore(data_store_location)
156 else:
157 self.gs = LocalFileSystemDataStore(data_store_location)
borenetdc89ca52014-10-17 07:37:05 -0700158 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700159 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
160 'tools', 'perf')
161
162 self._local_skp_dir = os.path.join(
163 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
164 self._local_record_webpages_archive_dir = os.path.join(
165 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
166
167 # List of SKP files generated by this script.
168 self._skp_files = []
169
170 def _ParsePageSets(self, page_sets):
171 if not page_sets:
172 raise ValueError('Must specify at least one page_set!')
173 elif self._all_page_sets_specified:
174 # Get everything from the page_sets directory.
175 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
176 'page_sets')
177 ps = [os.path.join(page_sets_dir, page_set)
178 for page_set in os.listdir(page_sets_dir)
179 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
180 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700181 chromium_ps = [
182 os.path.join(self._chrome_page_sets_path, cr_page_set)
183 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
184 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700185 elif '*' in page_sets:
186 # Explode and return the glob.
187 ps = glob.glob(page_sets)
188 else:
189 ps = page_sets.split(',')
190 ps.sort()
191 return ps
192
rmistry49d093c2015-03-31 05:04:29 -0700193 def _IsChromiumPageSet(self, page_set):
194 """Returns true if the specified page set is a Chromium page set."""
195 return page_set.startswith(self._chrome_page_sets_path)
196
borenetdc89ca52014-10-17 07:37:05 -0700197 def Run(self):
198 """Run the SkPicturePlayback BuildStep."""
199
rmistryf802f322014-10-22 05:04:43 -0700200 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700201 if not os.path.isfile(CREDENTIALS_FILE_PATH):
202 # Download the credentials.json file from Google Storage.
203 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
204
205 if not os.path.isfile(CREDENTIALS_FILE_PATH):
206 print """\n\nCould not locate credentials file in the storage.
207 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700208 {
209 "google": {
210 "username": "google_testing_account_username",
211 "password": "google_testing_account_password"
212 },
213 "facebook": {
214 "username": "facebook_testing_account_username",
215 "password": "facebook_testing_account_password"
216 }
217 }\n\n""" % CREDENTIALS_FILE_PATH
218 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700219
borenetdc89ca52014-10-17 07:37:05 -0700220 # Delete any left over data files in the data directory.
221 for archive_file in glob.glob(
222 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
223 os.remove(archive_file)
224
225 # Delete the local root directory if it already exists.
226 if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR):
227 shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR)
228
229 # Create the required local storage directories.
230 self._CreateLocalStorageDirs()
231
232 # Start the timer.
233 start_time = time.time()
234
235 # Loop through all page_sets.
236 for page_set in self._page_sets:
237
rmistry7620bf02014-10-27 06:42:11 -0700238 page_set_basename = os.path.basename(page_set).split('.')[0]
239 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700240 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700241 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700242
rmistry49d093c2015-03-31 05:04:29 -0700243 if self._IsChromiumPageSet(page_set):
244 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
245 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700246 # Create an archive of the specified webpages if '--record=True' is
247 # specified.
248 record_wpr_cmd = (
rmistry7620bf02014-10-27 06:42:11 -0700249 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700250 'DISPLAY=%s' % X11_DISPLAY,
251 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
252 '--extra-browser-args=--disable-setuid-sandbox',
253 '--browser=exact',
254 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700255 '%s_page_set' % page_set_basename,
256 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700257 )
258 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700259 try:
260 shell_utils.run(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700261
262 # Move over the created archive into the local webpages archive
263 # directory.
264 shutil.move(
265 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
266 self._local_record_webpages_archive_dir)
267 shutil.move(
268 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
269 page_set_json_name),
270 self._local_record_webpages_archive_dir)
271
borenetdc89ca52014-10-17 07:37:05 -0700272 # Break out of the retry loop since there were no errors.
273 break
rmistry0ec28af2014-10-28 14:25:17 -0700274 except Exception:
275 # There was a failure continue with the loop.
276 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700277 else:
278 # If we get here then record_wpr did not succeed and thus did not
279 # break out of the loop.
280 raise Exception('record_wpr failed for page_set: %s' % page_set)
281
282 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700283 # Get the webpages archive so that it can be replayed.
284 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700285
borenet78399152014-10-17 12:15:46 -0700286 run_benchmark_cmd = (
rmistryf802f322014-10-22 05:04:43 -0700287 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700288 'DISPLAY=%s' % X11_DISPLAY,
289 'timeout', '300',
borenet78399152014-10-17 12:15:46 -0700290 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
borenetdc89ca52014-10-17 07:37:05 -0700291 '--extra-browser-args=--disable-setuid-sandbox',
292 '--browser=exact',
293 '--browser-executable=%s' % self._browser_executable,
294 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700295 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700296 '--page-set-base-dir=%s' % page_set_dir,
297 '--skp-outdir=%s' % TMP_SKP_DIR,
298 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700299 )
borenetdc89ca52014-10-17 07:37:05 -0700300
301 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
302 try:
303 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet78399152014-10-17 12:15:46 -0700304 shell_utils.run(' '.join(run_benchmark_cmd), shell=True)
borenetdc89ca52014-10-17 07:37:05 -0700305 except shell_utils.CommandFailedException:
306 # skpicture_printer sometimes fails with AssertionError but the
307 # captured SKP is still valid. This is a known issue.
308 pass
309
borenetdc89ca52014-10-17 07:37:05 -0700310 # Rename generated SKP files into more descriptive names.
311 try:
312 self._RenameSkpFiles(page_set)
313 # Break out of the retry loop since there were no errors.
314 break
315 except Exception:
316 # There was a failure continue with the loop.
317 traceback.print_exc()
318 print '\n\n=======Retrying %s=======\n\n' % page_set
319 time.sleep(10)
320 else:
borenet78399152014-10-17 12:15:46 -0700321 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700322 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700323 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700324
borenetdc89ca52014-10-17 07:37:05 -0700325 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
326 time.time() - start_time)
327
328 if self._skia_tools:
329 render_pictures_cmd = [
330 os.path.join(self._skia_tools, 'render_pictures'),
331 '-r', self._local_skp_dir
332 ]
333 render_pdfs_cmd = [
334 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800335 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700336 ]
337
338 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
339 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
340 proc = subprocess.Popen(tools_cmd)
rmistry0ec28af2014-10-28 14:25:17 -0700341 (code, _) = shell_utils.log_process_after_completion(proc, echo=False)
borenetdc89ca52014-10-17 07:37:05 -0700342 if code != 0:
343 raise Exception('%s failed!' % ' '.join(tools_cmd))
344
345 if not self._non_interactive:
346 print '\n\n=======Running debugger======='
347 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800348 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700349
350 print '\n\n'
351
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700352 if self._upload:
353 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700354 # Copy the directory structure in the root directory into Google Storage.
355 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
356 if self._alternate_upload_dir:
357 dest_dir_name = self._alternate_upload_dir
358
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700359 self.gs.upload_dir_contents(
rmistry009b0692015-03-31 05:20:12 -0700360 LOCAL_PLAYBACK_ROOT_DIR, dest_dir=dest_dir_name,
borenetdc89ca52014-10-17 07:37:05 -0700361 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
362 predefined_acl=GS_PREDEFINED_ACL,
363 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
364
365 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700366 posixpath.join(self.gs.target_name(), dest_dir_name,
367 SKPICTURES_DIR_NAME))
borenetdc89ca52014-10-17 07:37:05 -0700368 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700369 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700370 print 'Generated resources are available in %s\n\n' % (
371 LOCAL_PLAYBACK_ROOT_DIR)
372
373 return 0
374
rmistry49d093c2015-03-31 05:04:29 -0700375 def _GetSkiaSkpFileName(self, page_set):
376 """Returns the SKP file name for Skia page sets."""
377 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
378 ps_filename = os.path.basename(page_set)
379 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
380 ps_basename, _ = os.path.splitext(ps_filename)
381 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
382 _, page_name, device = ps_basename.split('_')
383 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
384 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
385
386 def _GetChromiumSkpFileName(self, page_set, site):
387 """Returns the SKP file name for Chromium page sets."""
388 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
389 _, webpage = os.path.split(site)
390 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
391 webpage = webpage.lstrip('http___').lstrip('https___')
392 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
393 ps_filename = os.path.basename(page_set)
394 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
395 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
396 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
397
borenetdc89ca52014-10-17 07:37:05 -0700398 def _RenameSkpFiles(self, page_set):
399 """Rename generated SKP files into more descriptive names.
400
401 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
402 .skp in there to be this page_set's representative .skp.
403 """
borenetdc89ca52014-10-17 07:37:05 -0700404 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700405 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700406 if self._IsChromiumPageSet(page_set):
407 filename = self._GetChromiumSkpFileName(page_set, site)
408 else:
409 filename = self._GetSkiaSkpFileName(page_set)
410
borenetdc89ca52014-10-17 07:37:05 -0700411 # We choose the largest .skp as the most likely to be interesting.
412 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
413 key=lambda path: os.stat(path).st_size)
414 dest = os.path.join(self._local_skp_dir, filename)
415 print 'Moving', largest_skp, 'to', dest
416 shutil.move(largest_skp, dest)
417 self._skp_files.append(filename)
418 shutil.rmtree(site)
419
420 def _CreateLocalStorageDirs(self):
421 """Creates required local storage directories for this script."""
422 for d in (self._local_record_webpages_archive_dir,
423 self._local_skp_dir):
424 if os.path.exists(d):
425 shutil.rmtree(d)
426 os.makedirs(d)
427
rmistry7620bf02014-10-27 06:42:11 -0700428 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700429 """Downloads the webpages archive and its required page set from GS."""
430 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
431 wpr_data_file)
432 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
433 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700434 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700435 gs = self.gs
436 if (gs.does_storage_object_exist(wpr_source) and
437 gs.does_storage_object_exist(page_set_source)):
438 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700439 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
440 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700441 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700442 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700443 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700444 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700445 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
446 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700447
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700448class DataStore:
449 """An abstract base class for uploading recordings to a data storage.
450 The interface emulates the google storage api."""
451 def target_name(self):
452 raise NotImplementedError()
453 def target_type(self):
454 raise NotImplementedError()
455 def does_storage_object_exist(self, *args):
456 raise NotImplementedError()
457 def download_file(self, *args):
458 raise NotImplementedError()
459 def upload_dir_contents(self, source_dir, **kwargs):
460 raise NotImplementedError()
461
462class GoogleStorageDataStore(DataStore):
463 def __init__(self, data_store_url):
464 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700465 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700466 gs_utils.GS_PREFIX)
467 self.gs = gs_utils.GSUtils()
468 def target_name(self):
469 return self._data_store_url
470 def target_type(self):
471 return 'Google Storage'
472 def does_storage_object_exist(self, *args):
473 return self.gs.does_storage_object_exist(self._bucket, *args)
474 def download_file(self, *args):
475 self.gs.download_file(self._bucket, *args)
476 def upload_dir_contents(self, source_dir, **kwargs):
477 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
478
479class LocalFileSystemDataStore(DataStore):
480 def __init__(self, data_store_location):
481 self._base_dir = data_store_location
482 def target_name(self):
483 return self._base_dir
484 def target_type(self):
485 return self._base_dir
486 def does_storage_object_exist(self, name, *args):
487 return os.path.isfile(os.path.join(self._base_dir, name))
488 def download_file(self, name, local_path, *args):
489 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
490 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
491 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(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700528 '', '--data_store',
529 help=('The location of the file storage to use to download and upload '
530 'files. Can be \'gs://<bucket>\' for Google Storage, or '
531 'a directory for local filesystem storage'),
532 default='gs://chromium-skia-gm')
533 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700534 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700535 help= ('Uploads to a different directory in Google Storage or local '
536 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700537 default=None)
538 option_parser.add_option(
539 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700540 help=('Temporary directory where SKPs and webpage archives will be '
541 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700542 default=tempfile.gettempdir())
543 option_parser.add_option(
544 '', '--browser_executable',
545 help='The exact browser executable to run.',
546 default=None)
547 option_parser.add_option(
548 '', '--chrome_src_path',
549 help='Path to the chromium src directory.',
550 default=None)
551 option_parser.add_option(
552 '', '--non-interactive', action='store_true',
553 help='Runs the script without any prompts. If this flag is specified and '
554 '--skia_tools is specified then the debugger is not run.',
555 default=False)
556 options, unused_args = option_parser.parse_args()
557
558 playback = SkPicturePlayback(options)
559 sys.exit(playback.Run())