aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/cargo/version.py96
-rwxr-xr-xrun_unittests.py1
-rw-r--r--unittests/cargotests.py61
3 files changed, 158 insertions, 0 deletions
diff --git a/mesonbuild/cargo/version.py b/mesonbuild/cargo/version.py
new file mode 100644
index 0000000..cb09a00
--- /dev/null
+++ b/mesonbuild/cargo/version.py
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2022-2023 Intel Corporation
+
+"""Convert Cargo versions into Meson compatible ones."""
+
+from __future__ import annotations
+import typing as T
+
+
+def convert(cargo_ver: str) -> T.List[str]:
+ """Convert a Cargo compatible version into a Meson compatible one.
+
+ :param cargo_ver: The version, as Cargo specifies
+ :return: A list of version constraints, as Meson understands them
+ """
+ # Cleanup, just for safety
+ cargo_ver = cargo_ver.strip()
+ cargo_vers = [c.strip() for c in cargo_ver.split(',')]
+
+ out: T.List[str] = []
+
+ for ver in cargo_vers:
+ # This covers >= and =< as well
+ # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#comparison-requirements
+ if ver.startswith(('>', '<', '=')):
+ out.append(ver)
+
+ elif ver.startswith('~'):
+ # Rust has these tilde requirements, which means that it is >= to
+ # the version, but less than the next version
+ # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#tilde-requirements
+ # we convert those into a pair of constraints
+ v = ver[1:].split('.')
+ out.append(f'>= {".".join(v)}')
+ if len(v) == 3:
+ out.append(f'< {v[0]}.{int(v[1]) + 1}.0')
+ elif len(v) == 2:
+ out.append(f'< {v[0]}.{int(v[1]) + 1}')
+ else:
+ out.append(f'< {int(v[0]) + 1}')
+
+ elif '*' in ver:
+ # Rust has astrisk requirements,, which are like 1.* == ~1
+ # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#wildcard-requirements
+ v = ver.split('.')[:-1]
+ if v:
+ out.append(f'>= {".".join(v)}')
+ if len(v) == 2:
+ out.append(f'< {v[0]}.{int(v[1]) + 1}')
+ elif len(v) == 1:
+ out.append(f'< {int(v[0]) + 1}')
+
+ else:
+ # a Caret version is equivalent to the default strategy
+ # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#caret-requirements
+ if ver.startswith('^'):
+ ver = ver[1:]
+
+ # If there is no qualifier, then it means this or the next non-zero version
+ # That means that if this is `1.1.0``, then we need `>= 1.1.0` && `< 2.0.0`
+ # Or if we have `0.1.0`, then we need `>= 0.1.0` && `< 0.2.0`
+ # Or if we have `0.1`, then we need `>= 0.1.0` && `< 0.2.0`
+ # Or if we have `0.0.0`, then we need `< 1.0.0`
+ # Or if we have `0.0`, then we need `< 1.0.0`
+ # Or if we have `0`, then we need `< 1.0.0`
+ # Or if we have `0.0.3`, then we need `>= 0.0.3` && `< 0.0.4`
+ # https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-cratesio
+ #
+ # this works much like the ~ versions, but in reverse. Tilde starts
+ # at the patch version and works up, to the major version, while
+ # bare numbers start at the major version and work down to the patch
+ # version
+ vers = ver.split('.')
+ min_: T.List[str] = []
+ max_: T.List[str] = []
+ bumped = False
+ for v_ in vers:
+ if v_ != '0' and not bumped:
+ min_.append(v_)
+ max_.append(str(int(v_) + 1))
+ bumped = True
+ else:
+ if not (bumped and v_ == '0'):
+ min_.append(v_)
+ if not bumped:
+ max_.append('0')
+
+ # If there is no minimum, don't emit one
+ if set(min_) != {'0'}:
+ out.append('>= {}'.format('.'.join(min_)))
+ if set(max_) != {'0'}:
+ out.append('< {}'.format('.'.join(max_)))
+ else:
+ out.append('< 1')
+
+ return out
diff --git a/run_unittests.py b/run_unittests.py
index ddcde76..a820acc 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -36,6 +36,7 @@ from mesonbuild.mesonlib import python_command, setup_vsenv
import mesonbuild.modules.pkgconfig
from unittests.allplatformstests import AllPlatformTests
+from unittests.cargotests import CargoVersionTest
from unittests.darwintests import DarwinTests
from unittests.failuretests import FailureTests
from unittests.linuxcrosstests import LinuxCrossArmTests, LinuxCrossMingwTests
diff --git a/unittests/cargotests.py b/unittests/cargotests.py
new file mode 100644
index 0000000..884052b
--- /dev/null
+++ b/unittests/cargotests.py
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2022-2023 Intel Corporation
+
+from __future__ import annotations
+import unittest
+import typing as T
+
+from mesonbuild.cargo.version import convert
+
+
+class CargoVersionTest(unittest.TestCase):
+
+ def test_cargo_to_meson(self) -> None:
+ cases: T.List[T.Tuple[str, T.List[str]]] = [
+ # Basic requirements
+ ('>= 1', ['>= 1']),
+ ('> 1', ['> 1']),
+ ('= 1', ['= 1']),
+ ('< 1', ['< 1']),
+ ('<= 1', ['<= 1']),
+
+ # tilde tests
+ ('~1', ['>= 1', '< 2']),
+ ('~1.1', ['>= 1.1', '< 1.2']),
+ ('~1.1.2', ['>= 1.1.2', '< 1.2.0']),
+
+ # Wildcards
+ ('*', []),
+ ('1.*', ['>= 1', '< 2']),
+ ('2.3.*', ['>= 2.3', '< 2.4']),
+
+ # Unqualified
+ ('2', ['>= 2', '< 3']),
+ ('2.4', ['>= 2.4', '< 3']),
+ ('2.4.5', ['>= 2.4.5', '< 3']),
+ ('0.0.0', ['< 1']),
+ ('0.0', ['< 1']),
+ ('0', ['< 1']),
+ ('0.0.5', ['>= 0.0.5', '< 0.0.6']),
+ ('0.5.0', ['>= 0.5', '< 0.6']),
+ ('0.5', ['>= 0.5', '< 0.6']),
+
+ # Caret (Which is the same as unqualified)
+ ('^2', ['>= 2', '< 3']),
+ ('^2.4', ['>= 2.4', '< 3']),
+ ('^2.4.5', ['>= 2.4.5', '< 3']),
+ ('^0.0.0', ['< 1']),
+ ('^0.0', ['< 1']),
+ ('^0', ['< 1']),
+ ('^0.0.5', ['>= 0.0.5', '< 0.0.6']),
+ ('^0.5.0', ['>= 0.5', '< 0.6']),
+ ('^0.5', ['>= 0.5', '< 0.6']),
+
+ # Multiple requirements
+ ('>= 1.2.3, < 1.4.7', ['>= 1.2.3', '< 1.4.7']),
+ ]
+
+ for (data, expected) in cases:
+ with self.subTest():
+ self.assertListEqual(convert(data), expected)
+