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 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 UpdateMakefile(): 292 RewriteTextFile('src/Makefile.am', 293 lambda line : re.sub( 294 r'^PROTOBUF_VERSION = .*$', 295 'PROTOBUF_VERSION = %s' % ":".join(map(str,GetSharedObjectVersion())), 296 line)) 297 298 299def UpdateObjectiveC(): 300 RewriteTextFile('Protobuf.podspec', 301 lambda line : re.sub( 302 r"^ s.version = '.*'$", 303 " s.version = '%s'" % GetFullVersion(rc_suffix = '-rc'), 304 line)) 305 RewriteTextFile('Protobuf-C++.podspec', 306 lambda line : re.sub( 307 r"^ s.version = '.*'$", 308 " s.version = '%s'" % GetFullVersion(rc_suffix = '-rc'), 309 line)) 310 311 312def UpdatePhp(): 313 def Callback(document): 314 def CreateNode(tagname, indent, children): 315 elem = document.createElement(tagname) 316 indent += 1 317 for child in children: 318 elem.appendChild(document.createTextNode('\n' + (' ' * indent))) 319 elem.appendChild(child) 320 indent -= 1 321 elem.appendChild(document.createTextNode('\n' + (' ' * indent))) 322 return elem 323 324 root = document.documentElement 325 now = datetime.datetime.now() 326 ReplaceText(Find(root, 'date'), now.strftime('%Y-%m-%d')) 327 ReplaceText(Find(root, 'time'), now.strftime('%H:%M:%S')) 328 version = Find(root, 'version') 329 ReplaceText(Find(version, 'release'), GetFullVersion(rc_suffix = 'RC')) 330 ReplaceText(Find(version, 'api'), NEW_VERSION) 331 stability = Find(root, 'stability') 332 ReplaceText(Find(stability, 'release'), 333 'stable' if RC_VERSION < 0 else 'beta') 334 ReplaceText(Find(stability, 'api'), 'stable' if RC_VERSION < 0 else 'beta') 335 changelog = Find(root, 'changelog') 336 for old_version in changelog.getElementsByTagName('version'): 337 if Find(old_version, 'release').firstChild.nodeValue == NEW_VERSION: 338 print ('[WARNING] Version %s already exists in the change log.' 339 % NEW_VERSION) 340 return 341 if RC_VERSION != 0: 342 changelog.appendChild(document.createTextNode(' ')) 343 release = CreateNode('release', 2, [ 344 CreateNode('version', 3, [ 345 FindAndClone(version, 'release'), 346 FindAndClone(version, 'api') 347 ]), 348 CreateNode('stability', 3, [ 349 FindAndClone(stability, 'release'), 350 FindAndClone(stability, 'api') 351 ]), 352 FindAndClone(root, 'date'), 353 FindAndClone(root, 'time'), 354 FindAndClone(root, 'license'), 355 CreateNode('notes', 3, []), 356 ]) 357 changelog.appendChild(release) 358 changelog.appendChild(document.createTextNode('\n ')) 359 RewriteXml('php/ext/google/protobuf/package.xml', Callback) 360 RewriteTextFile('php/ext/google/protobuf/protobuf.h', 361 lambda line : re.sub( 362 r"^#define PHP_PROTOBUF_VERSION .*$", 363 "#define PHP_PROTOBUF_VERSION \"%s\"" % GetFullVersion(rc_suffix = 'RC'), 364 line)) 365 366def UpdatePython(): 367 RewriteTextFile('python/google/protobuf/__init__.py', 368 lambda line : re.sub( 369 r"^__version__ = '.*'$", 370 "__version__ = '%s'" % GetFullVersion(rc_suffix = 'rc'), 371 line)) 372 373def UpdateRuby(): 374 RewriteXml('ruby/pom.xml', 375 lambda document : ReplaceText( 376 Find(document.documentElement, 'version'), GetFullVersion())) 377 RewriteXml('ruby/pom.xml', 378 lambda document : ReplaceText( 379 Find(Find(Find(document.documentElement, 'dependencies'), 'dependency'), 'version'), 380 GetFullVersion())) 381 RewriteTextFile('ruby/google-protobuf.gemspec', 382 lambda line : re.sub( 383 r'^ s.version = ".*"$', 384 ' s.version = "%s"' % GetFullVersion(rc_suffix = '.rc.'), 385 line)) 386 387def UpdateBazel(): 388 RewriteTextFile('protobuf_version.bzl', 389 lambda line : re.sub( 390 r"^PROTOBUF_VERSION = '.*'$", 391 "PROTOBUF_VERSION = '%s'" % GetFullVersion(), 392 line)) 393 394 395UpdateCMake() 396UpdateConfigure() 397UpdateCsharp() 398UpdateCpp() 399UpdateJava() 400UpdateMakefile() 401UpdateObjectiveC() 402UpdatePhp() 403UpdatePython() 404UpdateRuby() 405UpdateBazel() 406