summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--BuildTools/DocBook/SCons/DocBook.py21
-rw-r--r--BuildTools/SCons/Tools/textfile.py179
-rw-r--r--BuildTools/SCons/Version.py72
-rw-r--r--Documentation/SwiftenDevelopersGuide/SConscript35
-rw-r--r--Swift/QtUI/SConscript4
-rw-r--r--Swiften/SConscript2
6 files changed, 72 insertions, 241 deletions
diff --git a/BuildTools/DocBook/SCons/DocBook.py b/BuildTools/DocBook/SCons/DocBook.py
index ffb0bfc..d7c95ba 100644
--- a/BuildTools/DocBook/SCons/DocBook.py
+++ b/BuildTools/DocBook/SCons/DocBook.py
@@ -5,6 +5,19 @@
5import SCons.Util, SCons.Action 5import SCons.Util, SCons.Action
6import xml.dom.minidom, re, os.path, sys 6import xml.dom.minidom, re, os.path, sys
7 7
8def maybeBytesToString(s):
9 if isinstance(s, bytes):
10 return s.decode('utf-8')
11 return s
12
13def prepareForWrite(s):
14 try:
15 if isinstance(s, unicode):
16 return s.encode('utf-8')
17 except NameError:
18 pass
19 return s
20
8def generate(env) : 21def generate(env) :
9 # Location of stylesheets and catalogs 22 # Location of stylesheets and catalogs
10 docbook_dir = "#/BuildTools/DocBook" 23 docbook_dir = "#/BuildTools/DocBook"
@@ -32,15 +45,15 @@ def generate(env) :
32 rewritePrefix="%(docbook_xsl_dir)s/" /> 45 rewritePrefix="%(docbook_xsl_dir)s/" />
33</catalog>""" 46</catalog>"""
34 47
35 docbook_xml_dir = source[0].get_contents() 48 docbook_xml_dir = maybeBytesToString(source[0].get_contents())
36 docbook_xsl_dir = source[1].get_contents() 49 docbook_xsl_dir = maybeBytesToString(source[1].get_contents())
37 if env["PLATFORM"] == "win32" : 50 if env["PLATFORM"] == "win32" :
38 docbook_xml_dir = docbook_xml_dir.replace("\\","/") 51 docbook_xml_dir = docbook_xml_dir.replace("\\","/")
39 docbook_xsl_dir = docbook_xsl_dir.replace("\\","/") 52 docbook_xsl_dir = docbook_xsl_dir.replace("\\","/")
40 file = open(target[0].abspath, "w") 53 file = open(target[0].abspath, "w")
41 file.write(catalog % { 54 file.write(catalog % {
42 "docbook_xml_dir" : docbook_xml_dir, 55 "docbook_xml_dir" : prepareForWrite(docbook_xml_dir),
43 "docbook_xsl_dir" : docbook_xsl_dir, 56 "docbook_xsl_dir" : prepareForWrite(docbook_xsl_dir),
44 }) 57 })
45 file.close() 58 file.close()
46 59
diff --git a/BuildTools/SCons/Tools/textfile.py b/BuildTools/SCons/Tools/textfile.py
deleted file mode 100644
index 73105ad..0000000
--- a/BuildTools/SCons/Tools/textfile.py
+++ /dev/null
@@ -1,179 +0,0 @@
1# -*- python -*-
2#
3# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation
4#
5# Permission is hereby granted, free of charge, to any person obtaining
6# a copy of this software and associated documentation files (the
7# "Software"), to deal in the Software without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish,
9# distribute, sublicense, and/or sell copies of the Software, and to
10# permit persons to whom the Software is furnished to do so, subject to
11# the following conditions:
12#
13# The above copyright notice and this permission notice shall be included
14# in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23#
24
25__doc__ = """
26Textfile/Substfile builder for SCons.
27
28 Create file 'target' which typically is a textfile. The 'source'
29 may be any combination of strings, Nodes, or lists of same. A
30 'linesep' will be put between any part written and defaults to
31 os.linesep.
32
33 The only difference between the Textfile builder and the Substfile
34 builder is that strings are converted to Value() nodes for the
35 former and File() nodes for the latter. To insert files in the
36 former or strings in the latter, wrap them in a File() or Value(),
37 respectively.
38
39 The values of SUBST_DICT first have any construction variables
40 expanded (its keys are not expanded). If a value of SUBST_DICT is
41 a python callable function, it is called and the result is expanded
42 as the value. Values are substituted in a "random" order; if any
43 substitution could be further expanded by another subsitition, it
44 is unpredictible whether the expansion will occur.
45"""
46
47__revision__ = "src/engine/SCons/Tool/textfile.py 5357 2011/09/09 21:31:03 bdeegan"
48
49import SCons
50
51import os
52import re
53
54from SCons.Node import Node
55from SCons.Node.Python import Value
56from SCons.Util import is_String, is_Sequence, is_Dict
57
58def _do_subst(node, subs):
59 """
60 Fetch the node contents and replace all instances of the keys with
61 their values. For example, if subs is
62 {'%VERSION%': '1.2345', '%BASE%': 'MyProg', '%prefix%': '/bin'},
63 then all instances of %VERSION% in the file will be replaced with
64 1.2345 and so forth.
65 """
66 contents = node.get_contents().decode('utf-8')
67 if not subs: return contents
68 for (k,v) in subs:
69 contents = re.sub(k, v, contents)
70 return contents
71
72def _action(target, source, env):
73 # prepare the line separator
74 linesep = env['LINESEPARATOR']
75 if linesep is None:
76 linesep = os.linesep
77 elif is_String(linesep):
78 pass
79 elif isinstance(linesep, Value):
80 linesep = linesep.get_text_contents()
81 else:
82 raise SCons.Errors.UserError(
83 'unexpected type/class for LINESEPARATOR: %s'
84 % repr(linesep), None)
85
86 # create a dictionary to use for the substitutions
87 if 'SUBST_DICT' not in env:
88 subs = None # no substitutions
89 else:
90 d = env['SUBST_DICT']
91 if is_Dict(d):
92 d = list(d.items())
93 elif is_Sequence(d):
94 pass
95 else:
96 raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence')
97 subs = []
98 for (k,v) in d:
99 if callable(v):
100 v = v()
101 if is_String(v):
102 v = env.subst(v)
103 else:
104 v = str(v)
105 subs.append((k,v))
106
107 # write the file
108 try:
109 fd = open(target[0].get_path(), "wb")
110 except (OSError,IOError) as e:
111 raise SCons.Errors.UserError("Can't write target file %s" % target[0])
112 # separate lines by 'linesep' only if linesep is not empty
113 lsep = None
114 for s in source:
115 if lsep: fd.write(lsep)
116 stringtowrite = _do_subst(s, subs)
117 if isinstance(stringtowrite, str):
118 fd.write(stringtowrite)
119 elif isinstance(stringtowrite, unicode):
120 fd.write(stringtowrite.encode('utf-8'))
121 lsep = linesep
122 fd.close()
123
124def _strfunc(target, source, env):
125 return "Creating '%s'" % target[0]
126
127def _convert_list_R(newlist, sources):
128 for elem in sources:
129 if is_Sequence(elem):
130 _convert_list_R(newlist, elem)
131 elif isinstance(elem, Node):
132 newlist.append(elem)
133 else:
134 newlist.append(Value(elem))
135def _convert_list(target, source, env):
136 if len(target) != 1:
137 raise SCons.Errors.UserError("Only one target file allowed")
138 newlist = []
139 _convert_list_R(newlist, source)
140 return target, newlist
141
142_common_varlist = ['SUBST_DICT', 'LINESEPARATOR']
143
144_text_varlist = _common_varlist + ['TEXTFILEPREFIX', 'TEXTFILESUFFIX']
145_text_builder = SCons.Builder.Builder(
146 action = SCons.Action.Action(_action, _strfunc, varlist = _text_varlist),
147 source_factory = Value,
148 emitter = _convert_list,
149 prefix = '$TEXTFILEPREFIX',
150 suffix = '$TEXTFILESUFFIX',
151 )
152
153_subst_varlist = _common_varlist + ['SUBSTFILEPREFIX', 'TEXTFILESUFFIX']
154_subst_builder = SCons.Builder.Builder(
155 action = SCons.Action.Action(_action, _strfunc, varlist = _subst_varlist),
156 source_factory = SCons.Node.FS.File,
157 emitter = _convert_list,
158 prefix = '$SUBSTFILEPREFIX',
159 suffix = '$SUBSTFILESUFFIX',
160 src_suffix = ['.in'],
161 )
162
163def generate(env):
164 env['LINESEPARATOR'] = os.linesep
165 env['BUILDERS']['MyTextfile'] = _text_builder
166 env['TEXTFILEPREFIX'] = ''
167 env['TEXTFILESUFFIX'] = '.txt'
168 env['BUILDERS']['MySubstfile'] = _subst_builder
169 env['SUBSTFILEPREFIX'] = ''
170 env['SUBSTFILESUFFIX'] = ''
171
172def exists(env):
173 return 1
174
175# Local Variables:
176# tab-width:4
177# indent-tabs-mode:nil
178# End:
179# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/BuildTools/SCons/Version.py b/BuildTools/SCons/Version.py
index 001374a..23cd850 100644
--- a/BuildTools/SCons/Version.py
+++ b/BuildTools/SCons/Version.py
@@ -1,26 +1,34 @@
1from __future__ import print_function
1import subprocess, os, datetime, re, os.path, sys, unittest 2import subprocess, os, datetime, re, os.path, sys, unittest
2 3
3def getGitBuildVersion(root, project) : 4def gitDescribeToVersion(tag, tagPrefix):
4 tag = git("describe --tags --exact --match \"" + project + "-*\"", root) 5 m = re.match(r'^' + tagPrefix + r'-(.+?)(?:-(.+?)-(.+?))?$', tag)
5 if tag : 6 if not m:
6 return tag.rstrip()[len(project)+1:] 7 return None
7 tag = git("describe --tags --match \"" + project + "-*\"", root) 8 result = m.group(1)
8 if tag : 9 if m.group(2):
9 m = re.match(project + "-(.*)-(.*)-(.*)", tag) 10 result += "-dev" + m.group(2)
10 if m : 11 return result
11 return m.group(1) + "-dev" + m.group(2) 12
13def getGitBuildVersion(root, tagPrefix) :
14 tag = git("describe --tags --match \"" + tagPrefix + "-*\"", root)
15 if tag:
16 return gitDescribeToVersion(tag, tagPrefix)
12 return None 17 return None
13 18
14def git(cmd, root) : 19def git(cmd, root):
15 full_cmd = "git " + cmd 20 full_cmd = "git " + cmd
21 # Would've used with .. as p, but that's not supported by Python 2.7
16 p = subprocess.Popen(full_cmd, cwd=root, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt")) 22 p = subprocess.Popen(full_cmd, cwd=root, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt"))
17 gitVersion = p.stdout.read() 23 gitVersion = p.stdout.read()
18 # error = p.stderr.read() 24 try:
19 # if error: 25 p.stdin.close()
20 # print("Git error: " + error) 26 p.stdout.close()
21 p.stdin.close() 27 p.stderr.close()
22 if p.wait() == 0 : 28 except:
23 return gitVersion 29 pass
30 if p.wait() == 0:
31 return bytes(gitVersion).decode('utf-8')
24 return None 32 return None
25 33
26def getBuildVersion(root, project) : 34def getBuildVersion(root, project) :
@@ -95,7 +103,7 @@ def convertToWindowsVersion(version):
95 return (major, minor, patch) 103 return (major, minor, patch)
96 104
97# Test Windows version mapping scheme 105# Test Windows version mapping scheme
98class convertToWindowsVersionTest(unittest.TestCase): 106class TestCases(unittest.TestCase):
99 def testWindowsVersionsAreDescending(self): 107 def testWindowsVersionsAreDescending(self):
100 versionStringsWithOldVersions = [ 108 versionStringsWithOldVersions = [
101 ("5.0rc11", None), 109 ("5.0rc11", None),
@@ -111,13 +119,13 @@ class convertToWindowsVersionTest(unittest.TestCase):
111 ("4.0rc5", (4, 0, 50000)), 119 ("4.0rc5", (4, 0, 50000)),
112 ("4.0rc4", (4, 0, 40000)), 120 ("4.0rc4", (4, 0, 40000)),
113 ("4.0rc3", (4, 0, 30000)), 121 ("4.0rc3", (4, 0, 30000)),
122 ('4.0rc2-dev39', (4, 0, 20039)),
114 ('4.0rc2-dev34', (4, 0, 20034)), 123 ('4.0rc2-dev34', (4, 0, 20034)),
115 ('4.0rc2-dev33', (4, 0, 20033)), 124 ('4.0rc2-dev33', (4, 0, 20033)),
116 ('4.0rc2-dev31', (4, 0, 20031)), 125 ('4.0rc2-dev31', (4, 0, 20031)),
117 ('4.0rc2-dev30', (4, 0, 20030)), 126 ('4.0rc2-dev30', (4, 0, 20030)),
118 ('4.0rc2-dev29', (4, 0, 20029)), 127 ('4.0rc2-dev29', (4, 0, 20029)),
119 ('4.0rc2-dev27', (4, 0, 20027)), 128 ('4.0rc2-dev27', (4, 0, 20027)),
120 ('4.0rc2-dev39', (4, 0, 20039)),
121 ('4.0rc2', (4, 0, 20000)), 129 ('4.0rc2', (4, 0, 20000)),
122 ('4.0rc1', (4, 0, 10000)), 130 ('4.0rc1', (4, 0, 10000)),
123 ('4.0beta2-dev203', (4, 0, 2203)), 131 ('4.0beta2-dev203', (4, 0, 2203)),
@@ -163,7 +171,17 @@ class convertToWindowsVersionTest(unittest.TestCase):
163 ('3.0alpha-dev524', (3, 0, 524)), 171 ('3.0alpha-dev524', (3, 0, 524)),
164 ('3.0alpha-dev515', (3, 0, 515)), 172 ('3.0alpha-dev515', (3, 0, 515)),
165 ] 173 ]
166 windowsVersionMapping = list(map(lambda (x,y): (x, convertToWindowsVersion(x)), versionStringsWithOldVersions)) 174 prevParsed = None
175 prevOldVersion = None
176 for string, oldVersion in versionStringsWithOldVersions:
177 parsed = convertToWindowsVersion(string)
178 if prevOldVersion and oldVersion:
179 self.assertTrue(oldVersion <= prevOldVersion, "Old version %r must come before %r" % (oldVersion, prevOldVersion))
180 if prevParsed:
181 self.assertTrue(parsed < prevParsed, "%s => %r must be smaller than %s => %r" % (string, parsed, prevString, prevParsed))
182 prevString = string
183 prevParsed = parsed
184 prevOldVersion = oldVersion
167 185
168 def testThatBetaIsHigherThanAlpha(self): 186 def testThatBetaIsHigherThanAlpha(self):
169 self.assertTrue(convertToWindowsVersion("3.0beta0") > convertToWindowsVersion("3.0alpha0")) 187 self.assertTrue(convertToWindowsVersion("3.0beta0") > convertToWindowsVersion("3.0alpha0"))
@@ -190,12 +208,24 @@ class convertToWindowsVersionTest(unittest.TestCase):
190 self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0rc1")) 208 self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0rc1"))
191 self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0rc11")) 209 self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0rc11"))
192 210
211 def testGitDescribeToVersion(self):
212 self.assertEqual("5.0alpha2-dev100", gitDescribeToVersion("swift-5.0alpha2-100-g33d5f6571", "swift"))
213 self.assertEqual("5.0alpha2", gitDescribeToVersion("swift-5.0alpha2", "swift"))
214 self.assertEqual("5.0-dev1", gitDescribeToVersion("swift-5.0-1-g012312312", "swift"))
215 self.assertIsNone(gitDescribeToVersion("swiften-5.0-1-g012312312", "swift"))
216 self.assertIsNone(gitDescribeToVersion("quickly-5.0-1-g012312312", "swift"))
217 self.assertIsNone(gitDescribeToVersion("swift-", "swift"))
218 self.assertIsNone(gitDescribeToVersion("swift", "swift"))
219
193if __name__ == '__main__': 220if __name__ == '__main__':
194 if len(sys.argv) == 1: 221 if len(sys.argv) == 1:
195 unittest.main() 222 unittest.main()
196 elif len(sys.argv) == 2: 223 elif len(sys.argv) == 2:
197 print convertToWindowsVersion(sys.argv[1]) 224 if sys.argv[1] == "--git-build-version":
225 print(getGitBuildVersion(os.path.dirname(os.path.dirname(__file__)), "swift"))
226 else:
227 print(convertToWindowsVersion(sys.argv[1]))
198 sys.exit(0) 228 sys.exit(0)
199 else: 229 else:
200 print "Error: Simply run the script without arguments or pass a single argument." 230 print("Error: Simply run the script without arguments or pass a single argument.")
201 sys.exit(-1) 231 sys.exit(-1)
diff --git a/Documentation/SwiftenDevelopersGuide/SConscript b/Documentation/SwiftenDevelopersGuide/SConscript
index ac7c67a..95e5c87 100644
--- a/Documentation/SwiftenDevelopersGuide/SConscript
+++ b/Documentation/SwiftenDevelopersGuide/SConscript
@@ -9,36 +9,6 @@ env.Tool("DocBook", toolpath = ["#/BuildTools/DocBook/SCons"])
9import sys, re, os.path 9import sys, re, os.path
10 10
11def generateDocBookCode(env, target, source) : 11def generateDocBookCode(env, target, source) :
12 # Strips empty lines from the beginning & end of a program
13 def stripEmptyLines(program) :
14 programLines = program.split('\n')
15 newProgramLines = []
16 inProgram = False
17 for line in programLines :
18 if not re.match("^\s*$", line) or inProgram :
19 inProgram = True
20 newProgramLines.append(line)
21 return '\n'.join(newProgramLines).rstrip()
22
23 def createCallouts(program, calloutPrefix) :
24 newProgramLines = []
25 calloutLines = []
26 nextID = 0
27 for line in program.split("\n") :
28 # FIXME: Takes the largest match
29 m = re.match(".*\/* \(\*\) (.*) \*/.*", line)
30 if m :
31 cobID = "cob-" + calloutPrefix + "-" + str(nextID)
32 coID = "co-" + calloutPrefix + "-" + str(nextID)
33 nextID += 1
34 line = re.sub("/\*.*\*/", "]]><co id=\"%(cobID)s\" linkends=\"%(coID)s\"/><![CDATA[" % {"cobID" : cobID, "coID" : coID}, line)
35 calloutLines.append("<callout arearefs=\"%(cobID)s\" id=\"%(coID)s\"><para>%(text)s</para></callout>" % {"cobID": cobID, "coID": coID, "text": m.group(1)})
36 newProgramLines.append(line)
37 callouts = ""
38 if len(calloutLines) > 0 :
39 callouts = "<calloutlist>" + "\n".join(calloutLines) + "</calloutlist>"
40 return ("\n".join(newProgramLines), callouts)
41
42 # Parse program 12 # Parse program
43 filename = source[0].abspath 13 filename = source[0].abspath
44 filenameBase = os.path.basename(filename).replace(".cpp", "") 14 filenameBase = os.path.basename(filename).replace(".cpp", "")
@@ -71,10 +41,7 @@ def generateDocBookCode(env, target, source) :
71 inputfile.close() 41 inputfile.close()
72 42
73 for programName, program in programs.items() : 43 for programName, program in programs.items() :
74 program = stripEmptyLines(program) 44 document = "<foo><programlisting><![CDATA[" + program.strip() + "]]></programlisting></foo>"
75 (program, callouts) = createCallouts(program, filenameBase + "-" + programName)
76
77 document = "<foo><programlisting><![CDATA[" + program + "]]></programlisting>" + callouts + "</foo>"
78 45
79 # Generate code 46 # Generate code
80 output = open(target[0].abspath, 'w') 47 output = open(target[0].abspath, 'w')
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index a2ad9b1..584cfea 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -24,7 +24,7 @@ def generateQRCTheme(dir, prefix) :
24 24
25Import("env") 25Import("env")
26 26
27myenv = env.Clone() 27myenv = env.Clone(tools = [ 'textfile' ])
28 28
29# Disable warnings that affect Qt 29# Disable warnings that affect Qt
30myenv["CXXFLAGS"] = list(filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"])) 30myenv["CXXFLAGS"] = list(filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"]))
@@ -365,7 +365,7 @@ myenv["TEXTFILESUFFIX"] = ""
365copying_files = [myenv.File("../../COPYING.gpl"), myenv.File("../../COPYING.thirdparty"), myenv.File("../../COPYING.dependencies")] 365copying_files = [myenv.File("../../COPYING.gpl"), myenv.File("../../COPYING.thirdparty"), myenv.File("../../COPYING.dependencies")]
366if env["PLATFORM"] == "darwin" and env["HAVE_SPARKLE"] : 366if env["PLATFORM"] == "darwin" and env["HAVE_SPARKLE"] :
367 copying_files.append(env["SPARKLE_COPYING"]) 367 copying_files.append(env["SPARKLE_COPYING"])
368myenv.MyTextfile(target = "COPYING", source = copying_files, LINESEPARATOR = "\n\n========\n\n\n") 368myenv.Textfile(target = "COPYING", source = copying_files, LINESEPARATOR = "\n\n========\n\n\n")
369 369
370################################################################################ 370################################################################################
371# Translation 371# Translation
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 4deddaf..5705113 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -12,7 +12,7 @@ external_swiften_dep_modules = ["BOOST"]
12if env["SCONS_STAGE"] == "flags" : 12if env["SCONS_STAGE"] == "flags" :
13 env["SWIFTEN_DLL"] = env["swiften_dll"] 13 env["SWIFTEN_DLL"] = env["swiften_dll"]
14 env["SWIFTEN_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift") 14 env["SWIFTEN_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
15 version_match = re.match("(\d+)\.(\d+).*", env["SWIFTEN_VERSION"]) 15 version_match = re.match(r"(\d+)\.(\d+).*", env["SWIFTEN_VERSION"])
16 if version_match : 16 if version_match :
17 env["SWIFTEN_VERSION_MAJOR"] = int(version_match.group(1)) 17 env["SWIFTEN_VERSION_MAJOR"] = int(version_match.group(1))
18 env["SWIFTEN_VERSION_MINOR"] = int(version_match.group(2)) 18 env["SWIFTEN_VERSION_MINOR"] = int(version_match.group(2))