diff options
Diffstat (limited to 'mesonbuild/cargo/version.py')
-rw-r--r-- | mesonbuild/cargo/version.py | 96 |
1 files changed, 96 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 |