#!/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 __init__(self, args): self.args = args 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, ) if self.args.rc: self.suffix = f"-rc{self.args.rc}" if self.args.git: self.suffix = "git" 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})", 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( rf"set\(LLVM_VERSION_{c} (\d+)", rf"set(LLVM_VERSION_{c} {cver}", line, ) if nline != line: break 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( rf"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( rf"__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( rf"_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("--git", action="store_true", help="Git 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() if args.rc and args.git: raise RuntimeError("Can't specify --git and --rc at the same time!") verstr = args.version # 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 / "cmake" / "Modules" / "LLVMVersion.cmake", CMakeProcessor(args)), # Lit configuration ( "llvm/utils/lit/lit/__init__.py", LitProcessor(args), ), # mlgo-utils configuration ( "llvm/utils/mlgo-utils/mlgo/__init__.py", LitProcessor(args), ), # GN build system ( "llvm/utils/gn/secondary/llvm/version.gni", GNIProcessor(args), ), ( "libcxx/include/__config", LibCXXProcessor(args), ), ) for f, processor in files_to_update: processor.process_file(source_root / Path(f), version)