blob: 04068b70b4c3ca36fd073492af4520bd01523eca [file]
#!/usr/bin/python3
#
# Copyright 2020 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# gen_restricted_traces.py:
# Generates integration code for the restricted trace tests.
import getpass
import glob
import fnmatch
import re
import json
import os
import sys
import subprocess
CIPD_TRACE_PREFIX = 'angle/traces'
EXPERIMENTAL_CIPD_PREFIX = 'experimental/google.com/%s/angle/traces'
DEPS_PATH = '../../../DEPS'
DEPS_START = '# === ANGLE Restricted Trace Generated Code Start ==='
DEPS_END = '# === ANGLE Restricted Trace Generated Code End ==='
DEPS_TEMPLATE = """\
'src/tests/restricted_traces/{trace}': {{
'packages': [
{{
'package': '{trace_prefix}/{trace}',
'version': 'version:{version}',
}},
],
'dep_type': 'cipd',
'condition': 'checkout_angle_restricted_trace_{trace}',
}},
"""
STAR_PATH = '../../../infra/config/representative_traces.star'
DEPS_VAR_START = '# === ANGLE Restricted Trace Generated Var Start ==='
DEPS_VAR_END = '# === ANGLE Restricted Trace Generated Var End ==='
DEPS_VAR_TEMPLATE = """\
'checkout_angle_restricted_trace_{trace}': '{condition}',
"""
def reject_duplicate_keys(pairs):
found_keys = {}
for key, value in pairs:
if key in found_keys:
raise ValueError("duplicate key: %r" % (key,))
else:
found_keys[key] = value
return found_keys
def read_json(json_file):
with open(json_file) as map_file:
return json.loads(map_file.read(), object_pairs_hook=reject_duplicate_keys)
def write_json(json_file, data):
with open(json_file, 'w') as map_file:
json.dump(data, map_file, indent=4, sort_keys=True)
def update_deps(trace_pairs, representative_traces, cq_extra_traces):
# Generate substitution string
replacement = ""
for (trace, version) in trace_pairs:
if 'x' in version:
version = version.strip('x')
trace_prefix = EXPERIMENTAL_CIPD_PREFIX % getpass.getuser()
else:
trace_prefix = CIPD_TRACE_PREFIX
sub = {'trace': trace, 'version': version, 'trace_prefix': trace_prefix}
replacement += DEPS_TEMPLATE.format(**sub)
# Update DEPS to download CIPD dependencies
with open(DEPS_PATH) as f:
lines = f.readlines()
def slice_to_replace(lines, start_tag, end_tag):
start, end = [i for i, s in enumerate(lines) if start_tag in s or end_tag in s]
return slice(start + 1, end)
# Replace lines between DEPS_START and DEPS_END with new code
lines[slice_to_replace(lines, DEPS_START, DEPS_END)] = [replacement]
# Replace lines between DEPS_VAR_START and DEPS_VAR_END with new code
deps_vars = []
for (trace, _) in trace_pairs:
if trace in representative_traces or trace in cq_extra_traces:
condition = 'checkout_angle_restricted_traces'
else:
condition = 'checkout_extra_traces'
deps_vars.append(DEPS_VAR_TEMPLATE.format(trace=trace, condition=condition))
lines[slice_to_replace(lines, DEPS_VAR_START, DEPS_VAR_END)] = deps_vars
with open(DEPS_PATH, 'w') as f:
f.write(''.join(lines))
return True
def update_star(smoke_traces, representative_traces, cq_extra_traces):
star_content = '''\
# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generated by gen_restricted_traces.py. DO NOT EDIT."""
SMOKE_TRACES = [
{smoke_traces}]
REPRESENTATIVE_TRACES = [
{representative_traces}]
'''
combined_representative = sorted(list(set(representative_traces + cq_extra_traces)))
smoke_list = "".join([f' "{trace}",\n' for trace in sorted(smoke_traces)])
rep_list = "".join([f' "{trace}",\n' for trace in combined_representative])
with open(STAR_PATH, 'w') as f:
f.write(star_content.format(smoke_traces=smoke_list, representative_traces=rep_list))
return True
def run_lucicfg():
import platform
infra_config_dir = os.path.abspath('../../../infra/config')
cmd = ['lucicfg']
if platform.system() == 'Windows':
cmd = ['lucicfg.bat']
try:
subprocess.check_output(
cmd + ['generate', 'main.star'], cwd=infra_config_dir, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
print(f'lucicfg failed: {e.output.decode()}')
return False
except Exception as e:
print(f'Error running lucicfg: {e}')
return False
return True
def main():
json_file = 'restricted_traces.json'
# Need to change directory to the script's directory so relative paths work
# if run from other directories.
os.chdir(os.path.dirname(os.path.abspath(__file__)))
json_data = read_json(json_file)
if 'traces' not in json_data:
print('Trace data missing traces key.')
return 1
raw_trace_parts = [trace.split(' ') for trace in json_data['traces']]
representative_traces = [p[0] for p in raw_trace_parts if 'representative' in p[2:]]
smoke_traces = [p[0] for p in raw_trace_parts if 'smoke' in p[2:]]
cq_extra_traces = [
p[0] for p in raw_trace_parts if 'ci' not in p[2:] and 'representative' not in p[2:]
]
trace_pairs = [(p[0], p[1]) for p in raw_trace_parts]
# auto_script parameters.
if len(sys.argv) > 1:
inputs = [json_file]
outputs = ['.gitignore', STAR_PATH]
if sys.argv[1] == 'inputs':
print(','.join(inputs))
elif sys.argv[1] == 'outputs':
print(','.join(outputs))
else:
print('Invalid script parameters.')
return 1
return 0
LIMIT_N = 10
if len(cq_extra_traces) > LIMIT_N:
print(f'Error: Too many CQ extra traces ({len(cq_extra_traces)}). Limit is {LIMIT_N}.')
print('Please move some traces to conditional checkout by adding the "ci" tag.')
return 1
if not update_deps(trace_pairs, representative_traces, cq_extra_traces):
print('DEPS file update failed')
return 1
if not update_star(smoke_traces, representative_traces, cq_extra_traces):
print('representative_traces.star update failed')
return 1
if not run_lucicfg():
print('lucicfg generate failed')
return 1
return 0
if __name__ == '__main__':
sys.exit(main())