xref: /llvm-project/llvm/utils/update_test_body.py (revision aacea0d0f67401f5a0b74947f3ff179ade9cbf6d)
1*aacea0d0SFangrui Song#!/usr/bin/env python3
2*aacea0d0SFangrui Song"""Generate test body using split-file and a custom script.
3*aacea0d0SFangrui Song
4*aacea0d0SFangrui SongThe script will prepare extra files with `split-file`, invoke `gen`, and then
5*aacea0d0SFangrui Songrewrite the part after `gen` with its stdout.
6*aacea0d0SFangrui Song
7*aacea0d0SFangrui Songhttps://llvm.org/docs/TestingGuide.html#elaborated-tests
8*aacea0d0SFangrui Song
9*aacea0d0SFangrui SongExample:
10*aacea0d0SFangrui SongPATH=/path/to/clang_build/bin:$PATH llvm/utils/update_test_body.py path/to/test.s
11*aacea0d0SFangrui Song"""
12*aacea0d0SFangrui Songimport argparse
13*aacea0d0SFangrui Songimport contextlib
14*aacea0d0SFangrui Songimport os
15*aacea0d0SFangrui Songimport re
16*aacea0d0SFangrui Songimport subprocess
17*aacea0d0SFangrui Songimport sys
18*aacea0d0SFangrui Songimport tempfile
19*aacea0d0SFangrui Song
20*aacea0d0SFangrui Song
21*aacea0d0SFangrui Song@contextlib.contextmanager
22*aacea0d0SFangrui Songdef cd(directory):
23*aacea0d0SFangrui Song    cwd = os.getcwd()
24*aacea0d0SFangrui Song    os.chdir(directory)
25*aacea0d0SFangrui Song    try:
26*aacea0d0SFangrui Song        yield
27*aacea0d0SFangrui Song    finally:
28*aacea0d0SFangrui Song        os.chdir(cwd)
29*aacea0d0SFangrui Song
30*aacea0d0SFangrui Song
31*aacea0d0SFangrui Songdef process(args, path):
32*aacea0d0SFangrui Song    prolog = []
33*aacea0d0SFangrui Song    seen_gen = False
34*aacea0d0SFangrui Song    with open(path) as f:
35*aacea0d0SFangrui Song        for line in f.readlines():
36*aacea0d0SFangrui Song            line = line.rstrip()
37*aacea0d0SFangrui Song            prolog.append(line)
38*aacea0d0SFangrui Song            if (seen_gen and re.match(r"(.|//)---", line)) or line.startswith(".endif"):
39*aacea0d0SFangrui Song                break
40*aacea0d0SFangrui Song            if re.match(r"(.|//)--- gen", line):
41*aacea0d0SFangrui Song                seen_gen = True
42*aacea0d0SFangrui Song        else:
43*aacea0d0SFangrui Song            print(
44*aacea0d0SFangrui Song                "'gen' should be followed by another part (---) or .endif",
45*aacea0d0SFangrui Song                file=sys.stderr,
46*aacea0d0SFangrui Song            )
47*aacea0d0SFangrui Song            return 1
48*aacea0d0SFangrui Song
49*aacea0d0SFangrui Song    if not seen_gen:
50*aacea0d0SFangrui Song        print("'gen' does not exist", file=sys.stderr)
51*aacea0d0SFangrui Song        return 1
52*aacea0d0SFangrui Song    with tempfile.TemporaryDirectory(prefix="update_test_body_") as dir:
53*aacea0d0SFangrui Song        try:
54*aacea0d0SFangrui Song            # If the last line starts with ".endif", remove it.
55*aacea0d0SFangrui Song            sub = subprocess.run(
56*aacea0d0SFangrui Song                ["split-file", "-", dir],
57*aacea0d0SFangrui Song                input="\n".join(
58*aacea0d0SFangrui Song                    prolog[:-1] if prolog[-1].startswith(".endif") else prolog
59*aacea0d0SFangrui Song                ).encode(),
60*aacea0d0SFangrui Song                capture_output=True,
61*aacea0d0SFangrui Song                check=True,
62*aacea0d0SFangrui Song            )
63*aacea0d0SFangrui Song        except subprocess.CalledProcessError as ex:
64*aacea0d0SFangrui Song            sys.stderr.write(ex.stderr.decode())
65*aacea0d0SFangrui Song            return 1
66*aacea0d0SFangrui Song        with cd(dir):
67*aacea0d0SFangrui Song            if args.shell:
68*aacea0d0SFangrui Song                print(f"invoke shell in the temporary directory '{dir}'")
69*aacea0d0SFangrui Song                subprocess.run([os.environ.get("SHELL", "sh")])
70*aacea0d0SFangrui Song                return 0
71*aacea0d0SFangrui Song
72*aacea0d0SFangrui Song            sub = subprocess.run(
73*aacea0d0SFangrui Song                ["sh", "-eu", "gen"],
74*aacea0d0SFangrui Song                capture_output=True,
75*aacea0d0SFangrui Song                # Don't encode the directory information to the Clang output.
76*aacea0d0SFangrui Song                # Remove unneeded details (.ident) as well.
77*aacea0d0SFangrui Song                env=dict(
78*aacea0d0SFangrui Song                    os.environ,
79*aacea0d0SFangrui Song                    CCC_OVERRIDE_OPTIONS="#^-fno-ident",
80*aacea0d0SFangrui Song                    PWD="/proc/self/cwd",
81*aacea0d0SFangrui Song                ),
82*aacea0d0SFangrui Song            )
83*aacea0d0SFangrui Song            sys.stderr.write(sub.stderr.decode())
84*aacea0d0SFangrui Song            if sub.returncode != 0:
85*aacea0d0SFangrui Song                print("'gen' failed", file=sys.stderr)
86*aacea0d0SFangrui Song                return sub.returncode
87*aacea0d0SFangrui Song            if not sub.stdout:
88*aacea0d0SFangrui Song                print("stdout is empty; forgot -o - ?", file=sys.stderr)
89*aacea0d0SFangrui Song                return 1
90*aacea0d0SFangrui Song            content = sub.stdout.decode()
91*aacea0d0SFangrui Song
92*aacea0d0SFangrui Song    with open(path, "w") as f:
93*aacea0d0SFangrui Song        # Print lines up to '.endif'.
94*aacea0d0SFangrui Song        print("\n".join(prolog), file=f)
95*aacea0d0SFangrui Song        # Then print the stdout of 'gen'.
96*aacea0d0SFangrui Song        f.write(content)
97*aacea0d0SFangrui Song
98*aacea0d0SFangrui Song
99*aacea0d0SFangrui Songparser = argparse.ArgumentParser(
100*aacea0d0SFangrui Song    description="Generate test body using split-file and a custom script"
101*aacea0d0SFangrui Song)
102*aacea0d0SFangrui Songparser.add_argument("files", nargs="+")
103*aacea0d0SFangrui Songparser.add_argument(
104*aacea0d0SFangrui Song    "--shell", action="store_true", help="invoke shell instead of 'gen'"
105*aacea0d0SFangrui Song)
106*aacea0d0SFangrui Songargs = parser.parse_args()
107*aacea0d0SFangrui Songfor path in args.files:
108*aacea0d0SFangrui Song    retcode = process(args, path)
109*aacea0d0SFangrui Song    if retcode != 0:
110*aacea0d0SFangrui Song        sys.exit(retcode)
111