bug 1257607 - Add Version type to moz.configure. r?glandium draft
authorTed Mielczarek <ted@mielczarek.org>
Thu, 17 Mar 2016 11:52:18 -0400
changeset 342079 da97a4f16d5c5d6a0961e167646245b47b4ffa00
parent 342076 f67ab46c2ac00a2a95cfc67e9763ac12b690ac14
child 516519 0dd780c16486ec5461fd2dff4f9614ae699933e7
push id13349
push usertmielczarek@mozilla.com
push dateFri, 18 Mar 2016 12:44:50 +0000
reviewersglandium
bugs1257607
milestone48.0a1
bug 1257607 - Add Version type to moz.configure. r?glandium This change adds a `Version` type to moz.configure which is a small wrapper around `distutils.version.Version`. It's suitable for wrapping version numbers in configure checks and doing equality or greater-than less-than comparisons in a sensible way. MozReview-Commit-ID: BOL6yvemulG
build/moz.configure/util.configure
moz.configure
python/moz.build
python/mozbuild/mozbuild/configure/util.py
python/mozbuild/mozbuild/test/configure/test_util.py
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -79,16 +79,22 @@ del _defines
 @template
 def unique_list(l):
     result = []
     for i in l:
         if l not in result:
             result.append(i)
     return result
 
+@template
+@advanced
+def Version(v):
+    'A version number that can be compared usefully.'
+    from mozbuild.configure.util import Version as _Version
+    return _Version(v)
 
 # Denotes a deprecated option. Combines option() and @depends:
 # @deprecated_option('--option')
 # def option(value):
 #     ...
 # @deprecated_option() takes the same arguments as option(), except `help`.
 # The function may handle the option like a typical @depends function would,
 # but it is recommended it emits a deprecation error message suggesting an
--- a/moz.configure
+++ b/moz.configure
@@ -79,17 +79,17 @@ def perl_for_old_configure(value):
 @template
 def perl_version_check(min_version):
     @depends(perl)
     @checking('for minimum required perl version >= %s' % min_version)
     @advanced
     def get_perl_version(perl):
         import subprocess
         try:
-            return subprocess.check_output([perl, '-e', 'print $]'])
+            return Version(subprocess.check_output([perl, '-e', 'print $]']))
         except subprocess.CalledProcessError as e:
             error('Failed to get perl version: %s' % e.message)
 
     @depends(get_perl_version)
     def check_perl_version(version):
         if version < min_version:
             error('Perl %s or higher is required.' % min_version)
 
--- a/python/moz.build
+++ b/python/moz.build
@@ -31,16 +31,17 @@ PYTHON_UNIT_TESTS += [
     'mozbuild/mozbuild/test/backend/test_android_eclipse.py',
     'mozbuild/mozbuild/test/backend/test_build.py',
     'mozbuild/mozbuild/test/backend/test_configenvironment.py',
     'mozbuild/mozbuild/test/backend/test_recursivemake.py',
     'mozbuild/mozbuild/test/backend/test_visualstudio.py',
     'mozbuild/mozbuild/test/compilation/test_warnings.py',
     'mozbuild/mozbuild/test/configure/test_configure.py',
     'mozbuild/mozbuild/test/configure/test_options.py',
+    'mozbuild/mozbuild/test/configure/test_util.py',
     'mozbuild/mozbuild/test/controller/test_ccachestats.py',
     'mozbuild/mozbuild/test/controller/test_clobber.py',
     'mozbuild/mozbuild/test/frontend/test_context.py',
     'mozbuild/mozbuild/test/frontend/test_emitter.py',
     'mozbuild/mozbuild/test/frontend/test_namespaces.py',
     'mozbuild/mozbuild/test/frontend/test_reader.py',
     'mozbuild/mozbuild/test/frontend/test_sandbox.py',
     'mozbuild/mozbuild/test/test_base.py',
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/configure/util.py
@@ -0,0 +1,40 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import itertools
+from distutils.version import LooseVersion
+
+
+class Version(LooseVersion):
+    '''A simple subclass of distutils.version.LooseVersion.
+    Adds attributes for `major`, `minor`, `patch` for the first three
+    version components so users can easily pull out major/minor
+    versions, like:
+
+    v = Version('1.2b')
+    v.major == 1
+    v.minor == 2
+    v.patch == 0
+    '''
+    def __init__(self, version):
+        # Can't use super, LooseVersion's base class is not a new-style class.
+        LooseVersion.__init__(self, version)
+        # Take the first three integer components, stopping at the first
+        # non-integer and padding the rest with zeroes.
+        (self.major,
+         self.minor,
+         self.patch) = [v if v else 0
+                        for v, _ in itertools.izip_longest(
+                                itertools.takewhile(
+                                    lambda x: isinstance(x, int), self.version),
+                                xrange(3))][:3]
+
+
+    def __cmp__(self, other):
+        # LooseVersion checks isinstance(StringType), so work around it.
+        if isinstance(other, unicode):
+            other = other.encode('ascii')
+        return LooseVersion.__cmp__(self, other)
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/configure/test_util.py
@@ -0,0 +1,49 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import unittest
+
+from mozunit import main
+
+from mozbuild.configure.util import Version
+
+
+class TestVersion(unittest.TestCase):
+    def test_version_simple(self):
+        v = Version('1')
+        self.assertEqual(v, '1')
+        self.assertLess(v, '2')
+        self.assertGreater(v, '0.5')
+        self.assertEqual(v.major, 1)
+        self.assertEqual(v.minor, 0)
+        self.assertEqual(v.patch, 0)
+
+    def test_version_more(self):
+        v = Version('1.2.3b')
+        self.assertLess(v, '2')
+        self.assertEqual(v.major, 1)
+        self.assertEqual(v.minor, 2)
+        self.assertEqual(v.patch, 3)
+
+    def test_version_bad(self):
+        # A version with a letter in the middle doesn't really make sense,
+        # so everything after it should be ignored.
+        v = Version('1.2b.3')
+        self.assertLess(v, '2')
+        self.assertEqual(v.major, 1)
+        self.assertEqual(v.minor, 2)
+        self.assertEqual(v.patch, 0)
+
+    def test_version_badder(self):
+        v = Version('1b.2.3')
+        self.assertLess(v, '2')
+        self.assertEqual(v.major, 1)
+        self.assertEqual(v.minor, 0)
+        self.assertEqual(v.patch, 0)
+
+
+if __name__ == '__main__':
+    main()