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 # whenever 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\],\[.*\],\[protobuf@googlegroups.com\],\[protobuf\]\)$', 123 ('AC_INIT([Protocol Buffers],[%s],[protobuf@googlegroups.com],[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 RewriteTextFile('src/google/protobuf/any.pb.h', RewritePbH) 208 RewriteTextFile('src/google/protobuf/api.pb.h', RewritePbH) 209 RewriteTextFile('src/google/protobuf/descriptor.pb.h', RewritePbH) 210 RewriteTextFile('src/google/protobuf/duration.pb.h', RewritePbH) 211 RewriteTextFile('src/google/protobuf/empty.pb.h', RewritePbH) 212 RewriteTextFile('src/google/protobuf/field_mask.pb.h', RewritePbH) 213 RewriteTextFile('src/google/protobuf/source_context.pb.h', RewritePbH) 214 RewriteTextFile('src/google/protobuf/struct.pb.h', RewritePbH) 215 RewriteTextFile('src/google/protobuf/timestamp.pb.h', RewritePbH) 216 RewriteTextFile('src/google/protobuf/type.pb.h', RewritePbH) 217 RewriteTextFile('src/google/protobuf/wrappers.pb.h', RewritePbH) 218 RewriteTextFile('src/google/protobuf/compiler/plugin.pb.h', RewritePbH) 219 220 221def UpdateCsharp(): 222 RewriteXml('csharp/src/Google.Protobuf/Google.Protobuf.csproj', 223 lambda document : ReplaceText( 224 Find(Find(document.documentElement, 'PropertyGroup'), 'VersionPrefix'), 225 GetFullVersion(rc_suffix = '-rc')), 226 add_xml_prefix=False) 227 228 RewriteXml('csharp/Google.Protobuf.Tools.nuspec', 229 lambda document : ReplaceText( 230 Find(Find(document.documentElement, 'metadata'), 'version'), 231 GetFullVersion(rc_suffix = '-rc'))) 232 233 234def UpdateJava(): 235 RewriteXml('java/pom.xml', 236 lambda document : ReplaceText( 237 Find(document.documentElement, 'version'), GetFullVersion())) 238 239 RewriteXml('java/bom/pom.xml', 240 lambda document : ReplaceText( 241 Find(document.documentElement, 'version'), GetFullVersion())) 242 243 RewriteXml('java/core/pom.xml', 244 lambda document : ReplaceText( 245 Find(Find(document.documentElement, 'parent'), 'version'), 246 GetFullVersion())) 247 248 RewriteXml('java/lite/pom.xml', 249 lambda document : ReplaceText( 250 Find(Find(document.documentElement, 'parent'), 'version'), 251 GetFullVersion())) 252 253 RewriteXml('java/util/pom.xml', 254 lambda document : ReplaceText( 255 Find(Find(document.documentElement, 'parent'), 'version'), 256 GetFullVersion())) 257 258 RewriteXml('java/kotlin/pom.xml', 259 lambda document : ReplaceText( 260 Find(Find(document.documentElement, 'parent'), 'version'), 261 GetFullVersion())) 262 263 RewriteXml('java/kotlin-lite/pom.xml', 264 lambda document : ReplaceText( 265 Find(Find(document.documentElement, 'parent'), 'version'), 266 GetFullVersion())) 267 268 RewriteXml('protoc-artifacts/pom.xml', 269 lambda document : ReplaceText( 270 Find(document.documentElement, 'version'), GetFullVersion())) 271 272 RewriteTextFile('java/README.md', 273 lambda line : re.sub( 274 r'<version>.*</version>', 275 '<version>%s</version>' % GetFullVersion(), 276 line)) 277 278 RewriteTextFile('java/README.md', 279 lambda line : re.sub( 280 r'implementation \'com.google.protobuf:protobuf-java:.*\'', 281 'implementation \'com.google.protobuf:protobuf-java:%s\'' % GetFullVersion(), 282 line)) 283 284 RewriteTextFile('java/lite.md', 285 lambda line : re.sub( 286 r'<version>.*</version>', 287 '<version>%s</version>' % GetFullVersion(), 288 line)) 289 290 291def UpdateJavaScript(): 292 RewriteTextFile('js/package.json', 293 lambda line : re.sub( 294 r'^ "version": ".*",$', 295 ' "version": "%s",' % GetFullVersion(rc_suffix = '-rc.'), 296 line)) 297 298 299def UpdateMakefile(): 300 RewriteTextFile('src/Makefile.am', 301 lambda line : re.sub( 302 r'^PROTOBUF_VERSION = .*$', 303 'PROTOBUF_VERSION = %s' % ":".join(map(str,GetSharedObjectVersion())), 304 line)) 305 306 307def UpdateObjectiveC(): 308 RewriteTextFile('Protobuf.podspec', 309 lambda line : re.sub( 310 r"^ s.version = '.*'$", 311 " s.version = '%s'" % GetFullVersion(rc_suffix = '-rc'), 312 line)) 313 RewriteTextFile('Protobuf-C++.podspec', 314 lambda line : re.sub( 315 r"^ s.version = '.*'$", 316 " s.version = '%s'" % GetFullVersion(rc_suffix = '-rc'), 317 line)) 318 319 320def UpdatePhp(): 321 def Callback(document): 322 def CreateNode(tagname, indent, children): 323 elem = document.createElement(tagname) 324 indent += 1 325 for child in children: 326 elem.appendChild(document.createTextNode('\n' + (' ' * indent))) 327 elem.appendChild(child) 328 indent -= 1 329 elem.appendChild(document.createTextNode('\n' + (' ' * indent))) 330 return elem 331 332 root = document.documentElement 333 now = datetime.datetime.now() 334 ReplaceText(Find(root, 'date'), now.strftime('%Y-%m-%d')) 335 ReplaceText(Find(root, 'time'), now.strftime('%H:%M:%S')) 336 version = Find(root, 'version') 337 ReplaceText(Find(version, 'release'), GetFullVersion(rc_suffix = 'RC')) 338 ReplaceText(Find(version, 'api'), NEW_VERSION) 339 stability = Find(root, 'stability') 340 ReplaceText(Find(stability, 'release'), 341 'stable' if RC_VERSION < 0 else 'beta') 342 ReplaceText(Find(stability, 'api'), 'stable' if RC_VERSION < 0 else 'beta') 343 changelog = Find(root, 'changelog') 344 for old_version in changelog.getElementsByTagName('version'): 345 if Find(old_version, 'release').firstChild.nodeValue == NEW_VERSION: 346 print ('[WARNING] Version %s already exists in the change log.' 347 % NEW_VERSION) 348 return 349 if RC_VERSION != 0: 350 changelog.appendChild(document.createTextNode(' ')) 351 release = CreateNode('release', 2, [ 352 CreateNode('version', 3, [ 353 FindAndClone(version, 'release'), 354 FindAndClone(version, 'api') 355 ]), 356 CreateNode('stability', 3, [ 357 FindAndClone(stability, 'release'), 358 FindAndClone(stability, 'api') 359 ]), 360 FindAndClone(root, 'date'), 361 FindAndClone(root, 'time'), 362 FindAndClone(root, 'license'), 363 CreateNode('notes', 3, []), 364 ]) 365 changelog.appendChild(release) 366 changelog.appendChild(document.createTextNode('\n ')) 367 RewriteXml('php/ext/google/protobuf/package.xml', Callback) 368 RewriteTextFile('php/ext/google/protobuf/protobuf.h', 369 lambda line : re.sub( 370 r"^#define PHP_PROTOBUF_VERSION .*$", 371 "#define PHP_PROTOBUF_VERSION \"%s\"" % GetFullVersion(rc_suffix = 'RC'), 372 line)) 373 374def UpdatePython(): 375 RewriteTextFile('python/google/protobuf/__init__.py', 376 lambda line : re.sub( 377 r"^__version__ = '.*'$", 378 "__version__ = '%s'" % GetFullVersion(rc_suffix = 'rc'), 379 line)) 380 381def UpdateRuby(): 382 RewriteXml('ruby/pom.xml', 383 lambda document : ReplaceText( 384 Find(document.documentElement, 'version'), GetFullVersion())) 385 RewriteXml('ruby/pom.xml', 386 lambda document : ReplaceText( 387 Find(Find(Find(document.documentElement, 'dependencies'), 'dependency'), 'version'), 388 GetFullVersion())) 389 RewriteTextFile('ruby/google-protobuf.gemspec', 390 lambda line : re.sub( 391 r'^ s.version = ".*"$', 392 ' s.version = "%s"' % GetFullVersion(rc_suffix = '.rc.'), 393 line)) 394 395def UpdateBazel(): 396 RewriteTextFile('protobuf_version.bzl', 397 lambda line : re.sub( 398 r"^PROTOBUF_VERSION = '.*'$", 399 "PROTOBUF_VERSION = '%s'" % GetFullVersion(), 400 line)) 401 402 403UpdateCMake() 404UpdateConfigure() 405UpdateCsharp() 406UpdateCpp() 407UpdateJava() 408UpdateJavaScript() 409UpdateMakefile() 410UpdateObjectiveC() 411UpdatePhp() 412UpdatePython() 413UpdateRuby() 414UpdateBazel() 415