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