1#! /usr/bin/env python 2# Copyright 2018 Google LLC. 3# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. 4 5import json 6import md5 7import multiprocessing 8import os 9import shutil 10import sys 11import tempfile 12import urllib 13import urllib2 14 15from subprocess import check_call, check_output 16 17assert '/' in [os.sep, os.altsep] and os.pardir == '..' 18 19ASSETS = 'platform_tools/android/apps/skqp/src/main/assets' 20BUCKET = 'skia-skqp-assets' 21 22def urlopen(url): 23 cookie = os.environ.get('SKIA_GOLD_COOKIE', '') 24 return urllib2.urlopen(urllib2.Request(url, headers={'Cookie': cookie})) 25 26def make_skqp_model(arg): 27 name, urls, exe = arg 28 tmp = tempfile.mkdtemp() 29 for url in urls: 30 urllib.urlretrieve(url, tmp + '/' + url[url.rindex('/') + 1:]) 31 check_call([exe, tmp, ASSETS + '/gmkb/' + name]) 32 shutil.rmtree(tmp) 33 sys.stdout.write(name + ' ') 34 sys.stdout.flush() 35 36def goldgetter(meta, exe): 37 assert os.path.exists(exe) 38 jobs = [] 39 for rec in meta: 40 urls = [d['URL'] for d in rec['digests'] 41 if d['status'] == 'positive' and 42 (set(d['paramset']['config']) & set(['vk', 'gles']))] 43 if urls: 44 jobs.append((rec['testName'], urls, exe)) 45 pool = multiprocessing.Pool(processes=20) 46 pool.map(make_skqp_model, jobs) 47 sys.stdout.write('\n') 48 return set((n for n, _, _ in jobs)) 49 50def gold(first_commit, last_commit): 51 c1, c2 = (check_output(['git', 'rev-parse', c]).strip() 52 for c in (first_commit, last_commit)) 53 f = urlopen('https://public-gold.skia.org/json/export?' + urllib.urlencode([ 54 ('fbegin', c1), 55 ('fend', c2), 56 ('query', 'config=gles&config=vk&source_type=gm'), 57 ('pos', 'true'), 58 ('neg', 'false'), 59 ('unt', 'false') 60 ])) 61 j = json.load(f) 62 f.close() 63 return j 64 65def gset(path): 66 s = set() 67 if os.path.isfile(path): 68 with open(path, 'r') as f: 69 for line in f: 70 s.add(line.strip()) 71 return s 72 73def make_rendertest_list(models, good, bad): 74 assert good.isdisjoint(bad) 75 do_score = good & models 76 no_score = bad | (good - models) 77 to_delete = models & bad 78 for d in to_delete: 79 path = ASSETS + '/gmkb/' + d 80 if os.path.isdir(path): 81 shutil.rmtree(path) 82 results = dict() 83 for n in do_score: 84 results[n] = 0 85 for n in no_score: 86 results[n] = -1 87 return ''.join('%s,%d\n' % (n, results[n]) for n in sorted(results)) 88 89def get_digest(path): 90 m = md5.new() 91 with open(path, 'r') as f: 92 m.update(f.read()) 93 return m.hexdigest() 94 95def upload_cmd(path, digest): 96 return ['gsutil', 'cp', path, 'gs://%s/%s' % (BUCKET, digest)] 97 98def upload_model(): 99 bucket_url = 'gs://%s/' % BUCKET 100 extant = set((u.replace(bucket_url, '', 1) 101 for u in check_output(['gsutil', 'ls', bucket_url]).splitlines() if u)) 102 cmds = [] 103 filelist = [] 104 for dirpath, _, filenames in os.walk(ASSETS + '/gmkb'): 105 for filename in filenames: 106 path = os.path.join(dirpath, filename) 107 digest = get_digest(path) 108 if digest not in extant: 109 cmds.append(upload_cmd(path, digest)) 110 filelist.append('%s;%s\n' % (digest, os.path.relpath(path, ASSETS))) 111 tmp = tempfile.mkdtemp() 112 filelist_path = tmp + '/x' 113 with open(filelist_path, 'w') as o: 114 for l in filelist: 115 o.write(l) 116 filelist_digest = get_digest(filelist_path) 117 if filelist_digest not in extant: 118 cmds.append(upload_cmd(filelist_path, filelist_digest)) 119 120 pool = multiprocessing.Pool(processes=20) 121 pool.map(check_call, cmds) 122 shutil.rmtree(tmp) 123 return filelist_digest 124 125def remove(x): 126 if os.path.isdir(x) and not os.path.islink(x): 127 shutil.rmtree(x) 128 if os.path.exists(x): 129 os.remove(x) 130 131def main(first_commit, last_commit): 132 check_call(upload_cmd('/dev/null', get_digest('/dev/null'))) 133 134 os.chdir(os.path.dirname(__file__) + '/../..') 135 remove(ASSETS + '/files.checksum') 136 for d in [ASSETS + '/gmkb', ASSETS + '/skqp', ]: 137 remove(d) 138 os.mkdir(d) 139 140 check_call([sys.executable, 'tools/git-sync-deps'], 141 env=dict(os.environ, GIT_SYNC_DEPS_QUIET='T')) 142 build = 'out/ndebug' 143 check_call(['bin/gn', 'gen', build, 144 '--args=cc="clang" cxx="clang++" is_debug=false']) 145 check_call(['ninja', '-C', build, 146 'jitter_gms', 'list_gpu_unit_tests', 'make_skqp_model']) 147 148 models = goldgetter(gold(first_commit, last_commit), build + '/make_skqp_model') 149 150 check_call([build + '/jitter_gms', 'tools/skqp/bad_gms.txt']) 151 152 with open(ASSETS + '/skqp/rendertests.txt', 'w') as o: 153 o.write(make_rendertest_list(models, gset('good.txt'), gset('bad.txt'))) 154 155 remove('good.txt') 156 remove('bad.txt') 157 158 with open(ASSETS + '/skqp/unittests.txt', 'w') as o: 159 o.write(check_output([build + '/list_gpu_unit_tests'])) 160 161 with open(ASSETS + '/files.checksum', 'w') as o: 162 o.write(upload_model() + '\n') 163 164 sys.stdout.write(ASSETS + '/files.checksum\n') 165 sys.stdout.write(ASSETS + '/skqp/rendertests.txt\n') 166 sys.stdout.write(ASSETS + '/skqp/unittests.txt\n') 167 168if __name__ == '__main__': 169 if len(sys.argv) != 3: 170 sys.stderr.write('Usage:\n %s C1 C2\n\n' % sys.argv[0]) 171 sys.exit(1) 172 main(sys.argv[1], sys.argv[2]) 173