aboutsummaryrefslogtreecommitdiff
path: root/scripts/maint/update_versions.py
blob: 07de1c343a0b99630d3b1376e9627b914dae41ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env python

# Future imports for Python 2.7, mandatory in 3.0
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import io
import os
import re
import sys
import time

def P(path):
    """
    Give 'path' as a path relative to the abs_top_srcdir environment
    variable.
    """
    return os.path.join(
        os.environ.get('abs_top_srcdir', "."),
        path)

def warn(msg):
    """
    Print an warning message.
    """
    print("WARNING: {}".format(msg), file=sys.stderr)

def find_version(infile):
    """
    Given an open file (or some other iterator of lines) holding a
    configure.ac file, find the current version line.
    """
    for line in infile:
        m = re.search(r'AC_INIT\(\[tor\],\s*\[([^\]]*)\]\)', line)
        if m:
            return m.group(1)

    return None

def update_version_in(infile, outfile, regex, versionline):
    """
    Copy every line from infile to outfile. If any line matches 'regex',
    replace it with 'versionline'.  Return True if any line was changed;
    false otherwise.

    'versionline' is either a string -- in which case it is used literally,
    or a function that receives the output of 'regex.match'.
    """
    found = False
    have_changed = False
    for line in infile:
        m = regex.match(line)
        if m:
            found = True
            oldline = line
            if type(versionline) == type(u""):
                line = versionline
            else:
                line = versionline(m)
            if not line.endswith("\n"):
                line += "\n"
            if oldline != line:
                have_changed = True
        outfile.write(line)

    if not found:
        warn("didn't find any version line to replace in {}".format(infile.name))

    return have_changed

def replace_on_change(fname, change):
    """
    If "change" is true, replace fname with fname.tmp.  Otherwise,
    delete fname.tmp.  Log what we're doing to stderr.
    """
    if not change:
        print("No change in {}".format(fname))
        os.unlink(fname+".tmp")
    else:
        print("Updating {}".format(fname))
        os.rename(fname+".tmp", fname)


def update_file(fname,
                regex,
                versionline,
                encoding="utf-8"):
    """
    Replace any line matching 'regex' in 'fname' with 'versionline'.
    Do not modify 'fname' if there are no changes made.  Use the
    provided encoding to read and write.
    """
    with io.open(fname, "r", encoding=encoding) as f, \
         io.open(fname+".tmp", "w", encoding=encoding) as outf:
        have_changed = update_version_in(f, outf, regex, versionline)

    replace_on_change(fname, have_changed)

# Find out our version
with open(P("configure.ac")) as f:
    version = find_version(f)

# If we have no version, we can't proceed.
if version == None:
    print("No version found in configure.ac", file=sys.stderr())
    sys.exit(1)

print("The version is {}".format(version))

today = time.strftime("%Y-%m-%d", time.gmtime())

# In configure.ac, we replace the definition of APPROX_RELEASE_DATE
# with "{today} for {version}", but only if the version does not match
# what is already there.
def replace_fn(m):
    if m.group(1) != version:
        # The version changed -- we change the date.
        return u'AC_DEFINE(APPROX_RELEASE_DATE, ["{}"], # for {}'.format(today, version)
    else:
        # No changes.
        return m.group(0)
update_file(P("configure.ac"),
            re.compile(r'AC_DEFINE\(APPROX_RELEASE_DATE.* for (.*)'),
            replace_fn)

# In tor-mingw.nsi.in, we replace the definition of VERSION.
update_file(P("contrib/win32build/tor-mingw.nsi.in"),
            re.compile(r'!define VERSION .*'),
            u'!define VERSION "{}"'.format(version),
            encoding="iso-8859-1")

# In src/win32/orconfig.h, we replace the definition of VERSION.
update_file(P("src/win32/orconfig.h"),
            re.compile(r'#define VERSION .*'),
            u'#define VERSION "{}"'.format(version))