diff --git a/llvm/utils/release/bump-version.py b/llvm/utils/release/bump-version.py new file mode 100755 --- /dev/null +++ b/llvm/utils/release/bump-version.py @@ -0,0 +1,272 @@ +#!/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 + +# Return a string from the version class +# optionally include the suffix (-rcX) +def version_str(version: packaging.version.Version, include_suffix: bool = True) -> str: + 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 +def process_cmake(fpath, version: packaging.version.Version): + major, minor, patch, suffix = ( + version.major, + version.minor, + version.micro, + version.pre, + ) + data = fpath.read_text() + + new_data = [] + for line in data.splitlines(True): + 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 suffix: + nline = re.sub( + r"set\(LLVM_VERSION_SUFFIX(.*)\)", + f"set(LLVM_VERSION_SUFFIX -{suffix[0]}{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", major), ("MINOR", minor), ("PATCH", patch)): + nline = re.sub( + fr"set\(LLVM_VERSION_{c} (\d+)", + fr"set(LLVM_VERSION_{c} {cver}", + line, + ) + if nline != line: + break + + # Print the failing line just to inform the user. + if nline != line: + print(f"{f.name}: {line.strip()} -> {nline.strip()}") + + new_data.append(nline) + + f.write_text("".join(new_data), newline="\n") + + +# Process the many bazel files. +def process_bazel(fpath, version: packaging.version.Version): + major, minor, patch, suffix = ( + version.major, + version.minor, + version.micro, + version.pre, + ) + data = fpath.read_text() + + new_data = [] + for line in data.splitlines(True): + nline = line + + # This maches the CLANG_VERSION line of clang/Config/config.h + if "CLANG_VERSION " in line: + nline = re.sub( + r"#define CLANG_VERSION (.*)'", + f"#define CLANG_VERSION {version_str(version, 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>{version_str(version)}\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)", major), + ("(MINOR)", minor), + ("(PATCH|PATCHLEVEL)", 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 {version_str(version)}"', + line, + ) + + if nline != line: + print(f"{f.name}: {line.strip()} -> {nline.strip()}") + + new_data.append(nline) + + f.write_text("".join(new_data), newline="\n") + + +# GN build system +def process_gni(fpath, version: packaging.version.Version): + major, minor, patch, suffix = ( + version.major, + version.minor, + version.micro, + version.pre, + ) + data = fpath.read_text() + + new_data = [] + for line in data.splitlines(True): + nline = line + if "llvm_version_" in line: + for c, cver in (("major", major), ("minor", minor), ("patch", patch)): + nline = re.sub( + fr"llvm_version_{c} = \d+", f"llvm_version_{c} = {cver}", line + ) + if nline != line: + break + + if nline != line: + print(f"{f.name}: {line.strip()} -> {nline.strip()}") + + new_data.append(nline) + + f.write_text("".join(new_data), newline="\n") + + +# LIT python file, a simple tuple +def process_lit(fpath, version: packaging.version.Version): + major, minor, patch, suffix = ( + version.major, + version.minor, + version.micro, + version.pre, + ) + data = fpath.read_text() + + new_data = [] + for line in data.splitlines(True): + nline = line + + if "__versioninfo__" in line: + nline = re.sub( + fr"__versioninfo__(.*)\((\d+), (\d+), (\d+)\)", + f"__versioninfo__\\1({major}, {minor}, {patch})", + line, + ) + + if nline != line: + print(f"{f.name}: {line.strip()} -> {nline.strip()}") + + new_data.append(nline) + + f.write_text("".join(new_data), newline="\n") + + +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, i.e. 15.0.1", default=None) + parser.add_argument("--rc", default=None, type=int, help="RC version") + + 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] + + files_to_update = ( + # Main CMakeLists. + (source_root / "llvm" / "CMakeLists.txt", process_cmake), + # Lit configuration + (source_root / "llvm" / "utils" / "lit" / "lit" / "__init__.py", process_lit), + # GN build system + ( + source_root + / "llvm" + / "utils" + / "gn" + / "secondary" + / "llvm" + / "version.gni", + process_gni, + ), + # Bazel build system + ( + source_root + / "utils" + / "bazel" + / "llvm-project-overlay" + / "llvm" + / "include" + / "llvm" + / "Config" + / "llvm-config.h", + process_bazel, + ), + ( + source_root + / "utils" + / "bazel" + / "llvm-project-overlay" + / "clang" + / "BUILD.bazel", + process_bazel, + ), + ( + source_root + / "utils" + / "bazel" + / "llvm-project-overlay" + / "clang" + / "include" + / "clang" + / "Config" + / "config.h", + process_bazel, + ), + ( + source_root + / "utils" + / "bazel" + / "llvm-project-overlay" + / "lld" + / "BUILD.bazel", + process_bazel, + ), + ) + + for f, func in files_to_update: + if func: + func(f, version)