1#!/usr/bin/env python3 2# Usage: ./update_version.py <MAJOR>.<MINOR>.<MICRO> [<RC version>] 3# 4# Example: 5# ./update_version.py 3.7.1 2 6# => Version will become 3.7.1-rc-2 (beta) 7# ./update_version.py 3.7.1 8# => Version will become 3.7.1 (stable) 9 10import datetime 11import re 12import sys 13from xml.dom import minidom 14 15if len(sys.argv) < 2 or len(sys.argv) > 3: 16 print(""" 17[ERROR] Please specify a version. 18 19./update_version.py <MAJOR>.<MINOR>.<MICRO> [<RC version>] 20 21Example: 22./update_version.py 3.7.1 2 23""") 24 exit(1) 25 26NEW_VERSION = sys.argv[1] 27NEW_VERSION_INFO = [int(x) for x in NEW_VERSION.split('.')] 28if len(NEW_VERSION_INFO) != 3: 29 print(""" 30[ERROR] Version must be in the format <MAJOR>.<MINOR>.<MICRO> 31 32Example: 33./update_version.py 3.7.3 34""") 35 exit(1) 36 37RC_VERSION = -1 38if len(sys.argv) > 2: 39 RC_VERSION = int(sys.argv[2]) 40 41 42def Find(elem, tagname): 43 for child in elem.childNodes: 44 if child.nodeName == tagname: 45 return child 46 return None 47 48 49def FindAndClone(elem, tagname): 50 return Find(elem, tagname).cloneNode(True) 51 52 53def ReplaceText(elem, text): 54 elem.firstChild.replaceWholeText(text) 55 56 57def GetFullVersion(rc_suffix = '-rc-'): 58 if RC_VERSION < 0: 59 return NEW_VERSION 60 else: 61 return '%s%s%s' % (NEW_VERSION, rc_suffix, RC_VERSION) 62 63 64def GetSharedObjectVersion(): 65 protobuf_version_offset = 11 66 expected_major_version = 3 67 if NEW_VERSION_INFO[0] != expected_major_version: 68 print("""[ERROR] Major protobuf version has changed. Please update 69update_version.py to readjust the protobuf_version_offset and 70expected_major_version such that the PROTOBUF_VERSION in src/Makefile.am is 71always increasing. 72 """) 73 exit(1) 74 return [NEW_VERSION_INFO[1] + protobuf_version_offset, NEW_VERSION_INFO[2], 0] 75 76 77def RewriteXml(filename, rewriter, add_xml_prefix=True): 78 document = minidom.parse(filename) 79 rewriter(document) 80 # document.toxml() always prepend the XML version without inserting new line. 81 # We wants to preserve as much of the original formatting as possible, so we 82 # will remove the default XML version and replace it with our custom one when 83 # wherever necessary. 84 content = document.toxml().replace('<?xml version="1.0" ?>', '') 85 file_handle = open(filename, 'wb') 86 if add_xml_prefix: 87 file_handle.write(b'<?xml version="1.0" encoding="UTF-8"?>\n') 88 file_handle.write(content.encode('utf-8')) 89 file_handle.write(b'\n') 90 file_handle.close() 91 92 93def RewriteTextFile(filename, line_rewriter): 94 lines = open(filename, 'r').readlines() 95 updated_lines = [] 96 for line in lines: 97 updated_lines.append(line_rewriter(line)) 98 if lines == updated_lines: 99 print('%s was not updated. Please double check.' % filename) 100 f = open(filename, 'w') 101 f.write(''.join(updated_lines)) 102 f.close() 103 104 105def UpdateCMake(): 106 cmake_files = ( 107 'cmake/libprotobuf.cmake', 108 'cmake/libprotobuf-lite.cmake', 109 'cmake/libprotoc.cmake' 110 ) 111 for cmake_file in cmake_files: 112 RewriteTextFile(cmake_file, 113 lambda line : re.sub( 114 r'SOVERSION ([0-9]+)$', 115 'SOVERSION %s' % GetSharedObjectVersion()[0], 116 line)) 117 118 119def UpdateConfigure(): 120 RewriteTextFile('configure.ac', 121 lambda line : re.sub( 122 r'^AC_INIT\(\[Protocol Buffers\],\[.*\],\[[email protected]\],\[protobuf\]\)$', 123 ('AC_INIT([Protocol Buffers],[%s],[[email protected]],[protobuf])' 124 % GetFullVersion()), 125 line)) 126 127 128def UpdateCpp(): 129 cpp_version = '%d%03d%03d' % ( 130 NEW_VERSION_INFO[0], NEW_VERSION_INFO[1], NEW_VERSION_INFO[2]) 131 version_suffix = '' 132 if RC_VERSION != -1: 133 version_suffix = '-rc%s' % RC_VERSION 134 def RewriteCommon(line): 135 line = re.sub( 136 r'^#define GOOGLE_PROTOBUF_VERSION .*$', 137 '#define GOOGLE_PROTOBUF_VERSION %s' % cpp_version, 138 line) 139 line = re.sub( 140 r'^#define PROTOBUF_VERSION .*$', 141 '#define PROTOBUF_VERSION %s' % cpp_version, 142 line) 143 line = re.sub( 144 r'^#define GOOGLE_PROTOBUF_VERSION_SUFFIX .*$', 145 '#define GOOGLE_PROTOBUF_VERSION_SUFFIX "%s"' % version_suffix, 146 line) 147 line = re.sub( 148 r'^#define PROTOBUF_VERSION_SUFFIX .*$', 149 '#define PROTOBUF_VERSION_SUFFIX "%s"' % version_suffix, 150 line) 151 if NEW_VERSION_INFO[2] == 0: 152 line = re.sub( 153 r'^#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC .*$', 154 '#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC %s' % cpp_version, 155 line) 156 line = re.sub( 157 r'^#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION .*$', 158 '#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION %s' % cpp_version, 159 line) 160 line = re.sub( 161 r'^static const int kMinHeaderVersionForLibrary = .*$', 162 'static const int kMinHeaderVersionForLibrary = %s;' % cpp_version, 163 line) 164 line = re.sub( 165 r'^static const int kMinHeaderVersionForProtoc = .*$', 166 'static const int kMinHeaderVersionForProtoc = %s;' % cpp_version, 167 line) 168 return line 169 170 def RewritePortDef(line): 171 line = re.sub( 172 r'^#define PROTOBUF_VERSION .*$', 173 '#define PROTOBUF_VERSION %s' % cpp_version, 174 line) 175 line = re.sub( 176 r'^#define PROTOBUF_VERSION_SUFFIX .*$', 177 '#define PROTOBUF_VERSION_SUFFIX "%s"' % version_suffix, 178 line) 179 if NEW_VERSION_INFO[2] == 0: 180 line = re.sub( 181 r'^#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC .*$', 182 '#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC %s' % cpp_version, 183 line) 184 line = re.sub( 185 r'^#define PROTOBUF_MIN_PROTOC_VERSION .*$', 186 '#define PROTOBUF_MIN_PROTOC_VERSION %s' % cpp_version, 187 line) 188 line = re.sub( 189 r'^#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION .*$', 190 '#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION %s' % cpp_version, 191 line) 192 return line 193 194 def RewritePbH(line): 195 line = re.sub( 196 r'^#if PROTOBUF_VERSION < .*$', 197 '#if PROTOBUF_VERSION < %s' % cpp_version, 198 line) 199 line = re.sub( 200 r'^#if .* < PROTOBUF_MIN_PROTOC_VERSION$', 201 '#if %s < PROTOBUF_MIN_PROTOC_VERSION' % cpp_version, 202 line) 203 return line 204 205 RewriteTextFile('src/google/protobuf/stubs/common.h', RewriteCommon) 206 RewriteTextFile('src/google/protobuf/port_def.inc', RewritePortDef) 207 208 209def UpdateCsharp(): 210 RewriteXml('csharp/src/Google.Protobuf/Google.Protobuf.csproj', 211 lambda document : ReplaceText( 212 Find(Find(document.documentElement, 'PropertyGroup'), 'VersionPrefix'), 213 GetFullVersion(rc_suffix = '-rc')), 214 add_xml_prefix=False) 215 216 RewriteXml('csharp/Google.Protobuf.Tools.nuspec', 217 lambda document : ReplaceText( 218 Find(Find(document.documentElement, 'metadata'), 'version'), 219 GetFullVersion(rc_suffix = '-rc'))) 220 221 222def UpdateJava(): 223 RewriteXml('java/pom.xml', 224 lambda document : ReplaceText( 225 Find(document.documentElement, 'version'), GetFullVersion())) 226 227 RewriteXml('java/bom/pom.xml', 228 lambda document : ReplaceText( 229 Find(document.documentElement, 'version'), GetFullVersion())) 230 231 RewriteXml('java/core/pom.xml', 232 lambda document : ReplaceText( 233 Find(Find(document.documentElement, 'parent'), 'version'), 234 GetFullVersion())) 235 236 RewriteXml('java/lite/pom.xml', 237 lambda document : ReplaceText( 238 Find(Find(document.documentElement, 'parent'), 'version'), 239 GetFullVersion())) 240 241 RewriteXml('java/util/pom.xml', 242 lambda document : ReplaceText( 243 Find(Find(document.documentElement, 'parent'), 'version'), 244 GetFullVersion())) 245 246 RewriteXml('java/kotlin/pom.xml', 247 lambda document : ReplaceText( 248 Find(Find(document.documentElement, 'parent'), 'version'), 249 GetFullVersion())) 250 251 RewriteXml('java/kotlin-lite/pom.xml', 252 lambda document : ReplaceText( 253 Find(Find(document.documentElement, 'parent'), 'version'), 254 GetFullVersion())) 255 256 RewriteXml('protoc-artifacts/pom.xml', 257 lambda document : ReplaceText( 258 Find(document.documentElement, 'version'), GetFullVersion())) 259 260 RewriteTextFile('java/README.md', 261 lambda line : re.sub( 262 r'<version>.*</version>', 263 '<version>%s</version>' % GetFullVersion(), 264 line)) 265 266 RewriteTextFile('java/README.md', 267 lambda line : re.sub( 268 r'implementation \'com.google.protobuf:protobuf-java:.*\'', 269 'implementation \'com.google.protobuf:protobuf-java:%s\'' % GetFullVersion(), 270 line)) 271 272 RewriteTextFile('java/lite.md', 273 lambda line : re.sub( 274 r'<version>.*</version>', 275 '<version>%s</version>' % GetFullVersion(), 276 line)) 277 278 279def UpdateMakefile(): 280 RewriteTextFile('src/Makefile.am', 281 lambda line : re.sub( 282 r'^PROTOBUF_VERSION = .*$', 283 'PROTOBUF_VERSION = %s' % ":".join(map(str,GetSharedObjectVersion())), 284 line)) 285 286 287def UpdateObjectiveC(): 288 RewriteTextFile('Protobuf.podspec', 289 lambda line : re.sub( 290 r"^ s.version = '.*'$", 291 " s.version = '%s'" % GetFullVersion(rc_suffix = '-rc'), 292 line)) 293 RewriteTextFile('Protobuf-C++.podspec', 294 lambda line : re.sub( 295 r"^ s.version = '.*'$", 296 " s.version = '%s'" % GetFullVersion(rc_suffix = '-rc'), 297 line)) 298 299 300def UpdatePhp(): 301 def Callback(document): 302 def CreateNode(tagname, indent, children): 303 elem = document.createElement(tagname) 304 indent += 1 305 for child in children: 306 elem.appendChild(document.createTextNode('\n' + (' ' * indent))) 307 elem.appendChild(child) 308 indent -= 1 309 elem.appendChild(document.createTextNode('\n' + (' ' * indent))) 310 return elem 311 312 root = document.documentElement 313 now = datetime.datetime.now() 314 ReplaceText(Find(root, 'date'), now.strftime('%Y-%m-%d')) 315 ReplaceText(Find(root, 'time'), now.strftime('%H:%M:%S')) 316 version = Find(root, 'version') 317 ReplaceText(Find(version, 'release'), GetFullVersion(rc_suffix = 'RC')) 318 ReplaceText(Find(version, 'api'), NEW_VERSION) 319 stability = Find(root, 'stability') 320 ReplaceText(Find(stability, 'release'), 321 'stable' if RC_VERSION < 0 else 'beta') 322 ReplaceText(Find(stability, 'api'), 'stable' if RC_VERSION < 0 else 'beta') 323 changelog = Find(root, 'changelog') 324 for old_version in changelog.getElementsByTagName('version'): 325 if Find(old_version, 'release').firstChild.nodeValue == NEW_VERSION: 326 print ('[WARNING] Version %s already exists in the change log.' 327 % NEW_VERSION) 328 return 329 if RC_VERSION != 0: 330 changelog.appendChild(document.createTextNode(' ')) 331 release = CreateNode('release', 2, [ 332 CreateNode('version', 3, [ 333 FindAndClone(version, 'release'), 334 FindAndClone(version, 'api') 335 ]), 336 CreateNode('stability', 3, [ 337 FindAndClone(stability, 'release'), 338 FindAndClone(stability, 'api') 339 ]), 340 FindAndClone(root, 'date'), 341 FindAndClone(root, 'time'), 342 FindAndClone(root, 'license'), 343 CreateNode('notes', 3, []), 344 ]) 345 changelog.appendChild(release) 346 changelog.appendChild(document.createTextNode('\n ')) 347 RewriteXml('php/ext/google/protobuf/package.xml', Callback) 348 RewriteTextFile('php/ext/google/protobuf/protobuf.h', 349 lambda line : re.sub( 350 r"^#define PHP_PROTOBUF_VERSION .*$", 351 "#define PHP_PROTOBUF_VERSION \"%s\"" % GetFullVersion(rc_suffix = 'RC'), 352 line)) 353 354def UpdatePython(): 355 RewriteTextFile('python/google/protobuf/__init__.py', 356 lambda line : re.sub( 357 r"^__version__ = '.*'$", 358 "__version__ = '%s'" % GetFullVersion(rc_suffix = 'rc'), 359 line)) 360 361def UpdateRuby(): 362 RewriteXml('ruby/pom.xml', 363 lambda document : ReplaceText( 364 Find(document.documentElement, 'version'), GetFullVersion())) 365 RewriteXml('ruby/pom.xml', 366 lambda document : ReplaceText( 367 Find(Find(Find(document.documentElement, 'dependencies'), 'dependency'), 'version'), 368 GetFullVersion())) 369 RewriteTextFile('ruby/google-protobuf.gemspec', 370 lambda line : re.sub( 371 r'^ s.version = ".*"$', 372 ' s.version = "%s"' % GetFullVersion(rc_suffix = '.rc.'), 373 line)) 374 375def UpdateBazel(): 376 RewriteTextFile('protobuf_version.bzl', 377 lambda line : re.sub( 378 r"^PROTOBUF_VERSION = '.*'$", 379 "PROTOBUF_VERSION = '%s'" % GetFullVersion(), 380 line)) 381 382 383UpdateCMake() 384UpdateConfigure() 385UpdateCsharp() 386UpdateCpp() 387UpdateJava() 388UpdateMakefile() 389UpdateObjectiveC() 390UpdatePhp() 391UpdatePython() 392UpdateRuby() 393UpdateBazel() 394