-
Notifications
You must be signed in to change notification settings - Fork 463
/
gen_version.py
executable file
·192 lines (158 loc) · 6.51 KB
/
gen_version.py
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env python3
#
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Generate version string for NotoColorEmoji.
This parses the color emoji template file and updates the lines
containing version string info, writing a new file.
The nameID 5 field in the emoji font should reflect the commit/date
of the repo it was built from. This will build a string of the following
format:
Version 1.39;GOOG;noto-emoji:20170220:a8a215d2e889'
This is intended to indicate that it was built by Google from noto-emoji
at commit a8a215d2e889 and date 20170220 (since dates are a bit easier
to locate in time than commit hashes).
For building with external data we don't include the commit id as we
might be using different resources. Instead the version string is:
Version 1.39;GOOG;noto-emoji:20170518;BETA <msg>
Here the date is the current date, and the message after 'BETA ' is
provided using the '-b' flag. There's no commit hash. This also
bypasses some checks about the state of the repo.
The release number should have 2 or 3 minor digits. Right now we've been
using 2 but at the next major release we probably want to use 3. This
supports both. It will bump the version number if none is provided,
maintaining the minor digit length.
"""
import argparse
import datetime
import re
from nototools import tool_utils
# These are not very lenient, we expect to be applied to the noto color
# emoji template ttx file which matches these. Why then require the
# input argument, you ask? Um... testing?
_nameid_re = re.compile(r'\s*<namerecord nameID="5"')
_version_re = re.compile(r'\s*Version\s(\d+.\d{2,3})')
_headrev_re = re.compile(r'\s*<fontRevision value="(\d+.\d{2,3})"/>')
def _get_existing_version(lines):
"""Scan lines for all existing version numbers, and ensure they match.
Return the matched version number string."""
version = None
def check_version(new_version):
if version is not None and new_version != version:
raise Exception(
'version %s and namerecord version %s do not match' % (
version, new_version))
return new_version
saw_nameid = False
for line in lines:
if saw_nameid:
saw_nameid = False
m = _version_re.match(line)
if not m:
raise Exception('could not match line "%s" in namerecord' % line)
version = check_version(m.group(1))
elif _nameid_re.match(line):
saw_nameid = True
else:
m = _headrev_re.match(line)
if m:
version = check_version(m.group(1))
return version
def _version_to_mm(version):
majs, mins = version.split('.')
minor_len = len(mins)
return int(majs), int(mins), minor_len
def _mm_to_version(major, minor, minor_len):
fmt = '%%d.%%0%dd' % minor_len
return fmt % (major, minor)
def _version_compare(lhs, rhs):
lmaj, lmin, llen = _version_to_mm(lhs)
rmaj, rmin, rlen = _version_to_mm(rhs)
# if major versions differ, we don't care about the minor length, else
# they should be the same
if lmaj != rmaj:
return lmaj - rmaj
if llen != rlen:
raise Exception('minor version lengths differ: "%s" and "%s"' % (lhs, rhs))
return lmin - rmin
def _version_bump(version):
major, minor, minor_len = _version_to_mm(version)
minor = (minor + 1) % (10 ** minor_len)
if minor == 0:
raise Exception('cannot bump version "%s", requires new major' % version)
return _mm_to_version(major, minor, minor_len)
def _get_repo_version_str(beta):
"""See above for description of this string."""
if beta is not None:
date_str = datetime.date.today().strftime('%Y%m%d')
return 'GOOG;noto-emoji:%s;BETA %s' % (date_str, beta)
p = tool_utils.resolve_path('[emoji]')
commit, date, _ = tool_utils.git_head_commit(p)
if not tool_utils.git_check_remote_commit(p, commit):
raise Exception('emoji not on upstream master branch')
date_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})')
m = date_re.match(date)
if not m:
raise Exception('could not match "%s" with "%s"' % (date, date_re.pattern))
ymd = ''.join(m.groups())
return 'GOOG;noto-emoji:%s:%s' % (ymd, commit[:12])
def _replace_existing_version(lines, version, version_str):
"""Update lines with new version strings in appropriate places."""
saw_nameid = False
for i in range(len(lines)):
line = lines[i]
if saw_nameid:
saw_nameid = False
# preserve indentation
lead_ws = len(line) - len(line.lstrip())
lines[i] = line[:lead_ws] + version_str + '\n'
elif _nameid_re.match(line):
saw_nameid = True
elif _headrev_re.match(line):
lead_ws = len(line) - len(line.lstrip())
lines[i] = line[:lead_ws] + '<fontRevision value="%s"/>\n' % version
def update_version(srcfile, dstfile, version, beta):
"""Update version in srcfile and write to dstfile. If version is None,
bumps the current version, else version must be greater than the
current version."""
with open(srcfile, 'r') as f:
lines = f.readlines()
current_version = _get_existing_version(lines)
if not version:
version = _version_bump(current_version)
elif version and _version_compare(version, current_version) <= 0:
raise Exception('new version %s is <= current version %s' % (
version, current_version))
version_str = 'Version %s;%s' % (version, _get_repo_version_str(beta))
_replace_existing_version(lines, version, version_str)
with open(dstfile, 'w') as f:
for line in lines:
f.write(line)
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'-v', '--version', help='version number, default bumps the current '
'version', metavar='ver')
parser.add_argument(
'-s', '--src', help='ttx file with name and head tables',
metavar='file', required=True)
parser.add_argument(
'-d', '--dst', help='name of edited ttx file to write',
metavar='file', required=True)
parser.add_argument(
'-b', '--beta', help='beta tag if font is built using external resources')
args = parser.parse_args()
update_version(args.src, args.dst, args.version, args.beta)
if __name__ == '__main__':
main()