xref: /llvm-project/llvm/utils/gn/build/write_vcsrevision.py (revision b71edfaa4ec3c998aadb35255ce2f60bba2940b0)
1#!/usr/bin/env python3
2
3"""Gets the current revision and writes it to VCSRevision.h."""
4
5import argparse
6import os
7import subprocess
8import sys
9
10
11THIS_DIR = os.path.abspath(os.path.dirname(__file__))
12LLVM_DIR = os.path.dirname(os.path.dirname(os.path.dirname(THIS_DIR)))
13
14
15def which(program):
16    # distutils.spawn.which() doesn't find .bat files,
17    # https://bugs.python.org/issue2200
18    for path in os.environ["PATH"].split(os.pathsep):
19        candidate = os.path.join(path, program)
20        if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
21            return candidate
22    return None
23
24
25def main():
26    parser = argparse.ArgumentParser(description=__doc__)
27    parser.add_argument(
28        "-d",
29        "--depfile",
30        help="if set, writes a depfile that causes this script "
31        "to re-run each time the current revision changes",
32    )
33    parser.add_argument(
34        "--write-git-rev",
35        action="store_true",
36        help="if set, writes git revision, else writes #undef",
37    )
38    parser.add_argument(
39        "--name",
40        action="append",
41        help="if set, writes a depfile that causes this script "
42        "to re-run each time the current revision changes",
43    )
44    parser.add_argument("vcs_header", help="path to the output file to write")
45    args = parser.parse_args()
46
47    vcsrevision_contents = ""
48    if args.write_git_rev:
49        git, use_shell = which("git"), False
50        if not git:
51            git = which("git.exe")
52        if not git:
53            git, use_shell = which("git.bat"), True
54        git_dir = (
55            subprocess.check_output(
56                [git, "rev-parse", "--git-dir"], cwd=LLVM_DIR, shell=use_shell
57            )
58            .decode()
59            .strip()
60        )
61        if not os.path.isdir(git_dir):
62            print('.git dir not found at "%s"' % git_dir, file=sys.stderr)
63            return 1
64
65        rev = (
66            subprocess.check_output(
67                [git, "rev-parse", "--short", "HEAD"], cwd=git_dir, shell=use_shell
68            )
69            .decode()
70            .strip()
71        )
72        url = (
73            subprocess.check_output(
74                [git, "remote", "get-url", "origin"], cwd=git_dir, shell=use_shell
75            )
76            .decode()
77            .strip()
78        )
79        for name in args.name:
80            vcsrevision_contents += '#define %s_REVISION "%s"\n' % (name, rev)
81            vcsrevision_contents += '#define %s_REPOSITORY "%s"\n' % (name, url)
82    else:
83        for name in args.name:
84            vcsrevision_contents += "#undef %s_REVISION\n" % name
85            vcsrevision_contents += "#undef %s_REPOSITORY\n" % name
86
87    # If the output already exists and is identical to what we'd write,
88    # return to not perturb the existing file's timestamp.
89    if (
90        os.path.exists(args.vcs_header)
91        and open(args.vcs_header).read() == vcsrevision_contents
92    ):
93        return 0
94
95    # http://neugierig.org/software/blog/2014/11/binary-revisions.html
96    if args.depfile:
97        build_dir = os.getcwd()
98        with open(args.depfile, "w") as depfile:
99            depfile.write(
100                "%s: %s\n"
101                % (
102                    args.vcs_header,
103                    os.path.relpath(os.path.join(git_dir, "logs", "HEAD"), build_dir),
104                )
105            )
106    open(args.vcs_header, "w").write(vcsrevision_contents)
107
108
109if __name__ == "__main__":
110    sys.exit(main())
111