1#!/usr/bin/env python3 2 3# This script bumps the version of LLVM in *all* the different places where 4# it needs to be defined. Which is quite a few. 5 6import sys 7import argparse 8import packaging.version 9from pathlib import Path 10import re 11from typing import Optional 12 13 14class Processor: 15 def __init__(self, args): 16 self.args = args 17 18 def process_line(self, line: str) -> str: 19 raise NotImplementedError() 20 21 def process_file(self, fpath: Path, version: packaging.version.Version) -> None: 22 self.version = version 23 self.major, self.minor, self.patch, self.suffix = ( 24 version.major, 25 version.minor, 26 version.micro, 27 version.pre, 28 ) 29 30 if self.args.rc: 31 self.suffix = f"-rc{self.args.rc}" 32 33 if self.args.git: 34 self.suffix = "git" 35 36 data = fpath.read_text() 37 new_data = [] 38 39 for line in data.splitlines(True): 40 nline = self.process_line(line) 41 42 # Print the failing line just to inform the user. 43 if nline != line: 44 print(f"{fpath.name}: {line.strip()} -> {nline.strip()}") 45 46 new_data.append(nline) 47 48 fpath.write_text("".join(new_data), newline="\n") 49 50 # Return a string from the version class 51 # optionally include the suffix (-rcX) 52 def version_str( 53 self, 54 version: Optional[packaging.version.Version] = None, 55 include_suffix: bool = True, 56 ) -> str: 57 if version is None: 58 version = self.version 59 60 ver = f"{version.major}.{version.minor}.{version.micro}" 61 if include_suffix and version.pre: 62 ver += f"-{version.pre[0]}{version.pre[1]}" 63 return ver 64 65 66# llvm/CMakeLists.txt 67class CMakeProcessor(Processor): 68 def process_line(self, line: str) -> str: 69 nline = line 70 71 # LLVM_VERSION_SUFFIX should be set to -rcX or be blank if we are 72 # building a final version. 73 if "set(LLVM_VERSION_SUFFIX" in line: 74 if self.suffix: 75 nline = re.sub( 76 r"set\(LLVM_VERSION_SUFFIX(.*)\)", 77 f"set(LLVM_VERSION_SUFFIX {self.suffix})", 78 line, 79 ) 80 else: 81 nline = re.sub( 82 r"set\(LLVM_VERSION_SUFFIX(.*)\)", f"set(LLVM_VERSION_SUFFIX)", line 83 ) 84 85 # Check the rest of the LLVM_VERSION_ lines. 86 elif "set(LLVM_VERSION_" in line: 87 for c, cver in ( 88 ("MAJOR", self.major), 89 ("MINOR", self.minor), 90 ("PATCH", self.patch), 91 ): 92 nline = re.sub( 93 rf"set\(LLVM_VERSION_{c} (\d+)", 94 rf"set(LLVM_VERSION_{c} {cver}", 95 line, 96 ) 97 if nline != line: 98 break 99 100 return nline 101 102 103# GN build system 104class GNIProcessor(Processor): 105 def process_line(self, line: str) -> str: 106 if "llvm_version_" in line: 107 for c, cver in ( 108 ("major", self.major), 109 ("minor", self.minor), 110 ("patch", self.patch), 111 ): 112 nline = re.sub( 113 rf"llvm_version_{c} = \d+", f"llvm_version_{c} = {cver}", line 114 ) 115 if nline != line: 116 return nline 117 118 return line 119 120 121# LIT python file, a simple tuple 122class LitProcessor(Processor): 123 def process_line(self, line: str) -> str: 124 if "__versioninfo__" in line: 125 nline = re.sub( 126 rf"__versioninfo__(.*)\((\d+), (\d+), (\d+)\)", 127 f"__versioninfo__\\1({self.major}, {self.minor}, {self.patch})", 128 line, 129 ) 130 return nline 131 return line 132 133 134# Handle libc++ config header 135class LibCXXProcessor(Processor): 136 def process_line(self, line: str) -> str: 137 # match #define _LIBCPP_VERSION 160000 in a relaxed way 138 match = re.match(r".*\s_LIBCPP_VERSION\s+(\d{6})$", line) 139 if match: 140 verstr = f"{str(self.major).zfill(2)}{str(self.minor).zfill(2)}{str(self.patch).zfill(2)}" 141 142 nline = re.sub( 143 rf"_LIBCPP_VERSION (\d+)", 144 f"_LIBCPP_VERSION {verstr}", 145 line, 146 ) 147 return nline 148 return line 149 150 151if __name__ == "__main__": 152 parser = argparse.ArgumentParser( 153 usage="Call this script with a version and it will bump the version for you" 154 ) 155 parser.add_argument("version", help="Version to bump to, e.g. 15.0.1", default=None) 156 parser.add_argument("--rc", default=None, type=int, help="RC version") 157 parser.add_argument("--git", action="store_true", help="Git version") 158 parser.add_argument( 159 "-s", 160 "--source-root", 161 default=None, 162 help="LLVM source root (/path/llvm-project). Defaults to the llvm-project the script is located in.", 163 ) 164 165 args = parser.parse_args() 166 167 if args.rc and args.git: 168 raise RuntimeError("Can't specify --git and --rc at the same time!") 169 170 verstr = args.version 171 172 # parse the version string with distutils. 173 # note that -rc will end up as version.pre here 174 # since it's a prerelease 175 version = packaging.version.parse(verstr) 176 177 # Find llvm-project root 178 source_root = Path(__file__).resolve().parents[3] 179 180 if args.source_root: 181 source_root = Path(args.source_root).resolve() 182 183 files_to_update = ( 184 # Main CMakeLists. 185 (source_root / "cmake" / "Modules" / "LLVMVersion.cmake", CMakeProcessor(args)), 186 # Lit configuration 187 ( 188 "llvm/utils/lit/lit/__init__.py", 189 LitProcessor(args), 190 ), 191 # mlgo-utils configuration 192 ( 193 "llvm/utils/mlgo-utils/mlgo/__init__.py", 194 LitProcessor(args), 195 ), 196 # GN build system 197 ( 198 "llvm/utils/gn/secondary/llvm/version.gni", 199 GNIProcessor(args), 200 ), 201 ( 202 "libcxx/include/__config", 203 LibCXXProcessor(args), 204 ), 205 ) 206 207 for f, processor in files_to_update: 208 processor.process_file(source_root / Path(f), version) 209