update_versions.py (3864B)
1 #!/usr/bin/env python 2 3 # Future imports for Python 2.7, mandatory in 3.0 4 from __future__ import division 5 from __future__ import print_function 6 from __future__ import unicode_literals 7 8 import io 9 import os 10 import re 11 import sys 12 import time 13 14 def P(path): 15 """ 16 Give 'path' as a path relative to the abs_top_srcdir environment 17 variable. 18 """ 19 return os.path.join( 20 os.environ.get('abs_top_srcdir', "."), 21 path) 22 23 def warn(msg): 24 """ 25 Print an warning message. 26 """ 27 print("WARNING: {}".format(msg), file=sys.stderr) 28 29 def find_version(infile): 30 """ 31 Given an open file (or some other iterator of lines) holding a 32 configure.ac file, find the current version line. 33 """ 34 for line in infile: 35 m = re.search(r'AC_INIT\(\[tor\],\s*\[([^\]]*)\]\)', line) 36 if m: 37 return m.group(1) 38 39 return None 40 41 def update_version_in(infile, outfile, regex, versionline): 42 """ 43 Copy every line from infile to outfile. If any line matches 'regex', 44 replace it with 'versionline'. Return True if any line was changed; 45 false otherwise. 46 47 'versionline' is either a string -- in which case it is used literally, 48 or a function that receives the output of 'regex.match'. 49 """ 50 found = False 51 have_changed = False 52 for line in infile: 53 m = regex.match(line) 54 if m: 55 found = True 56 oldline = line 57 if type(versionline) == type(u""): 58 line = versionline 59 else: 60 line = versionline(m) 61 if not line.endswith("\n"): 62 line += "\n" 63 if oldline != line: 64 have_changed = True 65 outfile.write(line) 66 67 if not found: 68 warn("didn't find any version line to replace in {}".format(infile.name)) 69 70 return have_changed 71 72 def replace_on_change(fname, change): 73 """ 74 If "change" is true, replace fname with fname.tmp. Otherwise, 75 delete fname.tmp. Log what we're doing to stderr. 76 """ 77 if not change: 78 print("No change in {}".format(fname)) 79 os.unlink(fname+".tmp") 80 else: 81 print("Updating {}".format(fname)) 82 os.rename(fname+".tmp", fname) 83 84 85 def update_file(fname, 86 regex, 87 versionline, 88 encoding="utf-8"): 89 """ 90 Replace any line matching 'regex' in 'fname' with 'versionline'. 91 Do not modify 'fname' if there are no changes made. Use the 92 provided encoding to read and write. 93 """ 94 with io.open(fname, "r", encoding=encoding) as f, \ 95 io.open(fname+".tmp", "w", encoding=encoding) as outf: 96 have_changed = update_version_in(f, outf, regex, versionline) 97 98 replace_on_change(fname, have_changed) 99 100 # Find out our version 101 with open(P("configure.ac")) as f: 102 version = find_version(f) 103 104 # If we have no version, we can't proceed. 105 if version == None: 106 print("No version found in configure.ac", file=sys.stderr()) 107 sys.exit(1) 108 109 print("The version is {}".format(version)) 110 111 today = time.strftime("%Y-%m-%d", time.gmtime()) 112 113 # In configure.ac, we replace the definition of APPROX_RELEASE_DATE 114 # with "{today} for {version}", but only if the version does not match 115 # what is already there. 116 def replace_fn(m): 117 if m.group(1) != version: 118 # The version changed -- we change the date. 119 return u'AC_DEFINE(APPROX_RELEASE_DATE, ["{}"], # for {}'.format(today, version) 120 else: 121 # No changes. 122 return m.group(0) 123 update_file(P("configure.ac"), 124 re.compile(r'AC_DEFINE\(APPROX_RELEASE_DATE.* for (.*)'), 125 replace_fn) 126 127 # In tor-mingw.nsi.in, we replace the definition of VERSION. 128 update_file(P("contrib/win32build/tor-mingw.nsi.in"), 129 re.compile(r'!define VERSION .*'), 130 u'!define VERSION "{}"'.format(version), 131 encoding="iso-8859-1")