xref: /llvm-project/llvm/utils/release/bump-version.py (revision cb1a3bb29ffcd65c221f017164a83400310a0d4b)
112ba7f27STobias Hieta#!/usr/bin/env python3
212ba7f27STobias Hieta
312ba7f27STobias Hieta# This script bumps the version of LLVM in *all* the different places where
412ba7f27STobias Hieta# it needs to be defined. Which is quite a few.
512ba7f27STobias Hieta
612ba7f27STobias Hietaimport sys
712ba7f27STobias Hietaimport argparse
812ba7f27STobias Hietaimport packaging.version
912ba7f27STobias Hietafrom pathlib import Path
1012ba7f27STobias Hietaimport re
1112ba7f27STobias Hietafrom typing import Optional
1212ba7f27STobias Hieta
1312ba7f27STobias Hieta
1412ba7f27STobias Hietaclass Processor:
1520fe2525STobias Hieta    def __init__(self, args):
1620fe2525STobias Hieta        self.args = args
1720fe2525STobias Hieta
1812ba7f27STobias Hieta    def process_line(self, line: str) -> str:
1912ba7f27STobias Hieta        raise NotImplementedError()
2012ba7f27STobias Hieta
2112ba7f27STobias Hieta    def process_file(self, fpath: Path, version: packaging.version.Version) -> None:
2212ba7f27STobias Hieta        self.version = version
2312ba7f27STobias Hieta        self.major, self.minor, self.patch, self.suffix = (
2412ba7f27STobias Hieta            version.major,
2512ba7f27STobias Hieta            version.minor,
2612ba7f27STobias Hieta            version.micro,
2712ba7f27STobias Hieta            version.pre,
2812ba7f27STobias Hieta        )
2920fe2525STobias Hieta
3020fe2525STobias Hieta        if self.args.rc:
3120fe2525STobias Hieta            self.suffix = f"-rc{self.args.rc}"
3220fe2525STobias Hieta
3320fe2525STobias Hieta        if self.args.git:
3420fe2525STobias Hieta            self.suffix = "git"
3520fe2525STobias Hieta
3612ba7f27STobias Hieta        data = fpath.read_text()
3712ba7f27STobias Hieta        new_data = []
3812ba7f27STobias Hieta
3912ba7f27STobias Hieta        for line in data.splitlines(True):
4012ba7f27STobias Hieta            nline = self.process_line(line)
4112ba7f27STobias Hieta
4212ba7f27STobias Hieta            # Print the failing line just to inform the user.
4312ba7f27STobias Hieta            if nline != line:
4412ba7f27STobias Hieta                print(f"{fpath.name}: {line.strip()} -> {nline.strip()}")
4512ba7f27STobias Hieta
4612ba7f27STobias Hieta            new_data.append(nline)
4712ba7f27STobias Hieta
4812ba7f27STobias Hieta        fpath.write_text("".join(new_data), newline="\n")
4912ba7f27STobias Hieta
5012ba7f27STobias Hieta    # Return a string from the version class
5112ba7f27STobias Hieta    # optionally include the suffix (-rcX)
5212ba7f27STobias Hieta    def version_str(
5312ba7f27STobias Hieta        self,
5412ba7f27STobias Hieta        version: Optional[packaging.version.Version] = None,
5512ba7f27STobias Hieta        include_suffix: bool = True,
5612ba7f27STobias Hieta    ) -> str:
5712ba7f27STobias Hieta        if version is None:
5812ba7f27STobias Hieta            version = self.version
5912ba7f27STobias Hieta
6012ba7f27STobias Hieta        ver = f"{version.major}.{version.minor}.{version.micro}"
6112ba7f27STobias Hieta        if include_suffix and version.pre:
6212ba7f27STobias Hieta            ver += f"-{version.pre[0]}{version.pre[1]}"
6312ba7f27STobias Hieta        return ver
6412ba7f27STobias Hieta
6512ba7f27STobias Hieta
6612ba7f27STobias Hieta# llvm/CMakeLists.txt
6712ba7f27STobias Hietaclass CMakeProcessor(Processor):
6812ba7f27STobias Hieta    def process_line(self, line: str) -> str:
6912ba7f27STobias Hieta        nline = line
7012ba7f27STobias Hieta
7112ba7f27STobias Hieta        # LLVM_VERSION_SUFFIX should be set to -rcX or be blank if we are
7212ba7f27STobias Hieta        # building a final version.
7312ba7f27STobias Hieta        if "set(LLVM_VERSION_SUFFIX" in line:
7412ba7f27STobias Hieta            if self.suffix:
7512ba7f27STobias Hieta                nline = re.sub(
7612ba7f27STobias Hieta                    r"set\(LLVM_VERSION_SUFFIX(.*)\)",
7720fe2525STobias Hieta                    f"set(LLVM_VERSION_SUFFIX {self.suffix})",
7812ba7f27STobias Hieta                    line,
7912ba7f27STobias Hieta                )
8012ba7f27STobias Hieta            else:
81b71edfaaSTobias Hieta                nline = re.sub(
82b71edfaaSTobias Hieta                    r"set\(LLVM_VERSION_SUFFIX(.*)\)", f"set(LLVM_VERSION_SUFFIX)", line
83b71edfaaSTobias Hieta                )
8412ba7f27STobias Hieta
8512ba7f27STobias Hieta        # Check the rest of the LLVM_VERSION_ lines.
8612ba7f27STobias Hieta        elif "set(LLVM_VERSION_" in line:
8712ba7f27STobias Hieta            for c, cver in (
8812ba7f27STobias Hieta                ("MAJOR", self.major),
8912ba7f27STobias Hieta                ("MINOR", self.minor),
9012ba7f27STobias Hieta                ("PATCH", self.patch),
9112ba7f27STobias Hieta            ):
9212ba7f27STobias Hieta                nline = re.sub(
93b71edfaaSTobias Hieta                    rf"set\(LLVM_VERSION_{c} (\d+)",
94b71edfaaSTobias Hieta                    rf"set(LLVM_VERSION_{c} {cver}",
9512ba7f27STobias Hieta                    line,
9612ba7f27STobias Hieta                )
9712ba7f27STobias Hieta                if nline != line:
9812ba7f27STobias Hieta                    break
9912ba7f27STobias Hieta
10012ba7f27STobias Hieta        return nline
10112ba7f27STobias Hieta
10212ba7f27STobias Hieta
10312ba7f27STobias Hieta# GN build system
10412ba7f27STobias Hietaclass GNIProcessor(Processor):
10512ba7f27STobias Hieta    def process_line(self, line: str) -> str:
10612ba7f27STobias Hieta        if "llvm_version_" in line:
10712ba7f27STobias Hieta            for c, cver in (
10812ba7f27STobias Hieta                ("major", self.major),
10912ba7f27STobias Hieta                ("minor", self.minor),
11012ba7f27STobias Hieta                ("patch", self.patch),
11112ba7f27STobias Hieta            ):
112b71edfaaSTobias Hieta                nline = re.sub(
113b71edfaaSTobias Hieta                    rf"llvm_version_{c} = \d+", f"llvm_version_{c} = {cver}", line
114b71edfaaSTobias Hieta                )
11512ba7f27STobias Hieta                if nline != line:
11612ba7f27STobias Hieta                    return nline
11712ba7f27STobias Hieta
11812ba7f27STobias Hieta        return line
11912ba7f27STobias Hieta
12012ba7f27STobias Hieta
12112ba7f27STobias Hieta# LIT python file, a simple tuple
12212ba7f27STobias Hietaclass LitProcessor(Processor):
12312ba7f27STobias Hieta    def process_line(self, line: str) -> str:
12412ba7f27STobias Hieta        if "__versioninfo__" in line:
12512ba7f27STobias Hieta            nline = re.sub(
126b71edfaaSTobias Hieta                rf"__versioninfo__(.*)\((\d+), (\d+), (\d+)\)",
12712ba7f27STobias Hieta                f"__versioninfo__\\1({self.major}, {self.minor}, {self.patch})",
12812ba7f27STobias Hieta                line,
12912ba7f27STobias Hieta            )
13012ba7f27STobias Hieta            return nline
13112ba7f27STobias Hieta        return line
13212ba7f27STobias Hieta
13312ba7f27STobias Hieta
13412ba7f27STobias Hieta# Handle libc++ config header
13512ba7f27STobias Hietaclass LibCXXProcessor(Processor):
13612ba7f27STobias Hieta    def process_line(self, line: str) -> str:
13712ba7f27STobias Hieta        # match #define _LIBCPP_VERSION 160000 in a relaxed way
13812ba7f27STobias Hieta        match = re.match(r".*\s_LIBCPP_VERSION\s+(\d{6})$", line)
13912ba7f27STobias Hieta        if match:
14012ba7f27STobias Hieta            verstr = f"{str(self.major).zfill(2)}{str(self.minor).zfill(2)}{str(self.patch).zfill(2)}"
14112ba7f27STobias Hieta
14212ba7f27STobias Hieta            nline = re.sub(
143b71edfaaSTobias Hieta                rf"_LIBCPP_VERSION (\d+)",
14412ba7f27STobias Hieta                f"_LIBCPP_VERSION {verstr}",
14512ba7f27STobias Hieta                line,
14612ba7f27STobias Hieta            )
14712ba7f27STobias Hieta            return nline
14812ba7f27STobias Hieta        return line
14912ba7f27STobias Hieta
15012ba7f27STobias Hieta
15112ba7f27STobias Hietaif __name__ == "__main__":
15212ba7f27STobias Hieta    parser = argparse.ArgumentParser(
15312ba7f27STobias Hieta        usage="Call this script with a version and it will bump the version for you"
15412ba7f27STobias Hieta    )
15512ba7f27STobias Hieta    parser.add_argument("version", help="Version to bump to, e.g. 15.0.1", default=None)
15612ba7f27STobias Hieta    parser.add_argument("--rc", default=None, type=int, help="RC version")
15720fe2525STobias Hieta    parser.add_argument("--git", action="store_true", help="Git version")
15812ba7f27STobias Hieta    parser.add_argument(
15912ba7f27STobias Hieta        "-s",
16012ba7f27STobias Hieta        "--source-root",
16112ba7f27STobias Hieta        default=None,
16212ba7f27STobias Hieta        help="LLVM source root (/path/llvm-project). Defaults to the llvm-project the script is located in.",
16312ba7f27STobias Hieta    )
16412ba7f27STobias Hieta
16512ba7f27STobias Hieta    args = parser.parse_args()
16612ba7f27STobias Hieta
16720fe2525STobias Hieta    if args.rc and args.git:
16820fe2525STobias Hieta        raise RuntimeError("Can't specify --git and --rc at the same time!")
16920fe2525STobias Hieta
17012ba7f27STobias Hieta    verstr = args.version
17112ba7f27STobias Hieta
17212ba7f27STobias Hieta    # parse the version string with distutils.
17312ba7f27STobias Hieta    # note that -rc will end up as version.pre here
17412ba7f27STobias Hieta    # since it's a prerelease
17512ba7f27STobias Hieta    version = packaging.version.parse(verstr)
17612ba7f27STobias Hieta
17712ba7f27STobias Hieta    # Find llvm-project root
17812ba7f27STobias Hieta    source_root = Path(__file__).resolve().parents[3]
17912ba7f27STobias Hieta
18012ba7f27STobias Hieta    if args.source_root:
18112ba7f27STobias Hieta        source_root = Path(args.source_root).resolve()
18212ba7f27STobias Hieta
18312ba7f27STobias Hieta    files_to_update = (
18412ba7f27STobias Hieta        # Main CMakeLists.
18520fe2525STobias Hieta        (source_root / "cmake" / "Modules" / "LLVMVersion.cmake", CMakeProcessor(args)),
18612ba7f27STobias Hieta        # Lit configuration
18712ba7f27STobias Hieta        (
18812ba7f27STobias Hieta            "llvm/utils/lit/lit/__init__.py",
18920fe2525STobias Hieta            LitProcessor(args),
19012ba7f27STobias Hieta        ),
191*cb1a3bb2SAiden Grossman        # mlgo-utils configuration
192*cb1a3bb2SAiden Grossman        (
193*cb1a3bb2SAiden Grossman            "llvm/utils/mlgo-utils/mlgo/__init__.py",
194*cb1a3bb2SAiden Grossman            LitProcessor(args),
195*cb1a3bb2SAiden Grossman        ),
19612ba7f27STobias Hieta        # GN build system
19712ba7f27STobias Hieta        (
19812ba7f27STobias Hieta            "llvm/utils/gn/secondary/llvm/version.gni",
19920fe2525STobias Hieta            GNIProcessor(args),
20012ba7f27STobias Hieta        ),
20112ba7f27STobias Hieta        (
20212ba7f27STobias Hieta            "libcxx/include/__config",
20320fe2525STobias Hieta            LibCXXProcessor(args),
20412ba7f27STobias Hieta        ),
20512ba7f27STobias Hieta    )
20612ba7f27STobias Hieta
20712ba7f27STobias Hieta    for f, processor in files_to_update:
20812ba7f27STobias Hieta        processor.process_file(source_root / Path(f), version)
209