blob: e8c049c3207a7975570479c9c0326c4dcf031489 [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
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
rmistryf802f322014-10-22 05:04:43 -070079# Location of the credentials.json file and the string that represents missing
80# passwords.
81CREDENTIALS_FILE_PATH = os.path.join(
82 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
83 'credentials.json'
84)
85
borenetdc89ca52014-10-17 07:37:05 -070086# Name of the SKP benchmark
87SKP_BENCHMARK = 'skpicture_printer'
88
89# The max base name length of Skp files.
90MAX_SKP_BASE_NAME_LEN = 31
91
92# Dictionary of device to platform prefixes for SKP files.
93DEVICE_TO_PLATFORM_PREFIX = {
94 'desktop': 'desk',
95 'galaxynexus': 'mobi',
96 'nexus10': 'tabl'
97}
98
99# How many times the record_wpr binary should be retried.
100RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700101# How many times the run_benchmark binary should be retried.
borenetdc89ca52014-10-17 07:37:05 -0700102RETRY_RUN_MEASUREMENT_COUNT = 5
103
rmistryf802f322014-10-22 05:04:43 -0700104# Location of the credentials.json file in Google Storage.
borenet75358672016-10-10 13:20:49 -0700105CREDENTIALS_GS_PATH = 'playback/credentials/credentials.json'
rmistryf802f322014-10-22 05:04:43 -0700106
borenetdc89ca52014-10-17 07:37:05 -0700107X11_DISPLAY = os.getenv('DISPLAY', ':0')
108
rmistry49d093c2015-03-31 05:04:29 -0700109# Path to Chromium's page sets.
110CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
111
112# Dictionary of supported Chromium page sets to their file prefixes.
113CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700114 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700115 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700116}
117
118
kkinnunene75d2d22014-12-03 04:38:46 -0800119def remove_prefix(s, prefix):
120 if s.startswith(prefix):
121 return s[len(prefix):]
122 return s
borenetdc89ca52014-10-17 07:37:05 -0700123
rmistry49d093c2015-03-31 05:04:29 -0700124
borenetdc89ca52014-10-17 07:37:05 -0700125class SkPicturePlayback(object):
126 """Class that archives or replays webpages and creates SKPs."""
127
128 def __init__(self, parse_options):
129 """Constructs a SkPicturePlayback BuildStep instance."""
130 assert parse_options.browser_executable, 'Must specify --browser_executable'
131 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700132 self._browser_args = '--disable-setuid-sandbox'
133 if parse_options.browser_extra_args:
134 self._browser_args = '%s %s' % (
135 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700136
rmistry49d093c2015-03-31 05:04:29 -0700137 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
138 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700139 self._all_page_sets_specified = parse_options.page_sets == 'all'
140 self._page_sets = self._ParsePageSets(parse_options.page_sets)
141
borenetdc89ca52014-10-17 07:37:05 -0700142 self._record = parse_options.record
143 self._skia_tools = parse_options.skia_tools
144 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700145 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700146 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700147 data_store_location = parse_options.data_store
borenet644638c2016-10-10 05:57:18 -0700148 if data_store_location.startswith(GS_PREFIX):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700149 self.gs = GoogleStorageDataStore(data_store_location)
150 else:
151 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800152 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
borenetdc89ca52014-10-17 07:37:05 -0700153 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700154 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
155 'tools', 'perf')
rmistryafaf4962016-02-27 10:04:57 -0800156 self._catapult_dir = os.path.join(parse_options.chrome_src_path,
157 'third_party', 'catapult')
borenetdc89ca52014-10-17 07:37:05 -0700158
159 self._local_skp_dir = os.path.join(
160 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
161 self._local_record_webpages_archive_dir = os.path.join(
162 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
163
164 # List of SKP files generated by this script.
165 self._skp_files = []
166
167 def _ParsePageSets(self, page_sets):
168 if not page_sets:
169 raise ValueError('Must specify at least one page_set!')
170 elif self._all_page_sets_specified:
171 # Get everything from the page_sets directory.
172 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
173 'page_sets')
174 ps = [os.path.join(page_sets_dir, page_set)
175 for page_set in os.listdir(page_sets_dir)
176 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
177 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700178 chromium_ps = [
179 os.path.join(self._chrome_page_sets_path, cr_page_set)
180 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
181 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700182 elif '*' in page_sets:
183 # Explode and return the glob.
184 ps = glob.glob(page_sets)
185 else:
186 ps = page_sets.split(',')
187 ps.sort()
188 return ps
189
rmistry49d093c2015-03-31 05:04:29 -0700190 def _IsChromiumPageSet(self, page_set):
191 """Returns true if the specified page set is a Chromium page set."""
192 return page_set.startswith(self._chrome_page_sets_path)
193
borenetdc89ca52014-10-17 07:37:05 -0700194 def Run(self):
195 """Run the SkPicturePlayback BuildStep."""
196
rmistryf802f322014-10-22 05:04:43 -0700197 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700198 if not os.path.isfile(CREDENTIALS_FILE_PATH):
199 # Download the credentials.json file from Google Storage.
200 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
201
202 if not os.path.isfile(CREDENTIALS_FILE_PATH):
203 print """\n\nCould not locate credentials file in the storage.
204 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700205 {
206 "google": {
207 "username": "google_testing_account_username",
208 "password": "google_testing_account_password"
209 },
210 "facebook": {
211 "username": "facebook_testing_account_username",
212 "password": "facebook_testing_account_password"
213 }
214 }\n\n""" % CREDENTIALS_FILE_PATH
215 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700216
borenetdc89ca52014-10-17 07:37:05 -0700217 # Delete any left over data files in the data directory.
218 for archive_file in glob.glob(
219 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
220 os.remove(archive_file)
221
borenetdc89ca52014-10-17 07:37:05 -0700222 # Create the required local storage directories.
223 self._CreateLocalStorageDirs()
224
225 # Start the timer.
226 start_time = time.time()
227
228 # Loop through all page_sets.
229 for page_set in self._page_sets:
230
rmistry7620bf02014-10-27 06:42:11 -0700231 page_set_basename = os.path.basename(page_set).split('.')[0]
232 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700233 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700234 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700235
rmistry49d093c2015-03-31 05:04:29 -0700236 if self._IsChromiumPageSet(page_set):
237 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
238 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700239 # Create an archive of the specified webpages if '--record=True' is
240 # specified.
241 record_wpr_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800242 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700243 'DISPLAY=%s' % X11_DISPLAY,
244 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700245 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700246 '--browser=exact',
247 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700248 '%s_page_set' % page_set_basename,
249 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700250 )
251 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700252 try:
borenet644638c2016-10-10 05:57:18 -0700253 subprocess.check_call(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700254
255 # Move over the created archive into the local webpages archive
256 # directory.
257 shutil.move(
258 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
259 self._local_record_webpages_archive_dir)
260 shutil.move(
261 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
262 page_set_json_name),
263 self._local_record_webpages_archive_dir)
264
borenetdc89ca52014-10-17 07:37:05 -0700265 # Break out of the retry loop since there were no errors.
266 break
rmistry0ec28af2014-10-28 14:25:17 -0700267 except Exception:
268 # There was a failure continue with the loop.
269 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700270 else:
271 # If we get here then record_wpr did not succeed and thus did not
272 # break out of the loop.
273 raise Exception('record_wpr failed for page_set: %s' % page_set)
274
275 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700276 # Get the webpages archive so that it can be replayed.
277 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700278
borenet78399152014-10-17 12:15:46 -0700279 run_benchmark_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800280 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700281 'DISPLAY=%s' % X11_DISPLAY,
Ravi Mistry57d69652016-09-08 09:08:44 -0400282 'timeout', '1800',
borenet78399152014-10-17 12:15:46 -0700283 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700284 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700285 '--browser=exact',
286 '--browser-executable=%s' % self._browser_executable,
287 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700288 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700289 '--page-set-base-dir=%s' % page_set_dir,
290 '--skp-outdir=%s' % TMP_SKP_DIR,
291 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700292 )
borenetdc89ca52014-10-17 07:37:05 -0700293
294 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
295 try:
296 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet644638c2016-10-10 05:57:18 -0700297 subprocess.check_call(' '.join(run_benchmark_cmd), shell=True)
298 except subprocess.CalledProcessError:
borenetdc89ca52014-10-17 07:37:05 -0700299 # skpicture_printer sometimes fails with AssertionError but the
300 # captured SKP is still valid. This is a known issue.
301 pass
302
borenetdc89ca52014-10-17 07:37:05 -0700303 # Rename generated SKP files into more descriptive names.
304 try:
305 self._RenameSkpFiles(page_set)
306 # Break out of the retry loop since there were no errors.
307 break
308 except Exception:
309 # There was a failure continue with the loop.
310 traceback.print_exc()
311 print '\n\n=======Retrying %s=======\n\n' % page_set
312 time.sleep(10)
313 else:
borenet78399152014-10-17 12:15:46 -0700314 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700315 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700316 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700317
borenetdc89ca52014-10-17 07:37:05 -0700318 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
319 time.time() - start_time)
320
321 if self._skia_tools:
322 render_pictures_cmd = [
323 os.path.join(self._skia_tools, 'render_pictures'),
324 '-r', self._local_skp_dir
325 ]
326 render_pdfs_cmd = [
327 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800328 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700329 ]
330
331 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
332 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
borenet644638c2016-10-10 05:57:18 -0700333 subprocess.check_call(tools_cmd)
borenetdc89ca52014-10-17 07:37:05 -0700334
335 if not self._non_interactive:
336 print '\n\n=======Running debugger======='
337 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800338 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700339
340 print '\n\n'
341
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700342 if self._upload:
343 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700344 # Copy the directory structure in the root directory into Google Storage.
345 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
346 if self._alternate_upload_dir:
347 dest_dir_name = self._alternate_upload_dir
348
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700349 self.gs.upload_dir_contents(
borenet644638c2016-10-10 05:57:18 -0700350 self._local_skp_dir, dest_dir=dest_dir_name)
borenetdc89ca52014-10-17 07:37:05 -0700351
352 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700353 posixpath.join(self.gs.target_name(), dest_dir_name,
354 SKPICTURES_DIR_NAME))
rmistry0575c492016-02-01 10:27:05 -0800355
borenetdc89ca52014-10-17 07:37:05 -0700356 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700357 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700358 print 'Generated resources are available in %s\n\n' % (
borenet1413d522016-08-08 06:26:00 -0700359 self._local_skp_dir)
borenetdc89ca52014-10-17 07:37:05 -0700360
borenet28698472016-07-25 05:18:15 -0700361 if self._upload_to_partner_bucket:
362 print '\n\n=======Uploading to Partner bucket %s =======\n\n' % (
363 PARTNERS_GS_BUCKET)
364 partner_gs = GoogleStorageDataStore(PARTNERS_GS_BUCKET)
365 partner_gs.delete_path(SKPICTURES_DIR_NAME)
borenet1413d522016-08-08 06:26:00 -0700366 print 'Uploading %s to %s' % (self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet644638c2016-10-10 05:57:18 -0700367 partner_gs.upload_dir_contents(self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet28698472016-07-25 05:18:15 -0700368 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
369 posixpath.join(partner_gs.target_name(), SKPICTURES_DIR_NAME))
370
borenetdc89ca52014-10-17 07:37:05 -0700371 return 0
372
rmistry49d093c2015-03-31 05:04:29 -0700373 def _GetSkiaSkpFileName(self, page_set):
374 """Returns the SKP file name for Skia page sets."""
375 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
376 ps_filename = os.path.basename(page_set)
377 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
378 ps_basename, _ = os.path.splitext(ps_filename)
379 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
380 _, page_name, device = ps_basename.split('_')
381 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
382 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
383
384 def _GetChromiumSkpFileName(self, page_set, site):
385 """Returns the SKP file name for Chromium page sets."""
386 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
387 _, webpage = os.path.split(site)
388 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700389 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700390 if webpage.startswith(prefix):
391 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700392 # /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)
rmistry80bd3ae2015-04-03 08:22:51 -0700410 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700411
rmistryaa31ee72015-04-23 12:47:33 -0700412 if self._skp_prefix:
413 filename = '%s%s' % (self._skp_prefix, filename)
414
borenetdc89ca52014-10-17 07:37:05 -0700415 # We choose the largest .skp as the most likely to be interesting.
416 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
417 key=lambda path: os.stat(path).st_size)
418 dest = os.path.join(self._local_skp_dir, filename)
419 print 'Moving', largest_skp, 'to', dest
420 shutil.move(largest_skp, dest)
421 self._skp_files.append(filename)
422 shutil.rmtree(site)
423
424 def _CreateLocalStorageDirs(self):
425 """Creates required local storage directories for this script."""
426 for d in (self._local_record_webpages_archive_dir,
427 self._local_skp_dir):
428 if os.path.exists(d):
429 shutil.rmtree(d)
430 os.makedirs(d)
431
rmistry7620bf02014-10-27 06:42:11 -0700432 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700433 """Downloads the webpages archive and its required page set from GS."""
434 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
435 wpr_data_file)
436 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
437 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700438 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700439 gs = self.gs
440 if (gs.does_storage_object_exist(wpr_source) and
441 gs.does_storage_object_exist(page_set_source)):
442 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700443 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
444 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700445 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700446 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700447 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700448 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700449 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
450 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700451
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700452class DataStore:
453 """An abstract base class for uploading recordings to a data storage.
454 The interface emulates the google storage api."""
455 def target_name(self):
456 raise NotImplementedError()
457 def target_type(self):
458 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700459 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700460 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700461 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700462 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700463 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700464 raise NotImplementedError()
465
borenet644638c2016-10-10 05:57:18 -0700466
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700467class GoogleStorageDataStore(DataStore):
468 def __init__(self, data_store_url):
borenet1cebf7b2016-10-10 08:14:11 -0700469 self._url = data_store_url.rstrip('/')
borenet644638c2016-10-10 05:57:18 -0700470
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700471 def target_name(self):
borenet644638c2016-10-10 05:57:18 -0700472 return self._url
473
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700474 def target_type(self):
475 return 'Google Storage'
borenet644638c2016-10-10 05:57:18 -0700476
477 def does_storage_object_exist(self, name):
478 try:
479 output = subprocess.check_output([
480 'gsutil', 'ls', '/'.join((self._url, name))])
481 except subprocess.CalledProcessError:
482 return False
borenet75358672016-10-10 13:20:49 -0700483 if len(output.splitlines()) != 1:
borenet644638c2016-10-10 05:57:18 -0700484 return False
485 return True
486
rmistryc33c79c2016-02-03 04:27:54 -0800487 def delete_path(self, path):
borenet77874632016-10-17 05:12:10 -0700488 subprocess.check_call(['gsutil', 'rm', '-r', '/'.join((self._url, path))])
borenet644638c2016-10-10 05:57:18 -0700489
490 def download_file(self, name, local_path):
491 subprocess.check_call([
492 'gsutil', 'cp', '/'.join((self._url, name)), local_path])
493
494 def upload_dir_contents(self, source_dir, dest_dir):
495 subprocess.check_call([
borenet27f93e02016-10-17 08:09:33 -0700496 'gsutil', 'cp', '-r', source_dir, '/'.join((self._url, dest_dir))])
borenet644638c2016-10-10 05:57:18 -0700497
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700498
499class LocalFileSystemDataStore(DataStore):
500 def __init__(self, data_store_location):
501 self._base_dir = data_store_location
502 def target_name(self):
503 return self._base_dir
504 def target_type(self):
505 return self._base_dir
borenet644638c2016-10-10 05:57:18 -0700506 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700507 return os.path.isfile(os.path.join(self._base_dir, name))
rmistryc33c79c2016-02-03 04:27:54 -0800508 def delete_path(self, path):
509 shutil.rmtree(path)
borenet644638c2016-10-10 05:57:18 -0700510 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700511 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
borenet644638c2016-10-10 05:57:18 -0700512 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700513 def copytree(source_dir, dest_dir):
514 if not os.path.exists(dest_dir):
515 os.makedirs(dest_dir)
516 for item in os.listdir(source_dir):
517 source = os.path.join(source_dir, item)
518 dest = os.path.join(dest_dir, item)
519 if os.path.isdir(source):
520 copytree(source, dest)
521 else:
522 shutil.copy2(source, dest)
523 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700524
525if '__main__' == __name__:
526 option_parser = optparse.OptionParser()
527 option_parser.add_option(
528 '', '--page_sets',
529 help='Specifies the page sets to use to archive. Supports globs.',
530 default='all')
531 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700532 '', '--record', action='store_true',
533 help='Specifies whether a new website archive should be created.',
534 default=False)
535 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700536 '', '--skia_tools',
537 help=('Path to compiled Skia executable tools. '
538 'render_pictures/render_pdfs is run on the set '
539 'after all SKPs are captured. If the script is run without '
540 '--non-interactive then the debugger is also run at the end. Debug '
541 'builds are recommended because they seem to catch more failures '
542 'than Release builds.'),
543 default=None)
544 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700545 '', '--upload', action='store_true',
546 help=('Uploads to Google Storage or copies to local filesystem storage '
547 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700548 default=False)
549 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800550 '', '--upload_to_partner_bucket', action='store_true',
551 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
552 'bucket if true.'),
553 default=False)
554 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700555 '', '--data_store',
556 help=('The location of the file storage to use to download and upload '
557 'files. Can be \'gs://<bucket>\' for Google Storage, or '
558 'a directory for local filesystem storage'),
boreneta86952a2016-10-05 08:23:04 -0700559 default='gs://skia-skps')
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700560 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700561 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700562 help= ('Uploads to a different directory in Google Storage or local '
563 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700564 default=None)
565 option_parser.add_option(
566 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700567 help=('Temporary directory where SKPs and webpage archives will be '
568 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700569 default=tempfile.gettempdir())
570 option_parser.add_option(
571 '', '--browser_executable',
572 help='The exact browser executable to run.',
573 default=None)
574 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700575 '', '--browser_extra_args',
576 help='Additional arguments to pass to the browser.',
577 default=None)
578 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700579 '', '--chrome_src_path',
580 help='Path to the chromium src directory.',
581 default=None)
582 option_parser.add_option(
583 '', '--non-interactive', action='store_true',
584 help='Runs the script without any prompts. If this flag is specified and '
585 '--skia_tools is specified then the debugger is not run.',
586 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700587 option_parser.add_option(
588 '', '--skp_prefix',
589 help='Prefix to add to the names of generated SKPs.',
590 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700591 options, unused_args = option_parser.parse_args()
592
593 playback = SkPicturePlayback(options)
594 sys.exit(playback.Run())