diff --git a/conan/tools/scm/__init__.py b/conan/tools/scm/__init__.py index 9d4097bc7b2..59846cc18c0 100644 --- a/conan/tools/scm/__init__.py +++ b/conan/tools/scm/__init__.py @@ -1,2 +1,200 @@ from conan.tools.scm.git import Git -from conans.model.version import Version +# develop2 uncomment the next line +# from conans.model.version import Version + + +# ######## DEVELOP2: Delete all the contents from here, this is copy-paste from the Version model +# ######## at conans.model.version (for an easier migration) +from functools import total_ordering +from conans.errors import ConanException + + +@total_ordering +class _VersionItem: + """ a single "digit" in a version, like X.Y.Z all X and Y and Z are VersionItems + They can be int or strings + """ + def __init__(self, item): + try: + self._v = int(item) + except ValueError: + self._v = item + + @property + def value(self): + return self._v + + def __str__(self): + return str(self._v) + + def __add__(self, other): + # necessary for the "bump()" functionality. Other aritmetic operations are missing + return self._v + other + + def __eq__(self, other): + if not isinstance(other, _VersionItem): + other = _VersionItem(other) + return self._v == other._v + + def __hash__(self): + return hash(self._v) + + def __lt__(self, other): + """ + @type other: _VersionItem + """ + if not isinstance(other, _VersionItem): + other = _VersionItem(other) + try: + return self._v < other._v + except TypeError: + return str(self._v) < str(other._v) + + +@total_ordering +class Version: + """ + This is NOT an implementation of semver, as users may use any pattern in their versions. + It is just a helper to parse "." or "-" and compare taking into account integers when possible + """ + def __init__(self, value): + value = str(value) + self._value = value + + items = value.rsplit("+", 1) # split for build + if len(items) == 2: + value, build = items + self._build = Version(build) # This is a nested version by itself + else: + value = items[0] + self._build = None + + items = value.rsplit("-", 1) # split for pre-release + if len(items) == 2: + value, pre = items + self._pre = Version(pre) # This is a nested version by itself + else: + value = items[0] + self._pre = None + items = value.split(".") + items = [_VersionItem(item) for item in items] + self._items = tuple(items) + while items and items[-1].value == 0: + del items[-1] + self._nonzero_items = tuple(items) + + def bump(self, index): + """ + Increments by 1 the version field at the specified index, setting to 0 the fields + on the right. + 2.5 => bump(1) => 2.6 + 1.5.7 => bump(0) => 2.0.0 + """ + # this method is used to compute version ranges from tilde ~1.2 and caret ^1.2.1 ranges + # TODO: at this moment it only works for digits, cannot increment pre-release or builds + # better not make it public yet, keep it internal + items = list(self._items[:index]) + try: + items.append(self._items[index]+1) + except TypeError: + raise ConanException(f"Cannot bump '{self._value} version index {index}, not an int") + items.extend([0] * (len(items) - index - 1)) + v = ".".join(str(i) for i in items) + # prerelease and build are dropped while bumping digits + result = Version(v) + return result + + def upper_bound(self, index): + items = list(self._items[:index]) + try: + items.append(self._items[index] + 1) + except TypeError: + raise ConanException(f"Cannot bump '{self._value} version index {index}, not an int") + items.extend([0] * (len(items) - index - 1)) + v = ".".join(str(i) for i in items) + v += "-" # Exclude prereleases + result = Version(v) + return result + + @property + def pre(self): + return self._pre + + @property + def build(self): + return self._build + + @property + def main(self): + return self._items + + @property + def major(self): + try: + return self.main[0] + except IndexError: + return None + + @property + def minor(self): + try: + return self.main[1] + except IndexError: + return None + + @property + def patch(self): + try: + return self.main[2] + except IndexError: + return None + + @property + def micro(self): + try: + return self.main[3] + except IndexError: + return None + + def __str__(self): + return self._value + + def __repr__(self): + return self._value + + def __eq__(self, other): + if other is None: + return False + if isinstance(other, str): + other = Version(other) + + return (self._nonzero_items, self._pre, self._build) ==\ + (other._nonzero_items, other._pre, other._build) + + def __hash__(self): + return hash((self._nonzero_items, self._pre, self._build)) + + def __lt__(self, other): + if other is None: + return False + if not isinstance(other, Version): + other = Version(other) + + if self._pre: + if other._pre: # both are pre-releases + return (self._nonzero_items, self._pre, self._build) < \ + (other._nonzero_items, other._pre, other._build) + else: # Left hand is pre-release, right side is regular + if self._nonzero_items == other._nonzero_items: # Problem only happens if both equal + return True + else: + return self._nonzero_items < other._nonzero_items + else: + if other._pre: # Left hand is regular, right side is pre-release + if self._nonzero_items == other._nonzero_items: # Problem only happens if both equal + return False + else: + return self._nonzero_items < other._nonzero_items + else: # None of them is pre-release + return (self._nonzero_items, self._build) < (other._nonzero_items, other._build) + diff --git a/conans/__init__.py b/conans/__init__.py index cc354d76021..4eca4011065 100644 --- a/conans/__init__.py +++ b/conans/__init__.py @@ -20,4 +20,4 @@ SERVER_CAPABILITIES = [COMPLEX_SEARCH_CAPABILITY, REVISIONS] # Server is always with revisions DEFAULT_REVISION_V1 = "0" -__version__ = '1.50.1' +__version__ = '1.50.2' diff --git a/conans/client/migrations_settings.py b/conans/client/migrations_settings.py index b66cb0cdc72..b23b0fcf6d2 100644 --- a/conans/client/migrations_settings.py +++ b/conans/client/migrations_settings.py @@ -3714,3 +3714,4 @@ settings_1_50_0 = settings_1_49_0 settings_1_50_1 = settings_1_50_0 +settings_1_50_2 = settings_1_50_1 diff --git a/conans/test/unittests/tools/scm/test_version.py b/conans/test/unittests/tools/scm/test_version.py index bdb83344ab5..7c52bb49e79 100644 --- a/conans/test/unittests/tools/scm/test_version.py +++ b/conans/test/unittests/tools/scm/test_version.py @@ -16,8 +16,17 @@ class Pkg(ConanFile): def configure(self): v = Version(self.settings.compiler.version) assert v > "10" + + v = Version("1.2.3") + self.output.warning(f"The major of 1.2.3 is {v.major}") + self.output.warning(f"The minor of 1.2.3 is {v.minor}") + self.output.warning(f"The patch of 1.2.3 is {v.patch}") """) c.save({"conanfile.py": conanfile}) settings = "-s compiler=gcc -s compiler.libcxx=libstdc++11" c.run("create . {} -s compiler.version=11".format(settings)) + assert "The major of 1.2.3 is 1" in c.out + assert "The minor of 1.2.3 is 2" in c.out + assert "The patch of 1.2.3 is 3" in c.out + c.run("create . {} -s compiler.version=9".format(settings), assert_error=True)