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 | |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 5 | Launch with --help to see more information. |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 6 | |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 7 | |
| 8 | Copyright 2011 Google Inc. |
| 9 | |
| 10 | Use of this source code is governed by a BSD-style license that can be |
| 11 | found in the LICENSE file. |
| 12 | ''' |
| 13 | |
| 14 | # common Python modules |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 15 | import fnmatch |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 16 | import optparse |
| 17 | import os |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 18 | import re |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 19 | import shutil |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 20 | import sys |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 21 | import tempfile |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 22 | |
| 23 | # modules declared within this same directory |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 24 | import compare_baselines |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 25 | import svn |
| 26 | |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 27 | USAGE_STRING = 'Usage: %s [options] [baseline_subdir]...' |
| 28 | HELP_STRING = ''' |
| 29 | |
| 30 | Downloads the actual gm results most recently generated by the Skia buildbots, |
| 31 | and adds any new ones to SVN control. |
| 32 | |
| 33 | If no baseline_subdir is given, then this tool will download the most-recently |
| 34 | generated actual gm results for ALL platforms. |
| 35 | |
| 36 | ''' + compare_baselines.HOWTO_STRING |
| 37 | |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 38 | # Base URL of SVN repository where buildbots store actual gm image results. |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 39 | GM_ACTUAL_URL = 'http://skia-autogen.googlecode.com/svn/gm-actual' |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 40 | |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 41 | # GM baseline image URL in regular Skia SVN repository |
| 42 | GM_BASELINE_URL = 'https://skia.googlecode.com/svn/gm-expected' |
| 43 | |
| 44 | GM_EXPECTED_DIR = 'gm-expected' |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 45 | |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 46 | OPTION_ADD_NEW_FILES = '--add-new-files' |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 47 | OPTION_BUILDER_SUFFIX = '--builder-suffix' |
| 48 | DEFAULT_BUILDER_SUFFIX = '32' |
| 49 | OPTION_IGNORE_LOCAL_MODS = '--ignore-local-mods' |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 50 | |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 51 | def GetLatestResultsSvnUrl(svn, baseline_subdir, builder_suffix): |
| 52 | """Return SVN URL from which we can check out the MOST RECENTLY generated images for this |
| 53 | baseline type. |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 54 | |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 55 | @param svn an Svn object we can use to call ListSubdirs() |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 56 | @param baseline_subdir indicates which platform we want images for |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 57 | @param builder_suffix if multiple builders uploaded actual GM images for this baseline type, |
| 58 | choose the one whose builder_name matches this suffix |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 59 | """ |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 60 | root_url = '%s/%s' % (GM_ACTUAL_URL, baseline_subdir) |
| 61 | subdirs = sorted(svn.ListSubdirs(root_url)) |
| 62 | num_subdirs = len(subdirs) |
| 63 | print('Within actual-results root URL %s, found these %d subdirs (presumably builder_names): %s' |
| 64 | % (root_url, num_subdirs, subdirs)) |
| 65 | |
| 66 | selected_subdir = None |
| 67 | if num_subdirs == 0: |
| 68 | print 'Found no builder_name subdirs, so reading actual images from the root_url itself.' |
| 69 | return root_url |
| 70 | elif num_subdirs == 1: |
| 71 | selected_subdir = subdirs[0] |
| 72 | print 'Found exactly one subdir in actual-results root_url: %s' % selected_subdir |
| 73 | else: |
| 74 | for possible_subdir in subdirs: |
| 75 | if possible_subdir.endswith(builder_suffix): |
| 76 | selected_subdir = possible_subdir |
| 77 | print 'Selected the first subdir ending in "%s": %s' % ( |
| 78 | builder_suffix, selected_subdir) |
| 79 | break |
| 80 | |
| 81 | if selected_subdir: |
| 82 | return '%s/%s/%s' % (root_url, selected_subdir, baseline_subdir) |
| 83 | else: |
| 84 | raise Exception('none of these subdirs of %s ended in "%s": %s' % ( |
| 85 | root_url, builder_suffix, subdirs)) |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 86 | |
| 87 | def GetBaselineSvnUrl(baseline_subdir): |
| 88 | """Return SVN URL from which we can check out the baseline images for this |
| 89 | baseline type. |
| 90 | |
| 91 | @param baseline_subdir indicates which platform we want baselines for |
| 92 | """ |
| 93 | return '%s/%s' % (GM_BASELINE_URL, baseline_subdir) |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 94 | |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 95 | def CopyMatchingFiles(source_dir, dest_dir, filename_pattern, only_copy_updates=False): |
| 96 | """Copy all files from source_dir that match filename_pattern, and save them (with their |
| 97 | original filenames) in dest_dir. |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 98 | |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 99 | @param source_dir |
| 100 | @param dest_dir where to save the copied files |
| 101 | @param filename_pattern only copy files that match this Unix-style filename |
| 102 | pattern (e.g., '*.jpg') |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 103 | @param only_copy_updates if True, only copy files that are already present in dest_dir |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 104 | """ |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 105 | all_filenames = os.listdir(source_dir) |
| 106 | matching_filenames = fnmatch.filter(all_filenames, filename_pattern) |
| 107 | for filename in matching_filenames: |
| 108 | source_path = os.path.join(source_dir, filename) |
| 109 | dest_path = os.path.join(dest_dir, filename) |
| 110 | if only_copy_updates and not os.path.isfile(dest_path): |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 111 | continue |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 112 | shutil.copyfile(source_path, dest_path) |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 113 | |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 114 | def DownloadBaselinesForOnePlatform(baseline_subdir): |
| 115 | """Download most recently generated baseline images for a single platform, |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 116 | and add any new ones to SVN control. |
| 117 | |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 118 | @param baseline_subdir |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 119 | """ |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 120 | # Create repo_to_modify to handle the SVN repository we will add files to. |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 121 | gm_dir = os.path.join(os.pardir, GM_EXPECTED_DIR) # Shouldn't assume we're in trunk... |
| 122 | try: |
| 123 | os.makedirs(gm_dir) |
| 124 | except: |
| 125 | pass |
| 126 | repo_to_modify = svn.Svn(gm_dir) |
| 127 | repo_to_modify.Checkout(GetBaselineSvnUrl(baseline_subdir), baseline_subdir) |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 128 | |
| 129 | # If there are any locally modified files in that directory, exit |
| 130 | # (so that we don't risk overwriting the user's previous work). |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 131 | new_and_modified_files = repo_to_modify.GetNewAndModifiedFiles() |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 132 | if not options.ignore_local_mods: |
| 133 | if new_and_modified_files: |
| 134 | raise Exception('Exiting because there are already new and/or ' |
| 135 | 'modified files in %s. To continue in spite of ' |
| 136 | 'that, run with %s option.' % ( |
| 137 | baseline_subdir, OPTION_IGNORE_LOCAL_MODS)) |
| 138 | |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 139 | # Download actual gm images into a separate repo in a temporary directory. |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 140 | actual_dir = tempfile.mkdtemp() |
| 141 | actual_repo = svn.Svn(actual_dir) |
| 142 | print 'Using %s as a temp dir' % actual_dir |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 143 | actual_url = GetLatestResultsSvnUrl(svn=actual_repo, baseline_subdir=baseline_subdir, |
| 144 | builder_suffix=options.builder_suffix) |
| 145 | print 'Reading actual buildbot GM results from %s' % actual_url |
| 146 | actual_repo.Checkout(actual_url, '.') |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 147 | |
| 148 | # Copy any of those files we are interested in into repo_to_modify, |
| 149 | # and then delete the temporary directory. |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 150 | CopyMatchingFiles(source_dir=actual_dir, |
| 151 | dest_dir=os.path.join(gm_dir, baseline_subdir), |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 152 | filename_pattern='*.png', |
| 153 | only_copy_updates=(not options.add_new_files)) |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 154 | shutil.rmtree(actual_dir) |
| 155 | actual_repo = None |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 156 | |
| 157 | # Add any new files to SVN control (if we are running with add_new_files). |
epoger@google.com | 0f645b6 | 2012-05-22 19:14:01 +0000 | [diff] [blame] | 158 | if options.add_new_files: |
| 159 | new_files = repo_to_modify.GetNewFiles() |
| 160 | if new_files: |
| 161 | repo_to_modify.AddFiles(sorted(new_files)) |
epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 162 | |
epoger@google.com | 0f645b6 | 2012-05-22 19:14:01 +0000 | [diff] [blame] | 163 | # Set the mimetype property on any new/modified image files in |
| 164 | # baseline_subdir. (We used to set the mimetype property on *all* image |
| 165 | # files in the directory, even those whose content wasn't changing, |
| 166 | # but that caused confusion. See |
| 167 | # http://code.google.com/p/skia/issues/detail?id=618 .) |
| 168 | modified_files = repo_to_modify.GetNewAndModifiedFiles() |
| 169 | repo_to_modify.SetProperty(sorted(fnmatch.filter(modified_files, '*.png')), |
| 170 | svn.PROPERTY_MIMETYPE, 'image/png') |
| 171 | repo_to_modify.SetProperty(sorted(fnmatch.filter(modified_files, '*.pdf')), |
| 172 | svn.PROPERTY_MIMETYPE, 'application/pdf') |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 173 | |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 174 | def RaiseUsageException(): |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 175 | raise Exception('%s\nRun with --help for more detail.' % ( |
| 176 | USAGE_STRING % __file__)) |
| 177 | |
| 178 | def Main(options, args): |
| 179 | """Allow other scripts to call this script with fake command-line args. |
| 180 | """ |
| 181 | # If no platforms are specified, do 'em all. |
| 182 | num_args = len(args) |
| 183 | if num_args == 0: |
| 184 | # TODO(epoger): automate the default set of platforms. We want to ensure |
| 185 | # that the user gets all of the platforms that the bots are running, |
| 186 | # not just whatever subdirectories he happens to have checked out... |
epoger@google.com | 4a6ba46 | 2012-07-27 15:19:54 +0000 | [diff] [blame] | 187 | # See http://code.google.com/p/skia/issues/detail?id=678 |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 188 | # Now that we have added Svn.ListSubdirs(), we should be able to do this |
| 189 | # pretty easily... |
epoger@google.com | 4a6ba46 | 2012-07-27 15:19:54 +0000 | [diff] [blame] | 190 | # |
| 191 | # For now, I generate this list using these Unix commands: |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 192 | # svn ls http://skia.googlecode.com/svn/gm-expected | grep ^base | sort >/tmp/baselines |
epoger@google.com | 4a6ba46 | 2012-07-27 15:19:54 +0000 | [diff] [blame] | 193 | # svn ls http://skia-autogen.googlecode.com/svn/gm-actual | grep ^base | sort >/tmp/actual |
| 194 | # comm -1 -2 /tmp/baselines /tmp/actual |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 195 | args = [ |
borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 196 | 'base-android-galaxy-nexus', |
| 197 | 'base-android-nexus-7', |
| 198 | 'base-android-nexus-s', |
| 199 | 'base-android-xoom', |
| 200 | 'base-macmini', |
| 201 | 'base-macmini-lion-float', |
| 202 | 'base-shuttle-win7-intel-float', |
| 203 | 'base-shuttle_ubuntu12_ati5770', |
bungeman@google.com | 7c961e9 | 2012-09-11 17:29:37 +0000 | [diff] [blame] | 204 | 'base-shuttle-win7-intel-angle', |
| 205 | 'base-shuttle-win7-intel-directwrite', |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 206 | ] |
| 207 | |
| 208 | # Trim all subdir names. |
| 209 | baseline_subdirs = [] |
| 210 | for arg in args: |
| 211 | baseline_subdirs.append(arg.rstrip(os.sep)) |
| 212 | |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 213 | # Process the subdirs, one at a time. |
| 214 | for baseline_subdir in baseline_subdirs: |
| 215 | DownloadBaselinesForOnePlatform(baseline_subdir=baseline_subdir) |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 216 | |
epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 217 | if __name__ == '__main__': |
epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 218 | parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING) |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 219 | parser.add_option(OPTION_ADD_NEW_FILES, |
| 220 | action='store_true', default=False, |
| 221 | help='in addition to downloading new versions of ' |
| 222 | 'existing baselines, also download baselines that are ' |
| 223 | 'not under SVN control yet') |
epoger@google.com | f5ad077 | 2012-09-07 16:05:34 +0000 | [diff] [blame] | 224 | parser.add_option(OPTION_BUILDER_SUFFIX, |
| 225 | action='store', type='string', default=DEFAULT_BUILDER_SUFFIX, |
| 226 | help='if multiple builders have uploaded actual GM images ' |
| 227 | 'for this platform, download the images uploaded by the ' |
| 228 | 'builder whose name ends in this suffix; defaults to ' |
| 229 | '"%s".' % DEFAULT_BUILDER_SUFFIX) |
| 230 | parser.add_option(OPTION_IGNORE_LOCAL_MODS, |
| 231 | action='store_true', default=False, |
| 232 | help='allow tool to run even if there are already ' |
| 233 | 'local modifications in the baseline_subdir') |
epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 234 | (options, args) = parser.parse_args() |
| 235 | Main(options, args) |