xref: /aosp_15_r20/external/cronet/testing/scripts/rust/generate_script.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env vpython3
2*6777b538SAndroid Build Coastguard Worker# Copyright 2021 The Chromium Authors
3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file.
5*6777b538SAndroid Build Coastguard Worker
6*6777b538SAndroid Build Coastguard Workerimport argparse
7*6777b538SAndroid Build Coastguard Workerimport os
8*6777b538SAndroid Build Coastguard Workerimport stat
9*6777b538SAndroid Build Coastguard Workerimport sys
10*6777b538SAndroid Build Coastguard Worker
11*6777b538SAndroid Build Coastguard Workersys.path.append(
12*6777b538SAndroid Build Coastguard Worker    os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
13*6777b538SAndroid Build Coastguard Worker                 'build'))
14*6777b538SAndroid Build Coastguard Workerimport action_helpers
15*6777b538SAndroid Build Coastguard Worker
16*6777b538SAndroid Build Coastguard Worker
17*6777b538SAndroid Build Coastguard Workerdef _parse_args(args):
18*6777b538SAndroid Build Coastguard Worker    description = 'Generator of bash scripts that can invoke the Python ' \
19*6777b538SAndroid Build Coastguard Worker                  'library for running Rust unit tests with support for ' \
20*6777b538SAndroid Build Coastguard Worker                  'Chromium test filters, sharding, and test output.'
21*6777b538SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(description=description)
22*6777b538SAndroid Build Coastguard Worker    parser.add_argument('--script-path',
23*6777b538SAndroid Build Coastguard Worker                        dest='script_path',
24*6777b538SAndroid Build Coastguard Worker                        help='Where to write the bash script.',
25*6777b538SAndroid Build Coastguard Worker                        metavar='FILEPATH',
26*6777b538SAndroid Build Coastguard Worker                        required=True)
27*6777b538SAndroid Build Coastguard Worker    parser.add_argument('--exe-dir',
28*6777b538SAndroid Build Coastguard Worker                        dest='exe_dir',
29*6777b538SAndroid Build Coastguard Worker                        help='Directory where the wrapped executables are',
30*6777b538SAndroid Build Coastguard Worker                        metavar='PATH',
31*6777b538SAndroid Build Coastguard Worker                        required=True)
32*6777b538SAndroid Build Coastguard Worker    parser.add_argument('--rust-test-executables',
33*6777b538SAndroid Build Coastguard Worker                        dest='rust_test_executables',
34*6777b538SAndroid Build Coastguard Worker                        help='File listing one or more executables to wrap. ' \
35*6777b538SAndroid Build Coastguard Worker                             '(basenames - no .exe extension or directory)',
36*6777b538SAndroid Build Coastguard Worker                        metavar='FILEPATH',
37*6777b538SAndroid Build Coastguard Worker                        required=True)
38*6777b538SAndroid Build Coastguard Worker    parser.add_argument('--make-bat',
39*6777b538SAndroid Build Coastguard Worker                        action='store_true',
40*6777b538SAndroid Build Coastguard Worker                        help='Generate a .bat file instead of a bash script')
41*6777b538SAndroid Build Coastguard Worker    return parser.parse_args(args=args)
42*6777b538SAndroid Build Coastguard Worker
43*6777b538SAndroid Build Coastguard Worker
44*6777b538SAndroid Build Coastguard Workerdef _find_test_executables(args):
45*6777b538SAndroid Build Coastguard Worker    exes = set()
46*6777b538SAndroid Build Coastguard Worker    input_filepath = args.rust_test_executables
47*6777b538SAndroid Build Coastguard Worker    with open(input_filepath) as f:
48*6777b538SAndroid Build Coastguard Worker        for line in f:
49*6777b538SAndroid Build Coastguard Worker            exe_name = line.strip()
50*6777b538SAndroid Build Coastguard Worker            # Append ".exe" extension when *targeting* Windows, not when this
51*6777b538SAndroid Build Coastguard Worker            # script is running on windows. We do that by using `args.make_bat`
52*6777b538SAndroid Build Coastguard Worker            # as a signal.
53*6777b538SAndroid Build Coastguard Worker            if exe_name in exes:
54*6777b538SAndroid Build Coastguard Worker                raise ValueError(
55*6777b538SAndroid Build Coastguard Worker                    f'Duplicate entry "{exe_name}" in {input_filepath}')
56*6777b538SAndroid Build Coastguard Worker            if args.make_bat:
57*6777b538SAndroid Build Coastguard Worker                suffix = ".exe"
58*6777b538SAndroid Build Coastguard Worker            else:
59*6777b538SAndroid Build Coastguard Worker                suffix = ""
60*6777b538SAndroid Build Coastguard Worker            exes.add(f'{exe_name}{suffix}')
61*6777b538SAndroid Build Coastguard Worker    if not exes:
62*6777b538SAndroid Build Coastguard Worker        raise ValueError(f'Unexpectedly empty file: {input_filepath}')
63*6777b538SAndroid Build Coastguard Worker    exes = sorted(exes)  # For stable results in unit tests.
64*6777b538SAndroid Build Coastguard Worker    return exes
65*6777b538SAndroid Build Coastguard Worker
66*6777b538SAndroid Build Coastguard Worker
67*6777b538SAndroid Build Coastguard Workerdef _validate_if_test_executables_exist(exes):
68*6777b538SAndroid Build Coastguard Worker    for exe in exes:
69*6777b538SAndroid Build Coastguard Worker        if not os.path.isfile(exe):
70*6777b538SAndroid Build Coastguard Worker            raise ValueError(f'File not found: {exe}')
71*6777b538SAndroid Build Coastguard Worker
72*6777b538SAndroid Build Coastguard Worker
73*6777b538SAndroid Build Coastguard Workerdef _generate_script(args, should_validate_if_exes_exist=True):
74*6777b538SAndroid Build Coastguard Worker    THIS_DIR = os.path.abspath(os.path.dirname(__file__))
75*6777b538SAndroid Build Coastguard Worker    GEN_SCRIPT_DIR = os.path.dirname(args.script_path)
76*6777b538SAndroid Build Coastguard Worker
77*6777b538SAndroid Build Coastguard Worker    # Path from the .bat or bash script to the test exes.
78*6777b538SAndroid Build Coastguard Worker    exe_dir = os.path.relpath(args.exe_dir, start=GEN_SCRIPT_DIR)
79*6777b538SAndroid Build Coastguard Worker    exe_dir = os.path.normpath(exe_dir)
80*6777b538SAndroid Build Coastguard Worker
81*6777b538SAndroid Build Coastguard Worker    # Path from the .bat or bash script to the python main.
82*6777b538SAndroid Build Coastguard Worker    main_dir = os.path.relpath(THIS_DIR, start=GEN_SCRIPT_DIR)
83*6777b538SAndroid Build Coastguard Worker    main_dir = os.path.normpath(main_dir)
84*6777b538SAndroid Build Coastguard Worker
85*6777b538SAndroid Build Coastguard Worker    exes = _find_test_executables(args)
86*6777b538SAndroid Build Coastguard Worker    if should_validate_if_exes_exist:
87*6777b538SAndroid Build Coastguard Worker        _validate_if_test_executables_exist(exes)
88*6777b538SAndroid Build Coastguard Worker
89*6777b538SAndroid Build Coastguard Worker    if args.make_bat:
90*6777b538SAndroid Build Coastguard Worker        res = '@echo off\n'
91*6777b538SAndroid Build Coastguard Worker        res += f'vpython3 "%~dp0\\{main_dir}\\rust_main_program.py" ^\n'
92*6777b538SAndroid Build Coastguard Worker        for exe in exes:
93*6777b538SAndroid Build Coastguard Worker            res += f'    "--rust-test-executable=%~dp0\\{exe_dir}\\{exe}" ^\n'
94*6777b538SAndroid Build Coastguard Worker        res += '    %*'
95*6777b538SAndroid Build Coastguard Worker    else:
96*6777b538SAndroid Build Coastguard Worker        res = '#!/bin/bash\n'
97*6777b538SAndroid Build Coastguard Worker        res += (f'env vpython3 '
98*6777b538SAndroid Build Coastguard Worker                f'"$(dirname $0)/{main_dir}/rust_main_program.py" \\\n')
99*6777b538SAndroid Build Coastguard Worker        for exe in exes:
100*6777b538SAndroid Build Coastguard Worker            res += (
101*6777b538SAndroid Build Coastguard Worker                f'    '
102*6777b538SAndroid Build Coastguard Worker                f'"--rust-test-executable=$(dirname $0)/{exe_dir}/{exe}" \\\n')
103*6777b538SAndroid Build Coastguard Worker        res += '    "$@"'
104*6777b538SAndroid Build Coastguard Worker    return res
105*6777b538SAndroid Build Coastguard Worker
106*6777b538SAndroid Build Coastguard Worker
107*6777b538SAndroid Build Coastguard Workerdef _main():
108*6777b538SAndroid Build Coastguard Worker    args = _parse_args(sys.argv[1:])
109*6777b538SAndroid Build Coastguard Worker
110*6777b538SAndroid Build Coastguard Worker    # atomic_output will ensure we only write to the file on disk if what we
111*6777b538SAndroid Build Coastguard Worker    # give to write() is different than what's currently on disk.
112*6777b538SAndroid Build Coastguard Worker    with action_helpers.atomic_output(args.script_path) as f:
113*6777b538SAndroid Build Coastguard Worker        f.write(_generate_script(args).encode())
114*6777b538SAndroid Build Coastguard Worker
115*6777b538SAndroid Build Coastguard Worker    # chmod a+x
116*6777b538SAndroid Build Coastguard Worker    st = os.stat(args.script_path)
117*6777b538SAndroid Build Coastguard Worker    if (not st.st_mode & stat.S_IXUSR) or (not st.st_mode & stat.S_IXGRP) or \
118*6777b538SAndroid Build Coastguard Worker       (not st.st_mode & stat.S_IXOTH):
119*6777b538SAndroid Build Coastguard Worker        os.chmod(args.script_path,
120*6777b538SAndroid Build Coastguard Worker                 st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
121*6777b538SAndroid Build Coastguard Worker
122*6777b538SAndroid Build Coastguard Worker
123*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__':
124*6777b538SAndroid Build Coastguard Worker    _main()
125