| 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 | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 45 | OPTION_IGNORE_LOCAL_MODS = '--ignore-local-mods' |
| 46 | OPTION_ADD_NEW_FILES = '--add-new-files' |
| 47 | |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 48 | def GetLatestResultsSvnUrl(baseline_subdir): |
| 49 | """Return SVN URL from which we can check out the MOST RECENTLY generated |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 50 | images for this baseline type. |
| 51 | |
| 52 | @param baseline_subdir indicates which platform we want images for |
| 53 | """ |
| borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 54 | return '%s/%s' % (GM_ACTUAL_URL, baseline_subdir) |
| 55 | |
| 56 | def GetBaselineSvnUrl(baseline_subdir): |
| 57 | """Return SVN URL from which we can check out the baseline images for this |
| 58 | baseline type. |
| 59 | |
| 60 | @param baseline_subdir indicates which platform we want baselines for |
| 61 | """ |
| 62 | return '%s/%s' % (GM_BASELINE_URL, baseline_subdir) |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 63 | |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 64 | def CopyMatchingFiles(source_dir, dest_dir, filename_pattern, |
| 65 | only_copy_updates=False): |
| 66 | """Copy all files from source_dir that match filename_pattern, and |
| 67 | save them (with their original filenames) in dest_dir. |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 68 | |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 69 | @param source_dir |
| 70 | @param dest_dir where to save the copied files |
| 71 | @param filename_pattern only copy files that match this Unix-style filename |
| 72 | pattern (e.g., '*.jpg') |
| 73 | @param only_copy_updates if True, only copy files that are already |
| 74 | present in dest_dir |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 75 | """ |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 76 | all_filenames = os.listdir(source_dir) |
| 77 | matching_filenames = fnmatch.filter(all_filenames, filename_pattern) |
| 78 | for filename in matching_filenames: |
| 79 | source_path = os.path.join(source_dir, filename) |
| 80 | dest_path = os.path.join(dest_dir, filename) |
| 81 | if only_copy_updates and not os.path.isfile(dest_path): |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 82 | continue |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 83 | shutil.copyfile(source_path, dest_path) |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 84 | |
| epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 85 | def DownloadBaselinesForOnePlatform(baseline_subdir): |
| 86 | """Download most recently generated baseline images for a single platform, |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 87 | and add any new ones to SVN control. |
| 88 | |
| epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 89 | @param baseline_subdir |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 90 | """ |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 91 | # 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] | 92 | gm_dir = os.path.join(os.pardir, GM_EXPECTED_DIR) # Shouldn't assume we're in trunk... |
| 93 | try: |
| 94 | os.makedirs(gm_dir) |
| 95 | except: |
| 96 | pass |
| 97 | repo_to_modify = svn.Svn(gm_dir) |
| 98 | repo_to_modify.Checkout(GetBaselineSvnUrl(baseline_subdir), baseline_subdir) |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 99 | |
| 100 | # If there are any locally modified files in that directory, exit |
| 101 | # (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] | 102 | new_and_modified_files = repo_to_modify.GetNewAndModifiedFiles() |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 103 | if not options.ignore_local_mods: |
| 104 | if new_and_modified_files: |
| 105 | raise Exception('Exiting because there are already new and/or ' |
| 106 | 'modified files in %s. To continue in spite of ' |
| 107 | 'that, run with %s option.' % ( |
| 108 | baseline_subdir, OPTION_IGNORE_LOCAL_MODS)) |
| 109 | |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 110 | # 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] | 111 | actual_dir = tempfile.mkdtemp() |
| 112 | actual_repo = svn.Svn(actual_dir) |
| 113 | print 'Using %s as a temp dir' % actual_dir |
| 114 | actual_repo.Checkout(GetLatestResultsSvnUrl(baseline_subdir), '.') |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 115 | |
| 116 | # Copy any of those files we are interested in into repo_to_modify, |
| 117 | # and then delete the temporary directory. |
| borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 118 | CopyMatchingFiles(source_dir=actual_dir, |
| 119 | dest_dir=os.path.join(gm_dir, baseline_subdir), |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 120 | filename_pattern='*.png', |
| 121 | only_copy_updates=(not options.add_new_files)) |
| borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 122 | shutil.rmtree(actual_dir) |
| 123 | actual_repo = None |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 124 | |
| 125 | # 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] | 126 | if options.add_new_files: |
| 127 | new_files = repo_to_modify.GetNewFiles() |
| 128 | if new_files: |
| 129 | repo_to_modify.AddFiles(sorted(new_files)) |
| epoger@google.com | 20ad5ac | 2012-01-17 21:26:05 +0000 | [diff] [blame] | 130 | |
| epoger@google.com | 0f645b6 | 2012-05-22 19:14:01 +0000 | [diff] [blame] | 131 | # Set the mimetype property on any new/modified image files in |
| 132 | # baseline_subdir. (We used to set the mimetype property on *all* image |
| 133 | # files in the directory, even those whose content wasn't changing, |
| 134 | # but that caused confusion. See |
| 135 | # http://code.google.com/p/skia/issues/detail?id=618 .) |
| 136 | modified_files = repo_to_modify.GetNewAndModifiedFiles() |
| 137 | repo_to_modify.SetProperty(sorted(fnmatch.filter(modified_files, '*.png')), |
| 138 | svn.PROPERTY_MIMETYPE, 'image/png') |
| 139 | repo_to_modify.SetProperty(sorted(fnmatch.filter(modified_files, '*.pdf')), |
| 140 | svn.PROPERTY_MIMETYPE, 'application/pdf') |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 141 | |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 142 | def RaiseUsageException(): |
| epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 143 | raise Exception('%s\nRun with --help for more detail.' % ( |
| 144 | USAGE_STRING % __file__)) |
| 145 | |
| 146 | def Main(options, args): |
| 147 | """Allow other scripts to call this script with fake command-line args. |
| 148 | """ |
| 149 | # If no platforms are specified, do 'em all. |
| 150 | num_args = len(args) |
| 151 | if num_args == 0: |
| 152 | # TODO(epoger): automate the default set of platforms. We want to ensure |
| 153 | # that the user gets all of the platforms that the bots are running, |
| 154 | # not just whatever subdirectories he happens to have checked out... |
| epoger@google.com | 4a6ba46 | 2012-07-27 15:19:54 +0000 | [diff] [blame] | 155 | # See http://code.google.com/p/skia/issues/detail?id=678 |
| 156 | # |
| 157 | # For now, I generate this list using these Unix commands: |
| borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 158 | # 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] | 159 | # svn ls http://skia-autogen.googlecode.com/svn/gm-actual | grep ^base | sort >/tmp/actual |
| 160 | # comm -1 -2 /tmp/baselines /tmp/actual |
| epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 161 | args = [ |
| borenet@google.com | 3578eb7 | 2012-08-28 16:53:10 +0000 | [diff] [blame] | 162 | 'base-android-galaxy-nexus', |
| 163 | 'base-android-nexus-7', |
| 164 | 'base-android-nexus-s', |
| 165 | 'base-android-xoom', |
| 166 | 'base-macmini', |
| 167 | 'base-macmini-lion-float', |
| 168 | 'base-shuttle-win7-intel-float', |
| 169 | 'base-shuttle_ubuntu12_ati5770', |
| epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 170 | ] |
| 171 | |
| 172 | # Trim all subdir names. |
| 173 | baseline_subdirs = [] |
| 174 | for arg in args: |
| 175 | baseline_subdirs.append(arg.rstrip(os.sep)) |
| 176 | |
| epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 177 | # Process the subdirs, one at a time. |
| 178 | for baseline_subdir in baseline_subdirs: |
| 179 | DownloadBaselinesForOnePlatform(baseline_subdir=baseline_subdir) |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 180 | |
| epoger@google.com | 27442af | 2011-12-29 21:13:08 +0000 | [diff] [blame] | 181 | if __name__ == '__main__': |
| epoger@google.com | 1610a68 | 2012-04-12 18:02:25 +0000 | [diff] [blame] | 182 | parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING) |
| epoger@google.com | d625655 | 2012-01-10 14:10:34 +0000 | [diff] [blame] | 183 | parser.add_option(OPTION_IGNORE_LOCAL_MODS, |
| 184 | action='store_true', default=False, |
| 185 | help='allow tool to run even if there are already ' |
| 186 | 'local modifications in the baseline_subdir') |
| 187 | parser.add_option(OPTION_ADD_NEW_FILES, |
| 188 | action='store_true', default=False, |
| 189 | help='in addition to downloading new versions of ' |
| 190 | 'existing baselines, also download baselines that are ' |
| 191 | 'not under SVN control yet') |
| 192 | (options, args) = parser.parse_args() |
| 193 | Main(options, args) |
| epoger@google.com | 4a6ba46 | 2012-07-27 15:19:54 +0000 | [diff] [blame] | 194 | |