blob: 3615b5d3c5f6a555fc66362e74e4c045b2cf18a5 [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
epoger@google.com1610a682012-04-12 18:02:25 +00005Launch with --help to see more information.
epoger@google.comd6256552012-01-10 14:10:34 +00006
epoger@google.com27442af2011-12-29 21:13:08 +00007
8Copyright 2011 Google Inc.
9
10Use of this source code is governed by a BSD-style license that can be
11found in the LICENSE file.
12'''
13
14# common Python modules
epoger@google.com20ad5ac2012-01-17 21:26:05 +000015import fnmatch
epoger@google.comd6256552012-01-10 14:10:34 +000016import optparse
17import os
epoger@google.com27442af2011-12-29 21:13:08 +000018import re
epoger@google.com20ad5ac2012-01-17 21:26:05 +000019import shutil
epoger@google.com27442af2011-12-29 21:13:08 +000020import sys
epoger@google.com20ad5ac2012-01-17 21:26:05 +000021import tempfile
epoger@google.com27442af2011-12-29 21:13:08 +000022
23# modules declared within this same directory
epoger@google.com1610a682012-04-12 18:02:25 +000024import compare_baselines
epoger@google.com27442af2011-12-29 21:13:08 +000025import svn
26
epoger@google.com1610a682012-04-12 18:02:25 +000027USAGE_STRING = 'Usage: %s [options] [baseline_subdir]...'
28HELP_STRING = '''
29
30Downloads the actual gm results most recently generated by the Skia buildbots,
31and adds any new ones to SVN control.
32
33If no baseline_subdir is given, then this tool will download the most-recently
34generated actual gm results for ALL platforms.
35
36''' + compare_baselines.HOWTO_STRING
37
epoger@google.com20ad5ac2012-01-17 21:26:05 +000038# Base URL of SVN repository where buildbots store actual gm image results.
borenet@google.com3578eb72012-08-28 16:53:10 +000039GM_ACTUAL_URL = 'http://skia-autogen.googlecode.com/svn/gm-actual'
epoger@google.com27442af2011-12-29 21:13:08 +000040
borenet@google.com3578eb72012-08-28 16:53:10 +000041# GM baseline image URL in regular Skia SVN repository
42GM_BASELINE_URL = 'https://skia.googlecode.com/svn/gm-expected'
43
44GM_EXPECTED_DIR = 'gm-expected'
epoger@google.comf5ad0772012-09-07 16:05:34 +000045
epoger@google.comd6256552012-01-10 14:10:34 +000046OPTION_ADD_NEW_FILES = '--add-new-files'
epoger@google.comf5ad0772012-09-07 16:05:34 +000047OPTION_BUILDER_SUFFIX = '--builder-suffix'
48DEFAULT_BUILDER_SUFFIX = '32'
49OPTION_IGNORE_LOCAL_MODS = '--ignore-local-mods'
epoger@google.comd6256552012-01-10 14:10:34 +000050
epoger@google.comf5ad0772012-09-07 16:05:34 +000051def 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.com27442af2011-12-29 21:13:08 +000054
epoger@google.comf5ad0772012-09-07 16:05:34 +000055 @param svn an Svn object we can use to call ListSubdirs()
epoger@google.com27442af2011-12-29 21:13:08 +000056 @param baseline_subdir indicates which platform we want images for
epoger@google.comf5ad0772012-09-07 16:05:34 +000057 @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.com27442af2011-12-29 21:13:08 +000059 """
epoger@google.comf5ad0772012-09-07 16:05:34 +000060 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.com3578eb72012-08-28 16:53:10 +000086
87def 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.com27442af2011-12-29 21:13:08 +000094
epoger@google.comf5ad0772012-09-07 16:05:34 +000095def 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.com27442af2011-12-29 21:13:08 +000098
epoger@google.com20ad5ac2012-01-17 21:26:05 +000099 @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.comf5ad0772012-09-07 16:05:34 +0000103 @param only_copy_updates if True, only copy files that are already present in dest_dir
epoger@google.com27442af2011-12-29 21:13:08 +0000104 """
epoger@google.com20ad5ac2012-01-17 21:26:05 +0000105 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.comd6256552012-01-10 14:10:34 +0000111 continue
epoger@google.com20ad5ac2012-01-17 21:26:05 +0000112 shutil.copyfile(source_path, dest_path)
epoger@google.com27442af2011-12-29 21:13:08 +0000113
epoger@google.com1610a682012-04-12 18:02:25 +0000114def DownloadBaselinesForOnePlatform(baseline_subdir):
115 """Download most recently generated baseline images for a single platform,
epoger@google.com27442af2011-12-29 21:13:08 +0000116 and add any new ones to SVN control.
117
epoger@google.com1610a682012-04-12 18:02:25 +0000118 @param baseline_subdir
epoger@google.com27442af2011-12-29 21:13:08 +0000119 """
epoger@google.com20ad5ac2012-01-17 21:26:05 +0000120 # Create repo_to_modify to handle the SVN repository we will add files to.
borenet@google.com3578eb72012-08-28 16:53:10 +0000121 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.comd6256552012-01-10 14:10:34 +0000128
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.com20ad5ac2012-01-17 21:26:05 +0000131 new_and_modified_files = repo_to_modify.GetNewAndModifiedFiles()
epoger@google.comd6256552012-01-10 14:10:34 +0000132 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.com20ad5ac2012-01-17 21:26:05 +0000139 # Download actual gm images into a separate repo in a temporary directory.
borenet@google.com3578eb72012-08-28 16:53:10 +0000140 actual_dir = tempfile.mkdtemp()
141 actual_repo = svn.Svn(actual_dir)
142 print 'Using %s as a temp dir' % actual_dir
epoger@google.comf5ad0772012-09-07 16:05:34 +0000143 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.com20ad5ac2012-01-17 21:26:05 +0000147
148 # Copy any of those files we are interested in into repo_to_modify,
149 # and then delete the temporary directory.
borenet@google.com3578eb72012-08-28 16:53:10 +0000150 CopyMatchingFiles(source_dir=actual_dir,
151 dest_dir=os.path.join(gm_dir, baseline_subdir),
epoger@google.com20ad5ac2012-01-17 21:26:05 +0000152 filename_pattern='*.png',
153 only_copy_updates=(not options.add_new_files))
borenet@google.com3578eb72012-08-28 16:53:10 +0000154 shutil.rmtree(actual_dir)
155 actual_repo = None
epoger@google.comd6256552012-01-10 14:10:34 +0000156
157 # Add any new files to SVN control (if we are running with add_new_files).
epoger@google.com0f645b62012-05-22 19:14:01 +0000158 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.com20ad5ac2012-01-17 21:26:05 +0000162
epoger@google.com0f645b62012-05-22 19:14:01 +0000163 # 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.com27442af2011-12-29 21:13:08 +0000173
epoger@google.comd6256552012-01-10 14:10:34 +0000174def RaiseUsageException():
epoger@google.com1610a682012-04-12 18:02:25 +0000175 raise Exception('%s\nRun with --help for more detail.' % (
176 USAGE_STRING % __file__))
177
178def 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.com4a6ba462012-07-27 15:19:54 +0000187 # See http://code.google.com/p/skia/issues/detail?id=678
epoger@google.comf5ad0772012-09-07 16:05:34 +0000188 # Now that we have added Svn.ListSubdirs(), we should be able to do this
189 # pretty easily...
epoger@google.com4a6ba462012-07-27 15:19:54 +0000190 #
191 # For now, I generate this list using these Unix commands:
borenet@google.com3578eb72012-08-28 16:53:10 +0000192 # svn ls http://skia.googlecode.com/svn/gm-expected | grep ^base | sort >/tmp/baselines
epoger@google.com4a6ba462012-07-27 15:19:54 +0000193 # 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.com1610a682012-04-12 18:02:25 +0000195 args = [
borenet@google.com3578eb72012-08-28 16:53:10 +0000196 '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.com7c961e92012-09-11 17:29:37 +0000204 'base-shuttle-win7-intel-angle',
205 'base-shuttle-win7-intel-directwrite',
epoger@google.com1610a682012-04-12 18:02:25 +0000206 ]
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.com1610a682012-04-12 18:02:25 +0000213 # Process the subdirs, one at a time.
214 for baseline_subdir in baseline_subdirs:
215 DownloadBaselinesForOnePlatform(baseline_subdir=baseline_subdir)
epoger@google.comd6256552012-01-10 14:10:34 +0000216
epoger@google.com27442af2011-12-29 21:13:08 +0000217if __name__ == '__main__':
epoger@google.com1610a682012-04-12 18:02:25 +0000218 parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
epoger@google.comd6256552012-01-10 14:10:34 +0000219 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.comf5ad0772012-09-07 16:05:34 +0000224 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.comd6256552012-01-10 14:10:34 +0000234 (options, args) = parser.parse_args()
235 Main(options, args)