| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 1 | ''' |
| 2 | Downloads the actual gm results most recently generated by the Skia buildbots, |
| 3 | and adds any new ones to SVN control. |
| 4 | |
| 5 | This tool makes it much easier to check in new baselines, via the following |
| 6 | steps: |
| 7 | |
| 8 | cd .../trunk |
| 9 | svn update |
| 10 | # make sure there are no files awaiting svn commit |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 11 | python tools/download-baselines.py gm/base-macmini-lion-fixed # or other gm/ subdir |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 12 | # upload CL for review |
| 13 | # validate that the new images look right |
| 14 | # commit CL |
| 15 | |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 16 | Launch with --help to see more options. |
| 17 | |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 18 | |
| 19 | Copyright 2011 Google Inc. |
| 20 | |
| 21 | Use of this source code is governed by a BSD-style license that can be |
| 22 | found in the LICENSE file. |
| 23 | ''' |
| 24 | |
| 25 | # common Python modules |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 26 | import optparse |
| 27 | import os |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 28 | import re |
| 29 | import sys |
| 30 | import urllib2 |
| 31 | |
| 32 | # modules declared within this same directory |
| 33 | import 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. |
| 42 | ACTUALS_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.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 53 | USAGE_STRING = 'usage: %s [options] <baseline_subdir>' |
| 54 | OPTION_IGNORE_LOCAL_MODS = '--ignore-local-mods' |
| 55 | OPTION_ADD_NEW_FILES = '--add-new-files' |
| 56 | |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 57 | IMAGE_REGEX = '.+\.png' |
| 58 | IMAGE_MIMETYPE = 'image/png' |
| 59 | |
| 60 | def 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 | |
| 75 | def 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.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 94 | def DownloadMatchingFiles(source_url, filename_regex, dest_dir, |
| 95 | only_download_updates=False): |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 96 | """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.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 100 | @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.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 104 | """ |
| 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.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 112 | 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.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 116 | |
| 117 | def 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.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 130 | def Main(options, args): |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 131 | """Download most recently generated baseline images for a given platform, |
| 132 | and add any new ones to SVN control. |
| 133 | |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 134 | @param options |
| 135 | @param args |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 136 | """ |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 137 | num_args = len(args) |
| 138 | if num_args != 1: |
| 139 | RaiseUsageException() |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 140 | |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 141 | baseline_subdir = args[0] |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 142 | while baseline_subdir.endswith('/'): |
| 143 | baseline_subdir = baseline_subdir[:-1] |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 144 | svn_handler = svn.Svn(baseline_subdir) |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 145 | |
| 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.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 163 | new_files = svn_handler.GetNewFiles() |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 164 | if new_files and options.add_new_files: |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 165 | svn_handler.AddFiles(new_files) |
| 166 | svn_handler.SetProperty(new_files, svn.PROPERTY_MIMETYPE, |
| 167 | IMAGE_MIMETYPE) |
| 168 | |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 169 | def RaiseUsageException(): |
| 170 | raise Exception(USAGE_STRING % __file__) |
| 171 | |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 172 | if __name__ == '__main__': |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 173 | 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) |