1#!/usr/bin/env vpython3 2# Copyright 2021 The ANGLE Project Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5"""Script to generate the test spec JSON files. Calls Chromium's generate_buildbot_json. 6 7=== NOTE: DO NOT RUN THIS SCRIPT DIRECTLY. === 8Run scripts/run_code_generation.py instead to update necessary hashes. 9 10""" 11 12import os 13import pprint 14import sys 15import subprocess 16import tempfile 17 18d = os.path.dirname 19THIS_DIR = d(os.path.abspath(__file__)) 20TESTING_BBOT_DIR = os.path.join(d(d(THIS_DIR)), 'testing', 'buildbot') 21sys.path.insert(0, TESTING_BBOT_DIR) 22 23import generate_buildbot_json 24 25# Add custom mixins here. 26ADDITIONAL_MIXINS = { 27 'angle_skia_gold_test': { 28 'args': [ 29 '--git-revision=${got_angle_revision}', 30 # BREAK GLASS IN CASE OF EMERGENCY 31 # Uncommenting this argument will bypass all interactions with Skia 32 # Gold in any tests that use it. This is meant as a temporary 33 # emergency stop in case of a Gold outage that's affecting the bots. 34 # '--bypass-skia-gold-functionality', 35 ], 36 'precommit_args': [ 37 '--gerrit-issue=${patch_issue}', 38 '--gerrit-patchset=${patch_set}', 39 '--buildbucket-id=${buildbucket_build_id}', 40 # This normally evaluates to "0", but will evaluate to "1" if 41 # "Use-Permissive-Angle-Pixel-Comparison: True" is present as a 42 # CL footer. 43 '--use-permissive-pixel-comparison=${use_permissive_angle_pixel_comparison}', 44 ], 45 }, 46 'timeout_120m': { 47 'swarming': { 48 'hard_timeout': 7200, 49 'io_timeout': 300 50 } 51 }, 52} 53 54MIXIN_FILE_NAME = os.path.join(THIS_DIR, 'mixins.pyl') 55MIXINS_PYL_TEMPLATE = """\ 56# GENERATED FILE - DO NOT EDIT. 57# Generated by {script_name} using data from {data_source} 58# 59# Copyright 2021 The ANGLE Project Authors. All rights reserved. 60# Use of this source code is governed by a BSD-style license that can be 61# found in the LICENSE file. 62# 63# This is a .pyl, or "Python Literal", file. You can treat it just like a 64# .json file, with the following exceptions: 65# * all keys must be quoted (use single quotes, please); 66# * comments are allowed, using '#' syntax; and 67# * trailing commas are allowed. 68# 69# For more info see Chromium's mixins.pyl in testing/buildbot. 70 71{mixin_data} 72""" 73 74 75def main(): 76 if len(sys.argv) > 1: 77 gen_bb_json = os.path.join(TESTING_BBOT_DIR, 'generate_buildbot_json.py') 78 mixins_pyl = os.path.join(TESTING_BBOT_DIR, 'mixins.pyl') 79 inputs = [ 80 'test_suite_exceptions.pyl', 'test_suites.pyl', 'variants.pyl', 'waterfalls.pyl', 81 gen_bb_json, mixins_pyl 82 ] 83 outputs = ['angle.json', 'mixins.pyl'] 84 if sys.argv[1] == 'inputs': 85 print(','.join(inputs)) 86 return 0 87 if sys.argv[1] == 'outputs': 88 print(','.join(outputs)) 89 return 0 90 91 # --verify-only enables dirty checks without relying on checked in hashes. 92 # Compares the content of the existing file with the generated content. 93 verify_only = '--verify-only' in sys.argv 94 95 if verify_only: 96 with tempfile.TemporaryDirectory() as temp_dir: 97 return run_generator(verify_only, temp_dir) 98 else: 99 return run_generator(verify_only, None) 100 101 102def write_or_verify_file(filename, content, verify_only): 103 if verify_only: 104 try: 105 with open(filename) as f: 106 # Note: .gitattributes "* text=auto" handles LF <-> CRLF on Windows 107 return f.read() == content 108 except FileNotFoundError: 109 return False 110 else: 111 with open(filename, 'w') as fout: 112 fout.write(content) 113 return True 114 115 116def run_generator(verify_only, temp_dir): 117 chromium_args = generate_buildbot_json.BBJSONGenerator.parse_args([]) 118 chromium_generator = generate_buildbot_json.BBJSONGenerator(chromium_args) 119 chromium_generator.load_configuration_files() 120 121 override_args = ['--pyl-files-dir', THIS_DIR] 122 if verify_only: 123 override_args += ['--output-dir', temp_dir] 124 angle_args = generate_buildbot_json.BBJSONGenerator.parse_args(override_args) 125 angle_generator = generate_buildbot_json.BBJSONGenerator(angle_args) 126 angle_generator.load_configuration_files() 127 angle_generator.resolve_configuration_files() 128 129 seen_mixins = set() 130 for waterfall in angle_generator.waterfalls: 131 seen_mixins = seen_mixins.union(waterfall.get('mixins', set())) 132 for bot_name, tester in waterfall['machines'].items(): 133 seen_mixins = seen_mixins.union(tester.get('mixins', set())) 134 for suite in angle_generator.test_suites.values(): 135 if isinstance(suite, list): 136 # Don't care about this, it's a composition, which shouldn't include a 137 # swarming mixin. 138 continue 139 for test in suite.values(): 140 assert isinstance(test, dict) 141 seen_mixins = seen_mixins.union(test.get('mixins', set())) 142 143 found_mixins = ADDITIONAL_MIXINS.copy() 144 for mixin in seen_mixins: 145 if mixin in found_mixins: 146 continue 147 assert (mixin in chromium_generator.mixins), 'Error with %s mixin' % mixin 148 found_mixins[mixin] = chromium_generator.mixins[mixin] 149 150 pp = pprint.PrettyPrinter(indent=2) 151 152 format_data = { 153 'script_name': os.path.basename(__file__), 154 'data_source': 'waterfall.pyl and Chromium\'s mixins.pyl', 155 'mixin_data': pp.pformat(found_mixins), 156 } 157 generated_mixin_pyl = MIXINS_PYL_TEMPLATE.format(**format_data) 158 159 if not write_or_verify_file(MIXIN_FILE_NAME, generated_mixin_pyl, verify_only): 160 print('infra/specs/mixins.pyl dirty') 161 return 1 162 163 if angle_generator.main() != 0: 164 print('buildbot (pyl to json) generation failed') 165 return 1 166 167 if verify_only: 168 for waterfall in angle_generator.waterfalls: 169 filename = waterfall['name'] + '.json' # angle.json, might have more in future 170 with open(os.path.join(temp_dir, filename)) as f: 171 content = f.read() 172 angle_filename = os.path.join(THIS_DIR, filename) 173 if not write_or_verify_file(angle_filename, content, True): 174 print('infra/specs/%s dirty' % filename) 175 return 1 176 177 return 0 178 179 180if __name__ == '__main__': # pragma: no cover 181 sys.exit(main()) 182