1#!/usr/bin/env python 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 RewriteXml(filename, rewriter, add_xml_prefix=True): 65 document = minidom.parse(filename) 66 rewriter(document) 67 # document.toxml() always prepend the XML version without inserting new line. 68 # We wants to preserve as much of the original formatting as possible, so we 69 # will remove the default XML version and replace it with our custom one when 70 # whever necessary. 71 content = document.toxml().replace('<?xml version="1.0" ?>', '') 72 file_handle = open(filename, 'wb') 73 if add_xml_prefix: 74 file_handle.write('<?xml version="1.0" encoding="UTF-8"?>\n') 75 file_handle.write(content) 76 file_handle.write('\n') 77 file_handle.close() 78 79 80def RewriteTextFile(filename, line_rewriter): 81 lines = open(filename, 'r').readlines() 82 updated_lines = [] 83 for line in lines: 84 updated_lines.append(line_rewriter(line)) 85 if lines == updated_lines: 86 print '%s was not updated. Please double check.' % filename 87 f = open(filename, 'w') 88 f.write(''.join(updated_lines)) 89 f.close() 90 91 92def UpdateConfigure(): 93 RewriteTextFile('configure.ac', 94 lambda line : re.sub( 95 r'^AC_INIT\(\[Protocol Buffers\],\[.*\],\[protobuf@googlegroups.com\],\[protobuf\]\)$', 96 ('AC_INIT([Protocol Buffers],[%s],[protobuf@googlegroups.com],[protobuf])' 97 % GetFullVersion()), 98 line)) 99 100 101def UpdateCpp(): 102 cpp_version = '%d%03d%03d' % ( 103 NEW_VERSION_INFO[0], NEW_VERSION_INFO[1], NEW_VERSION_INFO[2]) 104 def RewriteCommon(line): 105 line = re.sub( 106 r'^#define GOOGLE_PROTOBUF_VERSION .*$', 107 '#define GOOGLE_PROTOBUF_VERSION %s' % cpp_version, 108 line) 109 line = re.sub( 110 r'^#define PROTOBUF_VERSION .*$', 111 '#define PROTOBUF_VERSION %s' % cpp_version, 112 line) 113 if NEW_VERSION_INFO[2] == 0: 114 line = re.sub( 115 r'^#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC .*$', 116 '#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC %s' % cpp_version, 117 line) 118 line = re.sub( 119 r'^#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION .*$', 120 '#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION %s' % cpp_version, 121 line) 122 line = re.sub( 123 r'^static const int kMinHeaderVersionForLibrary = .*$', 124 'static const int kMinHeaderVersionForLibrary = %s;' % cpp_version, 125 line) 126 line = re.sub( 127 r'^static const int kMinHeaderVersionForProtoc = .*$', 128 'static const int kMinHeaderVersionForProtoc = %s;' % cpp_version, 129 line) 130 return line 131 132 def RewritePortDef(line): 133 line = re.sub( 134 r'^#define PROTOBUF_VERSION .*$', 135 '#define PROTOBUF_VERSION %s' % cpp_version, 136 line) 137 if NEW_VERSION_INFO[2] == 0: 138 line = re.sub( 139 r'^#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC .*$', 140 '#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC %s' % cpp_version, 141 line) 142 line = re.sub( 143 r'^#define PROTOBUF_MIN_PROTOC_VERSION .*$', 144 '#define PROTOBUF_MIN_PROTOC_VERSION %s' % cpp_version, 145 line) 146 line = re.sub( 147 r'^#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION .*$', 148 '#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION %s' % cpp_version, 149 line) 150 return line 151 152 def RewritePbH(line): 153 line = re.sub( 154 r'^#if PROTOBUF_VERSION < .*$', 155 '#if PROTOBUF_VERSION < %s' % cpp_version, 156 line) 157 line = re.sub( 158 r'^#if .* < PROTOBUF_MIN_PROTOC_VERSION$', 159 '#if %s < PROTOBUF_MIN_PROTOC_VERSION' % cpp_version, 160 line) 161 return line 162 163 RewriteTextFile('src/google/protobuf/stubs/common.h', RewriteCommon) 164 RewriteTextFile('src/google/protobuf/port_def.inc', RewritePortDef) 165 RewriteTextFile('src/google/protobuf/any.pb.h', RewritePbH) 166 RewriteTextFile('src/google/protobuf/api.pb.h', RewritePbH) 167 RewriteTextFile('src/google/protobuf/descriptor.pb.h', RewritePbH) 168 RewriteTextFile('src/google/protobuf/duration.pb.h', RewritePbH) 169 RewriteTextFile('src/google/protobuf/empty.pb.h', RewritePbH) 170 RewriteTextFile('src/google/protobuf/field_mask.pb.h', RewritePbH) 171 RewriteTextFile('src/google/protobuf/source_context.pb.h', RewritePbH) 172 RewriteTextFile('src/google/protobuf/struct.pb.h', RewritePbH) 173 RewriteTextFile('src/google/protobuf/timestamp.pb.h', RewritePbH) 174 RewriteTextFile('src/google/protobuf/type.pb.h', RewritePbH) 175 RewriteTextFile('src/google/protobuf/wrappers.pb.h', RewritePbH) 176 RewriteTextFile('src/google/protobuf/compiler/plugin.pb.h', RewritePbH) 177 178 179def UpdateCsharp(): 180 RewriteXml('csharp/src/Google.Protobuf/Google.Protobuf.csproj', 181 lambda document : ReplaceText( 182 Find(Find(document.documentElement, 'PropertyGroup'), 'VersionPrefix'), 183 GetFullVersion(rc_suffix = '-rc')), 184 add_xml_prefix=False) 185 186 RewriteXml('csharp/Google.Protobuf.Tools.nuspec', 187 lambda document : ReplaceText( 188 Find(Find(document.documentElement, 'metadata'), 'version'), 189 GetFullVersion(rc_suffix = '-rc'))) 190 191 192def UpdateJava(): 193 RewriteXml('java/pom.xml', 194 lambda document : ReplaceText( 195 Find(document.documentElement, 'version'), GetFullVersion())) 196 197 RewriteXml('java/bom/pom.xml', 198 lambda document : ReplaceText( 199 Find(document.documentElement, 'version'), GetFullVersion())) 200 201 RewriteXml('java/core/pom.xml', 202 lambda document : ReplaceText( 203 Find(Find(document.documentElement, 'parent'), 'version'), 204 GetFullVersion())) 205 206 RewriteXml('java/lite/pom.xml', 207 lambda document : ReplaceText( 208 Find(Find(document.documentElement, 'parent'), 'version'), 209 GetFullVersion())) 210 211 RewriteXml('java/util/pom.xml', 212 lambda document : ReplaceText( 213 Find(Find(document.documentElement, 'parent'), 'version'), 214 GetFullVersion())) 215 216 RewriteXml('protoc-artifacts/pom.xml', 217 lambda document : ReplaceText( 218 Find(document.documentElement, 'version'), GetFullVersion())) 219 220 221def UpdateJavaScript(): 222 RewriteTextFile('js/package.json', 223 lambda line : re.sub( 224 r'^ "version": ".*",$', 225 ' "version": "%s",' % GetFullVersion(rc_suffix = '-rc.'), 226 line)) 227 228 229def UpdateMakefile(): 230 protobuf_version_offset = 11 231 expected_major_version = 3 232 if NEW_VERSION_INFO[0] != expected_major_version: 233 print """[ERROR] Major protobuf version has changed. Please update 234update_version.py to readjust the protobuf_version_offset and 235expected_major_version such that the PROTOBUF_VERSION in src/Makefile.am is 236always increasing. 237 """ 238 exit(1) 239 240 protobuf_version_info = '%d:%d:0' % ( 241 NEW_VERSION_INFO[1] + protobuf_version_offset, NEW_VERSION_INFO[2]) 242 RewriteTextFile('src/Makefile.am', 243 lambda line : re.sub( 244 r'^PROTOBUF_VERSION = .*$', 245 'PROTOBUF_VERSION = %s' % protobuf_version_info, 246 line)) 247 248 249def UpdateObjectiveC(): 250 RewriteTextFile('Protobuf.podspec', 251 lambda line : re.sub( 252 r"^ s.version = '.*'$", 253 " s.version = '%s'" % GetFullVersion(rc_suffix = '-rc'), 254 line)) 255 RewriteTextFile('Protobuf-C++.podspec', 256 lambda line : re.sub( 257 r"^ s.version = '.*'$", 258 " s.version = '%s'" % GetFullVersion(rc_suffix = '-rc'), 259 line)) 260 261 262def UpdatePhp(): 263 def Callback(document): 264 def CreateNode(tagname, indent, children): 265 elem = document.createElement(tagname) 266 indent += 1 267 for child in children: 268 elem.appendChild(document.createTextNode('\n' + (' ' * indent))) 269 elem.appendChild(child) 270 indent -= 1 271 elem.appendChild(document.createTextNode('\n' + (' ' * indent))) 272 return elem 273 274 root = document.documentElement 275 now = datetime.datetime.now() 276 ReplaceText(Find(root, 'date'), now.strftime('%Y-%m-%d')) 277 ReplaceText(Find(root, 'time'), now.strftime('%H:%M:%S')) 278 version = Find(root, 'version') 279 ReplaceText(Find(version, 'release'), GetFullVersion(rc_suffix = 'RC')) 280 ReplaceText(Find(version, 'api'), NEW_VERSION) 281 stability = Find(root, 'stability') 282 ReplaceText(Find(stability, 'release'), 283 'stable' if RC_VERSION < 0 else 'beta') 284 ReplaceText(Find(stability, 'api'), 'stable' if RC_VERSION < 0 else 'beta') 285 changelog = Find(root, 'changelog') 286 for old_version in changelog.getElementsByTagName('version'): 287 if Find(old_version, 'release').firstChild.nodeValue == NEW_VERSION: 288 print ('[WARNING] Version %s already exists in the change log.' 289 % NEW_VERSION) 290 return 291 if RC_VERSION != 0: 292 changelog.appendChild(document.createTextNode(' ')) 293 release = CreateNode('release', 2, [ 294 CreateNode('version', 3, [ 295 FindAndClone(version, 'release'), 296 FindAndClone(version, 'api') 297 ]), 298 CreateNode('stability', 3, [ 299 FindAndClone(stability, 'release'), 300 FindAndClone(stability, 'api') 301 ]), 302 FindAndClone(root, 'date'), 303 FindAndClone(root, 'time'), 304 FindAndClone(root, 'license'), 305 FindAndClone(root, 'notes') 306 ]) 307 changelog.appendChild(release) 308 changelog.appendChild(document.createTextNode('\n ')) 309 RewriteXml('php/ext/google/protobuf/package.xml', Callback) 310 RewriteTextFile('php/ext/google/protobuf/protobuf.h', 311 lambda line : re.sub( 312 r'PHP_PROTOBUF_VERSION ".*"$', 313 'PHP_PROTOBUF_VERSION "%s"' % NEW_VERSION, 314 line)) 315 316 RewriteTextFile('php/ext/google/protobuf/protobuf.h', 317 lambda line : re.sub( 318 r"^#define PHP_PROTOBUF_VERSION .*$", 319 "#define PHP_PROTOBUF_VERSION \"%s\"" % GetFullVersion(rc_suffix = 'RC'), 320 line)) 321 322 RewriteTextFile('php/ext/google/protobuf/protobuf.h', 323 lambda line : re.sub( 324 r"^#define PHP_PROTOBUF_VERSION .*$", 325 "#define PHP_PROTOBUF_VERSION \"%s\"" % GetFullVersion(rc_suffix = 'RC'), 326 line)) 327 328def UpdatePython(): 329 RewriteTextFile('python/google/protobuf/__init__.py', 330 lambda line : re.sub( 331 r"^__version__ = '.*'$", 332 "__version__ = '%s'" % GetFullVersion(rc_suffix = 'rc'), 333 line)) 334 335def UpdateRuby(): 336 RewriteTextFile('ruby/google-protobuf.gemspec', 337 lambda line : re.sub( 338 r'^ s.version = ".*"$', 339 ' s.version = "%s"' % GetFullVersion(rc_suffix = '.rc.'), 340 line)) 341 342 343UpdateConfigure() 344UpdateCsharp() 345UpdateCpp() 346UpdateJava() 347UpdateJavaScript() 348UpdateMakefile() 349UpdateObjectiveC() 350UpdatePhp() 351UpdatePython() 352UpdateRuby() 353