xref: /aosp_15_r20/external/toolchain-utils/binary_search_tool/run_bisect_tests.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2020 The ChromiumOS Authors
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Run full bisection test."""
8
9
10import argparse
11import os
12import sys
13
14from cros_utils import command_executer
15
16
17TEST_DIR = "full_bisect_test"
18DEFAULT_BISECT_DIR = "/tmp/sysroot_bisect"
19
20
21def populate_good_files(top_dir, ce, bisect_dir=DEFAULT_BISECT_DIR):
22    # 'make clean'
23    work_dir = os.path.join(top_dir, TEST_DIR, "work")
24    cmd = "rm -f %s/*.o" % work_dir
25    status = ce.RunCommand(cmd)
26    if status != 0:
27        print("Error trying to clean out work directory: %s" % cmd)
28        return status
29
30    # set up the 'good' source files
31    script = os.path.join(top_dir, TEST_DIR, "make_sources_good.sh")
32    status = ce.RunCommand(script)
33    if status != 0:
34        print('Error setting up "good" source files: %s' % script)
35        return status
36
37    export_bisect = "export BISECT_DIR=%s; " % bisect_dir
38    # build the good source files
39    script_path = os.path.join(top_dir, TEST_DIR)
40    if os.path.exists("/usr/bin/x86_64-cros-linux-gnu-gcc"):
41        build_script = "chromeos_build.sh"
42    else:
43        build_script = "build.sh"
44    cmd = "%s export BISECT_STAGE=POPULATE_GOOD; pushd %s; ./%s; popd" % (
45        export_bisect,
46        script_path,
47        build_script,
48    )
49    status = ce.RunCommand(cmd)
50    return status
51
52
53def populate_bad_files(top_dir, ce, bisect_dir=DEFAULT_BISECT_DIR):
54    # 'make clean'
55    work_dir = os.path.join(top_dir, TEST_DIR, "work")
56    cmd = "rm -f %s/*.o" % work_dir
57    status = ce.RunCommand(cmd)
58    if status != 0:
59        print("Error trying to clean out work directory: %s" % cmd)
60        return status
61
62    # set up the 'bad' source files
63    script = os.path.join(top_dir, TEST_DIR, "make_sources_bad.sh")
64    status = ce.RunCommand(script)
65    if status != 0:
66        print('Error setting up "bad" source files: %s' % script)
67        return status
68
69    export_bisect = "export BISECT_DIR=%s; " % bisect_dir
70    # build the bad source files
71    script_path = os.path.join(top_dir, TEST_DIR)
72    if os.path.exists("/usr/bin/x86_64-cros-linux-gnu-gcc"):
73        build_script = "chromeos_build.sh"
74    else:
75        build_script = "build.sh"
76    cmd = "%s export BISECT_STAGE=POPULATE_BAD; pushd %s; ./%s ; popd" % (
77        export_bisect,
78        script_path,
79        build_script,
80    )
81    status = ce.RunCommand(cmd)
82    return status
83
84
85def run_main_bisection_test(top_dir, ce):
86    test_script = os.path.join(top_dir, TEST_DIR, "main-bisect-test.sh")
87    status = ce.RunCommand(test_script)
88    return status
89
90
91def verify_compiler_and_wrapper():
92    # We don't need to do any special setup if running inside a ChromeOS
93    # chroot.
94    if os.path.exists("/usr/bin/x86_64-cros-linux-gnu-gcc"):
95        return True
96
97    message = """
98*** IMPORTANT --- READ THIS CAREFULLY!! ***
99
100This test uses the command 'gcc' to compile the good/bad versions of the
101source program.  BEFORE you can run this script you must make sure that
102your compiler wrapper is in the right place, with the right name, so that
103a call to 'gcc' will go through your compiler wrapper and "do the right
104thing".
105
106Is your compiler wrapper properly set up? [Y/n]
107"""
108
109    print(message)
110    inp = sys.stdin.readline()
111    inp = inp.strip()
112    inp = inp.lower()
113    return not inp or inp == "y" or inp == "yes"
114
115
116def Main(argv):
117    parser = argparse.ArgumentParser()
118    parser.add_argument(
119        "--dir",
120        dest="directory",
121        help="Bisection work tree, where good  & bad object "
122        "files go.  Default is /tmp/sysroot_bisect",
123    )
124
125    options = parser.parse_args(argv)
126
127    # Make sure the compiler wrapper & soft links are properly set up.
128    wrapper_is_setup = verify_compiler_and_wrapper()
129    if not wrapper_is_setup:
130        print(
131            "Exiting now.  Please re-run after you have set up the compiler "
132            "wrapper."
133        )
134        return 0
135
136    # Make sure we're in the correct directory for running this test.
137    cwd = os.getcwd()
138    if not os.path.exists(os.path.join(cwd, "full_bisect_test")):
139        print(
140            "Error:  Wrong directory.  This script must be run from the top level"
141            " of the binary_search_tool tree (under toolchain_utils)."
142        )
143        return 1
144
145    ce = command_executer.GetCommandExecuter()
146    bisect_dir = options.directory
147    if not bisect_dir:
148        bisect_dir = DEFAULT_BISECT_DIR
149
150    # Make sure BISECT_DIR is clean
151    if os.path.exists(bisect_dir):
152        cmd = "rm -Rf %s/*" % bisect_dir
153        retv = ce.RunCommand(cmd)
154        if retv != 0:
155            return retv
156
157    retv = populate_good_files(cwd, ce, bisect_dir)
158    if retv != 0:
159        return retv
160
161    retv = populate_bad_files(cwd, ce, bisect_dir)
162    if retv != 0:
163        return retv
164
165    # Set up good/bad work soft links
166    cmd = "rm -f %s/%s/good-objects; ln -s %s/good %s/%s/good-objects" % (
167        cwd,
168        TEST_DIR,
169        bisect_dir,
170        cwd,
171        TEST_DIR,
172    )
173
174    status = ce.RunCommand(cmd)
175    if status != 0:
176        print("Error executing: %s; exiting now." % cmd)
177        return status
178
179    cmd = "rm -f %s/%s/bad-objects; ln -s %s/bad %s/%s/bad-objects" % (
180        cwd,
181        TEST_DIR,
182        bisect_dir,
183        cwd,
184        TEST_DIR,
185    )
186
187    status = ce.RunCommand(cmd)
188    if status != 0:
189        print("Error executing: %s; exiting now." % cmd)
190        return status
191
192    retv = run_main_bisection_test(cwd, ce)
193    return retv
194
195
196if __name__ == "__main__":
197    retval = Main(sys.argv[1:])
198    sys.exit(retval)
199