diff --git a/llvm/utils/release/bump-version.py b/llvm/utils/release/bump-version.py new file mode 100755 index 0000000000000..9d30c4ba8f370 --- /dev/null +++ b/llvm/utils/release/bump-version.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 + +# This script bumps the version of LLVM in *all* the different places where +# it needs to be defined. Which is quite a few. + +import sys +import argparse +import packaging.version +from pathlib import Path +import re +from typing import Optional + + +class Processor: + def process_line(self, line: str) -> str: + raise NotImplementedError() + + def process_file(self, fpath: Path, version: packaging.version.Version) -> None: + self.version = version + self.major, self.minor, self.patch, self.suffix = ( + version.major, + version.minor, + version.micro, + version.pre, + ) + data = fpath.read_text() + new_data = [] + + for line in data.splitlines(True): + nline = self.process_line(line) + + # Print the failing line just to inform the user. + if nline != line: + print(f"{fpath.name}: {line.strip()} -> {nline.strip()}") + + new_data.append(nline) + + fpath.write_text("".join(new_data), newline="\n") + + # Return a string from the version class + # optionally include the suffix (-rcX) + def version_str( + self, + version: Optional[packaging.version.Version] = None, + include_suffix: bool = True, + ) -> str: + if version is None: + version = self.version + + ver = f"{version.major}.{version.minor}.{version.micro}" + if include_suffix and version.pre: + ver += f"-{version.pre[0]}{version.pre[1]}" + return ver + + +# llvm/CMakeLists.txt +class CMakeProcessor(Processor): + def process_line(self, line: str) -> str: + nline = line + + # LLVM_VERSION_SUFFIX should be set to -rcX or be blank if we are + # building a final version. + if "set(LLVM_VERSION_SUFFIX" in line: + if self.suffix: + nline = re.sub( + r"set\(LLVM_VERSION_SUFFIX(.*)\)", + f"set(LLVM_VERSION_SUFFIX -{self.suffix[0]}{self.suffix[1]})", + line, + ) + else: + nline = re.sub(r"set\(LLVM_VERSION_SUFFIX(.*)\)", f"set(LLVM_VERSION_SUFFIX)", line) + + # Check the rest of the LLVM_VERSION_ lines. + elif "set(LLVM_VERSION_" in line: + for c, cver in ( + ("MAJOR", self.major), + ("MINOR", self.minor), + ("PATCH", self.patch), + ): + nline = re.sub( + fr"set\(LLVM_VERSION_{c} (\d+)", + fr"set(LLVM_VERSION_{c} {cver}", + line, + ) + if nline != line: + break + + return nline + + +# Process the many bazel files. +class BazelProcessor(Processor): + def process_line(self, line: str) -> str: + # This matches the CLANG_VERSION line of clang/Config/config.h + nline = line + if "CLANG_VERSION " in line: + nline = re.sub( + r"#define CLANG_VERSION (.*)'", + f"#define CLANG_VERSION {self.version_str(include_suffix=False)}'", + line, + ) + # Match version strings of LLVM, Clang and LLD overlay headers + elif "LLVM_VERSION_STRING" in line or "CLANG_VERSION_STRING" in line or "LLD_VERSION_STRING" in line: + nline = re.sub( + r"#define (LLVM|CLANG|LLD)_VERSION_STRING ([\\\"]+)[0-9\.rcgit-]+([\\\"]+)", + rf"#define \g<1>_VERSION_STRING \g<2>{self.version_str()}\g<3>", + line, + ) + # Match the split out MAJOR/MINOR/PATCH versions of LLVM and Clang overlay headers + # in LLVM the define is called _PATCH and in clang it's called _PATCHLEVEL + elif "LLVM_VERSION_" in line or "CLANG_VERSION_" in line: + for c, cver in ( + ("(MAJOR)", self.major), + ("(MINOR)", self.minor), + ("(PATCH|PATCHLEVEL)", self.patch), + ): + nline = re.sub( + fr"(LLVM|CLANG)_VERSION_{c} \d+", + rf"\g<1>_VERSION_\g<2> {cver}", + line, + ) + if nline != line: + break + # Match the BACKEND_PACKAGE_STRING in clang/config.h + elif "BACKEND_PACKAGE_STRING" in line: + nline = re.sub( + r'#define BACKEND_PACKAGE_STRING "LLVM ([0-9\.rcgit-]+)"', + f'#define BACKEND_PACKAGE_STRING "LLVM {self.version_str()}"', + line, + ) + + return nline + + +# GN build system +class GNIProcessor(Processor): + def process_line(self, line: str) -> str: + if "llvm_version_" in line: + for c, cver in ( + ("major", self.major), + ("minor", self.minor), + ("patch", self.patch), + ): + nline = re.sub(fr"llvm_version_{c} = \d+", f"llvm_version_{c} = {cver}", line) + if nline != line: + return nline + + return line + + +# LIT python file, a simple tuple +class LitProcessor(Processor): + def process_line(self, line: str) -> str: + if "__versioninfo__" in line: + nline = re.sub( + fr"__versioninfo__(.*)\((\d+), (\d+), (\d+)\)", + f"__versioninfo__\\1({self.major}, {self.minor}, {self.patch})", + line, + ) + return nline + return line + + +# Handle libc++ config header +class LibCXXProcessor(Processor): + def process_line(self, line: str) -> str: + # match #define _LIBCPP_VERSION 160000 in a relaxed way + match = re.match(r".*\s_LIBCPP_VERSION\s+(\d{6})$", line) + if match: + verstr = f"{str(self.major).zfill(2)}{str(self.minor).zfill(2)}{str(self.patch).zfill(2)}" + + nline = re.sub( + fr"_LIBCPP_VERSION (\d+)", + f"_LIBCPP_VERSION {verstr}", + line, + ) + return nline + return line + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + usage="Call this script with a version and it will bump the version for you" + ) + parser.add_argument("version", help="Version to bump to, e.g. 15.0.1", default=None) + parser.add_argument("--rc", default=None, type=int, help="RC version") + parser.add_argument( + "-s", + "--source-root", + default=None, + help="LLVM source root (/path/llvm-project). Defaults to the llvm-project the script is located in.", + ) + + args = parser.parse_args() + + verstr = args.version + if args.rc: + verstr += f"-rc{args.rc}" + + # parse the version string with distutils. + # note that -rc will end up as version.pre here + # since it's a prerelease + version = packaging.version.parse(verstr) + + # Find llvm-project root + source_root = Path(__file__).resolve().parents[3] + + if args.source_root: + source_root = Path(args.source_root).resolve() + + files_to_update = ( + # Main CMakeLists. + (source_root / "llvm" / "CMakeLists.txt", CMakeProcessor()), + # Lit configuration + ( + "llvm/utils/lit/lit/__init__.py", + LitProcessor(), + ), + # GN build system + ( + "llvm/utils/gn/secondary/llvm/version.gni", + GNIProcessor(), + ), + # Bazel build system + ( + "utils/bazel/llvm-project-overlay/llvm/include/llvm/Config/llvm-config.h", + BazelProcessor(), + ), + ( + "utils/bazel/llvm-project-overlay/clang/BUILD.bazel", + BazelProcessor(), + ), + ( + "utils/bazel/llvm-project-overlay/clang/include/clang/Config/config.h", + BazelProcessor(), + ), + ( + "utils/bazel/llvm-project-overlay/lld/BUILD.bazel", + BazelProcessor(), + ), + ( + "libcxx/include/__config", + LibCXXProcessor(), + ), + ) + + for f, processor in files_to_update: + processor.process_file(source_root / Path(f), version)