blob: d41b905d70e1c4707fe5f55107fffc9093ce395e [file] [log] [blame]
epoger@google.com27442af2011-12-29 21:13:08 +00001'''
2Downloads the actual gm results most recently generated by the Skia buildbots,
3and adds any new ones to SVN control.
4
5This tool makes it much easier to check in new baselines, via the following
6steps:
7
8cd .../trunk
9svn update
10# make sure there are no files awaiting svn commit
epoger@google.comd6256552012-01-10 14:10:34 +000011python tools/download-baselines.py gm/base-macmini-lion-fixed # or other gm/ subdir
epoger@google.com27442af2011-12-29 21:13:08 +000012# upload CL for review
13# validate that the new images look right
14# commit CL
15
epoger@google.comd6256552012-01-10 14:10:34 +000016Launch with --help to see more options.
17
epoger@google.com27442af2011-12-29 21:13:08 +000018
19Copyright 2011 Google Inc.
20
21Use of this source code is governed by a BSD-style license that can be
22found in the LICENSE file.
23'''
24
25# common Python modules
epoger@google.comd6256552012-01-10 14:10:34 +000026import optparse
27import os
epoger@google.com27442af2011-12-29 21:13:08 +000028import re
29import sys
30import urllib2
31
32# modules declared within this same directory
33import svn
34
35# Where to download recently generated baseline images for each baseline type.
36#
37# For now this only works for our Mac buildbots; our other buildbots aren't
38# uploading their results to a web server yet.
39#
40# Note also that these will currently work only within the Google corporate
41# network; that will also change soon.
42ACTUALS_BY_BASELINE_SUBDIR = {
43 'gm/base-macmini':
44 'http://172.29.92.185/b/build/slave/Skia_Mac_Float_NoDebug/gm/actual',
45 'gm/base-macmini-fixed':
46 'http://172.29.92.185/b/build/slave/Skia_Mac_Fixed_NoDebug/gm/actual',
47 'gm/base-macmini-lion-fixed':
48 'http://172.29.92.179/b/build/slave/Skia_MacMiniLion_Fixed_NoDebug/gm/actual',
49 'gm/base-macmini-lion-float':
50 'http://172.29.92.179/b/build/slave/Skia_MacMiniLion_Float_NoDebug/gm/actual',
51}
52
epoger@google.comd6256552012-01-10 14:10:34 +000053USAGE_STRING = 'usage: %s [options] <baseline_subdir>'
54OPTION_IGNORE_LOCAL_MODS = '--ignore-local-mods'
55OPTION_ADD_NEW_FILES = '--add-new-files'
56
epoger@google.com27442af2011-12-29 21:13:08 +000057IMAGE_REGEX = '.+\.png'
58IMAGE_MIMETYPE = 'image/png'
59
60def GetPlatformUrl(baseline_subdir):
61 """Return URL within which the buildbots store generated baseline images,
62 as of multiple svn revisions.
63
64 Raises KeyError if we don't have a URL matching this baseline_subdir.
65
66 @param baseline_subdir indicates which platform we want images for
67 """
68 try:
69 return ACTUALS_BY_BASELINE_SUBDIR[baseline_subdir]
70 except KeyError:
71 raise KeyError(
72 'unknown baseline_subdir "%s", try one of these instead: %s' % (
73 baseline_subdir, ACTUALS_BY_BASELINE_SUBDIR.keys()))
74
75def GetLatestResultsUrl(baseline_subdir):
76 """Return URL from which we can download the MOST RECENTLY generated
77 images for this baseline type.
78
79 @param baseline_subdir indicates which platform we want images for
80 """
81 base_platform_url = GetPlatformUrl(baseline_subdir)
82 print 'base_platform_url is %s' % base_platform_url
83
84 # Find the most recently generated baseline images within base_platform_url
85 response = urllib2.urlopen(base_platform_url)
86 html = response.read()
87 link_regex = re.compile('<a href="(.*)">')
88 links = link_regex.findall(html)
89 last_link = links[-1]
90 most_recent_result_url = '%s/%s' % (base_platform_url, last_link)
91 print 'most_recent_result_url is %s' % most_recent_result_url
92 return most_recent_result_url
93
epoger@google.comd6256552012-01-10 14:10:34 +000094def DownloadMatchingFiles(source_url, filename_regex, dest_dir,
95 only_download_updates=False):
epoger@google.com27442af2011-12-29 21:13:08 +000096 """Download all files from source_url that match filename_regex, and save
97 them (with their original filenames) in dest_dir.
98
99 @param source_url
epoger@google.comd6256552012-01-10 14:10:34 +0000100 @param filename_regex only download files that match this regex
101 @param dest_dir where to save the downloaded files
102 @param only_download_updates if True, only download files that are already
103 present in dest_dir (download updated versions of those files)
epoger@google.com27442af2011-12-29 21:13:08 +0000104 """
105 while source_url.endswith('/'):
106 source_url = source_url[:-1]
107 response = urllib2.urlopen(source_url)
108 html = response.read()
109 link_regex = re.compile('<a href="(%s)">' % filename_regex)
110 links = link_regex.findall(html)
111 for link in links:
epoger@google.comd6256552012-01-10 14:10:34 +0000112 dest_path = os.path.join(dest_dir, link)
113 if only_download_updates and not os.path.isfile(dest_path):
114 continue
115 DownloadBinaryFile('%s/%s' % (source_url, link), dest_path)
epoger@google.com27442af2011-12-29 21:13:08 +0000116
117def DownloadBinaryFile(source_url, dest_path):
118 """Download a single file from its source_url and save it to local disk
119 at dest_path.
120
121 @param source_url
122 @param dest_path
123 """
124 print 'DownloadBinaryFile: %s -> %s' % (source_url, dest_path)
125 url_fh = urllib2.urlopen(source_url)
126 local_fh = open(dest_path, 'wb')
127 local_fh.write(url_fh.read())
128 local_fh.close()
129
epoger@google.comd6256552012-01-10 14:10:34 +0000130def Main(options, args):
epoger@google.com27442af2011-12-29 21:13:08 +0000131 """Download most recently generated baseline images for a given platform,
132 and add any new ones to SVN control.
133
epoger@google.comd6256552012-01-10 14:10:34 +0000134 @param options
135 @param args
epoger@google.com27442af2011-12-29 21:13:08 +0000136 """
epoger@google.comd6256552012-01-10 14:10:34 +0000137 num_args = len(args)
138 if num_args != 1:
139 RaiseUsageException()
epoger@google.com27442af2011-12-29 21:13:08 +0000140
epoger@google.comd6256552012-01-10 14:10:34 +0000141 baseline_subdir = args[0]
epoger@google.com27442af2011-12-29 21:13:08 +0000142 while baseline_subdir.endswith('/'):
143 baseline_subdir = baseline_subdir[:-1]
epoger@google.com27442af2011-12-29 21:13:08 +0000144 svn_handler = svn.Svn(baseline_subdir)
epoger@google.comd6256552012-01-10 14:10:34 +0000145
146 # If there are any locally modified files in that directory, exit
147 # (so that we don't risk overwriting the user's previous work).
148 new_and_modified_files = svn_handler.GetNewAndModifiedFiles()
149 if not options.ignore_local_mods:
150 if new_and_modified_files:
151 raise Exception('Exiting because there are already new and/or '
152 'modified files in %s. To continue in spite of '
153 'that, run with %s option.' % (
154 baseline_subdir, OPTION_IGNORE_LOCAL_MODS))
155
156 # Download the actual results from the appropriate buildbot.
157 results_url = GetLatestResultsUrl(baseline_subdir)
158 DownloadMatchingFiles(source_url=results_url, filename_regex=IMAGE_REGEX,
159 dest_dir=baseline_subdir,
160 only_download_updates=(not options.add_new_files))
161
162 # Add any new files to SVN control (if we are running with add_new_files).
epoger@google.com27442af2011-12-29 21:13:08 +0000163 new_files = svn_handler.GetNewFiles()
epoger@google.comd6256552012-01-10 14:10:34 +0000164 if new_files and options.add_new_files:
epoger@google.com27442af2011-12-29 21:13:08 +0000165 svn_handler.AddFiles(new_files)
166 svn_handler.SetProperty(new_files, svn.PROPERTY_MIMETYPE,
167 IMAGE_MIMETYPE)
168
epoger@google.comd6256552012-01-10 14:10:34 +0000169def RaiseUsageException():
170 raise Exception(USAGE_STRING % __file__)
171
epoger@google.com27442af2011-12-29 21:13:08 +0000172if __name__ == '__main__':
epoger@google.comd6256552012-01-10 14:10:34 +0000173 parser = optparse.OptionParser(USAGE_STRING % '%prog')
174 parser.add_option(OPTION_IGNORE_LOCAL_MODS,
175 action='store_true', default=False,
176 help='allow tool to run even if there are already '
177 'local modifications in the baseline_subdir')
178 parser.add_option(OPTION_ADD_NEW_FILES,
179 action='store_true', default=False,
180 help='in addition to downloading new versions of '
181 'existing baselines, also download baselines that are '
182 'not under SVN control yet')
183 (options, args) = parser.parse_args()
184 Main(options, args)