You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rust-packaging/0007-split-features-into-su...

1065 lines
33 KiB

From 2cac5e5ad5ff5472923ce333bef59679612bbaa2 Mon Sep 17 00:00:00 2001
From: Igor Gnatenko <ignatenkobrain@fedoraproject.org>
Date: Fri, 26 Oct 2018 11:20:13 +0200
Subject: [PATCH 07/14] split features into subpackages
References: https://discussion.fedoraproject.org/t/rfc-new-crates-packaging-design-features-have-their-own-subpackages/563?u=ignatenkobrain
Signed-off-by: Igor Gnatenko <ignatenkobrain@fedoraproject.org>
---
data/cargo.attr | 4 +-
data/macros.cargo | 10 +
rust2rpm/__main__.py | 11 +-
rust2rpm/inspector.py | 22 ++-
rust2rpm/metadata.py | 317 ++++++++++++++++----------------
rust2rpm/templates/main.spec | 142 +++++++-------
test.py | 347 ++++-------------------------------
7 files changed, 298 insertions(+), 555 deletions(-)
diff --git a/data/cargo.attr b/data/cargo.attr
index 392a72b..4910b5c 100644
--- a/data/cargo.attr
+++ b/data/cargo.attr
@@ -1,3 +1,3 @@
-%__cargo_provides %{_bindir}/cargo-inspector --provides
-%__cargo_requires %{_bindir}/cargo-inspector --requires
+%__cargo_provides %{_bindir}/cargo-inspector --provides --feature=%{__cargo_feature_from_name -n %{name}}
+%__cargo_requires %{_bindir}/cargo-inspector --requires --feature=%{__cargo_feature_from_name -n %{name}}
%__cargo_path ^%{cargo_registry}/[^/]+/Cargo\\.toml$
diff --git a/data/macros.cargo b/data/macros.cargo
index a0c456a..7fb025b 100644
--- a/data/macros.cargo
+++ b/data/macros.cargo
@@ -84,3 +84,13 @@ if %__cargo_is_bin; then \
%{__rm} %{buildroot}%{_prefix}/.crates.toml \
fi \
)
+
+%__cargo_feature_from_name(n:) %{lua:
+local name = rpm.expand("%{-n*}")
+local feature = string.match(name, "^.+%+(.+)-devel$")
+if feature == nil then
+ print()
+else
+ print(feature)
+end
+}
diff --git a/rust2rpm/__main__.py b/rust2rpm/__main__.py
index f23ebbc..d19cb47 100644
--- a/rust2rpm/__main__.py
+++ b/rust2rpm/__main__.py
@@ -18,15 +18,19 @@ import requests
import tqdm
from . import Metadata, licensing
+from .metadata import normalize_deps
DEFAULT_EDITOR = "vi"
XDG_CACHE_HOME = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
CACHEDIR = os.path.join(XDG_CACHE_HOME, "rust2rpm")
API_URL = "https://crates.io/api/v1/"
JINJA_ENV = jinja2.Environment(loader=jinja2.ChoiceLoader([
- jinja2.FileSystemLoader(["/"]),
- jinja2.PackageLoader("rust2rpm", "templates"), ]),
- trim_blocks=True, lstrip_blocks=True)
+ jinja2.FileSystemLoader(["/"]),
+ jinja2.PackageLoader("rust2rpm", "templates"),
+ ]),
+ extensions=["jinja2.ext.do"],
+ trim_blocks=True,
+ lstrip_blocks=True)
def get_default_target():
# TODO: add fallback for /usr/lib/os-release
@@ -227,6 +231,7 @@ def main():
patch=args.patch,
store=args.store_crate)
+ JINJA_ENV.globals["normalize_deps"] = normalize_deps
template = JINJA_ENV.get_template("main.spec")
if args.patch and len(diff) > 0:
diff --git a/rust2rpm/inspector.py b/rust2rpm/inspector.py
index 2d488b2..9e79e88 100644
--- a/rust2rpm/inspector.py
+++ b/rust2rpm/inspector.py
@@ -1,8 +1,8 @@
import argparse
-import itertools
import sys
from . import Metadata
+from .metadata import normalize_deps
def main():
parser = argparse.ArgumentParser()
@@ -10,18 +10,23 @@ def main():
group.add_argument("-n", "--name", action="store_true", help="Print name")
group.add_argument("-v", "--version", action="store_true", help="Print version")
group.add_argument("-t", "--target-kinds", action="store_true", help="Print target kinds")
+ group.add_argument("-l", "--list-features", action="store_true", help="Print features")
group.add_argument("-P", "--provides", action="store_true", help="Print Provides")
group.add_argument("-R", "--requires", action="store_true", help="Print Requires")
group.add_argument("-BR", "--build-requires", action="store_true", help="Print BuildRequires")
group.add_argument("-TR", "--test-requires", action="store_true", help="Print TestRequires")
+ parser.add_argument("-f", "--feature", help="Feature to work on")
parser.add_argument("file", nargs="*", help="Path(s) to Cargo.toml")
args = parser.parse_args()
files = args.file or sys.stdin.readlines()
+ if not args.feature:
+ args.feature = None
+
def print_deps(deps):
if len(deps) > 0:
- print("\n".join(str(dep) for dep in deps))
+ print("\n".join(sorted(normalize_deps(deps))))
for f in files:
f = f.rstrip()
@@ -32,17 +37,20 @@ def main():
print(md.version)
if args.target_kinds:
print("\n".join(set(tgt.kind for tgt in md.targets)))
+ if args.list_features:
+ for f in sorted(f for f in md.dependencies if f is not None):
+ print(f)
if args.provides:
- print_deps(md.provides)
- if args.requires or args.build_requires:
- print_deps(list(itertools.chain(md.requires, md.build_requires)))
- if args.test_requires:
- print_deps(md.test_requires)
+ print(md.provides(args.feature))
if args.requires:
# Someone should own /usr/share/cargo/registry
print("cargo")
+ print_deps(md.requires(args.feature))
if args.build_requires:
print("rust-packaging")
+ print_deps(md.requires(args.feature or "default", resolve=True))
+ if args.test_requires:
+ print_deps(md.dev_dependencies)
if __name__ == "__main__":
main()
diff --git a/rust2rpm/metadata.py b/rust2rpm/metadata.py
index 5adeb65..4929cdd 100644
--- a/rust2rpm/metadata.py
+++ b/rust2rpm/metadata.py
@@ -1,208 +1,207 @@
__all__ = ["Dependency", "Metadata"]
-import itertools
+import copy
import json
import subprocess
-import sys
import semantic_version as semver
import rustcfg
-class Target(object):
- def __init__(self, kind, name):
- self.kind = kind
+class Target:
+ def __init__(self, name, kind):
self.name = name
+ self.kind = kind
def __repr__(self):
- return "<Target {self.kind}|{self.name}>".format(self=self)
-
-
-def _req_to_str(name, spec=None, feature=None):
- f_part = "/{}".format(feature) if feature is not None else ""
- basestr = "crate({}{})".format(name, f_part)
- if spec is None:
- return basestr
- if spec.kind == spec.KIND_EQUAL:
- spec.kind = spec.KIND_SHORTEQ
- if spec.kind == spec.KIND_ANY:
- if spec.spec == "":
- # Just wildcard
- return basestr
- else:
- # Wildcard in string
- assert False, spec.spec
- version = str(spec.spec).replace("-", "~")
- return "{} {} {}".format(basestr, spec.kind, version)
+ return f"<Target {self.name} ({self.kind})>"
-class Dependency(object):
- def __init__(self, name, req, features=(), provides=False):
+class Dependency:
+ def __init__(self, name, req=None, features=(), optional=False):
self.name = name
- self.spec = self._parse_req(req)
+ self.req = req
self.features = features
- self.provides = provides
- if self.provides:
- if len(self.spec.specs) > 1 or \
- (len(self.spec.specs) == 1 and self.spec.specs[0].kind != self.spec.specs[0].KIND_EQUAL):
- raise Exception("Provides can't be applied to ranged version, {!r}".format(self.spec))
-
- def __repr__(self):
- if self.provides:
- spec = self.spec.specs[0]
- provs = [_req_to_str(self.name, spec)]
- for feature in self.features:
- provs.append(_req_to_str(self.name, spec, feature))
- return " and ".join(provs)
-
- reqs = [_req_to_str(self.name, spec=req) for req in self.spec.specs]
- features = [_req_to_str(self.name, feature=feature) for feature in self.features]
+ self.optional = optional
- use_rich = False
- if len(reqs) > 1:
- reqstr = "({})".format(" with ".join(reqs))
- use_rich = True
- elif len(reqs) == 1:
- reqstr = reqs[0]
- else:
- reqstr = ""
- if len(features) > 0:
- featurestr = " with ".join(features)
- use_rich = True
- else:
- featurestr = ""
-
- if use_rich:
- if reqstr and featurestr:
- return "({} with {})".format(reqstr, featurestr)
- elif reqstr and not featurestr:
- return reqstr
- elif not reqstr and featurestr:
- return "({})".format(featurestr)
- else:
- assert False
- else:
- return reqstr
+ @classmethod
+ def from_json(cls, metadata):
+ features = set(metadata['features'])
+ if metadata['uses_default_features']:
+ features.add('default')
+ kwargs = {'name': metadata['name'],
+ 'req': metadata['req'],
+ 'optional': metadata['optional'],
+ 'features': features}
+ return cls(**kwargs)
@staticmethod
- def _parse_req(s):
- if "*" in s and s != "*":
- # XXX: https://github.com/rbarrois/python-semanticversion/issues/51
- s = "~{}".format(s.replace(".*", "", 1))
- if ".*" in s:
- s = s.replace(".*", "")
- spec = semver.Spec(s.replace(" ", ""))
- parsed = []
+ def _normalize_req(req):
+ if "*" in req and req != "*":
+ raise NotImplementedError(f"'*' is not supported: {req}")
+ spec = semver.Spec(req.replace(" ", ""))
+ reqs = []
for req in spec.specs:
- ver = req.spec
if req.kind == req.KIND_ANY:
- parsed.append("*")
+ # Any means any
continue
+ ver = req.spec
+ if ver.prerelease:
+ raise NotImplementedError(f"Pre-release requirement is not supported: {ver}")
+ if req.kind in (req.KIND_NEQ, req.KIND_EMPTY):
+ raise NotImplementedError(f"'!=' and empty kinds are not supported: {req}")
coerced = semver.Version.coerce(str(ver))
- if req.kind in (req.KIND_CARET, req.KIND_TILDE):
- if ver.prerelease:
- # pre-release versions only match the same x.y.z
- if ver.patch is not None:
- upper = ver.next_patch()
- elif ver.minor is not None:
- upper = ver.next_minor()
- else:
- upper = ver.next_major()
- elif req.kind == req.KIND_CARET:
- if ver.major == 0:
- if ver.minor is not None:
- if ver.patch is None or ver.minor != 0:
- upper = ver.next_minor()
- else:
- upper = ver.next_patch()
+ if req.kind == req.KIND_EQUAL:
+ req.kind = req.KIND_SHORTEQ
+ if req.kind in (req.KIND_CARET, req.KIND_COMPATIBLE):
+ if ver.major == 0:
+ if ver.minor is not None:
+ if ver.minor != 0 or ver.patch is None:
+ upper = ver.next_minor()
else:
- upper = ver.next_major()
+ upper = ver.next_patch()
else:
upper = ver.next_major()
- elif req.kind == req.KIND_TILDE:
- if ver.minor is None:
- upper = ver.next_major()
- else:
- upper = ver.next_minor()
else:
- assert False
- parsed.append(">={}".format(coerced))
- parsed.append("<{}".format(upper))
- elif req.kind == req.KIND_NEQ:
- parsed.append(">{}".format(coerced))
- parsed.append("<{}".format(coerced))
- elif req.kind in (req.KIND_EQUAL, req.KIND_GT, req.KIND_GTE, req.KIND_LT, req.KIND_LTE):
- parsed.append("{}{}".format(req.kind, coerced))
+ upper = ver.next_major()
+ reqs.append((">=", coerced))
+ reqs.append(("<", upper))
+ elif req.kind == req.KIND_TILDE:
+ if ver.minor is None:
+ upper = ver.next_major()
+ else:
+ upper = ver.next_minor()
+ reqs.append((">=", coerced))
+ reqs.append(("<", upper))
+ elif req.kind in (req.KIND_SHORTEQ,
+ req.KIND_GT,
+ req.KIND_GTE,
+ req.KIND_LT,
+ req.KIND_LTE):
+ reqs.append((str(req.kind), coerced))
else:
- assert False, req.kind
- return semver.Spec(",".join(parsed))
+ raise AssertionError(f"Found unhandled kind: {req.kind}")
+ return reqs
-class Metadata(object):
- def __init__(self):
- self.name = None
+ @staticmethod
+ def _apply_reqs(name, reqs, feature=None):
+ fstr = f"/{feature}" if feature is not None else ""
+ cap = f"crate({name}{fstr})"
+ if not reqs:
+ return cap
+ deps = " with ".join(f"{cap} {op} {version}" for op, version in reqs)
+ if len(reqs) > 1:
+ return f"({deps})"
+ else:
+ return deps
+
+ def normalize(self):
+ return [self._apply_reqs(self.name, self._normalize_req(self.req), feature)
+ for feature in self.features or (None,)]
+
+ def __repr__(self):
+ return f"<Dependency: {self.name} {self.req} ({', '.join(sorted(self.features))})>"
+
+ def __str__(self):
+ return "\n".join(self.normalize())
+
+class Metadata:
+ def __init__(self, name, version):
+ self.name = name
+ self.version = version
self.license = None
self.license_file = None
self.readme = None
self.description = None
- self.version = None
- self._targets = []
- self.provides = []
- self.requires = []
- self.build_requires = []
- self.test_requires = []
+ self.targets = set()
+ self.dependencies = {}
+ self.dev_dependencies = set()
@classmethod
def from_json(cls, metadata):
- self = cls()
-
md = metadata
- self.name = md["name"]
+ self = cls(md["name"], md["version"])
+
self.license = md["license"]
self.license_file = md["license_file"]
self.readme = md["readme"]
self.description = md.get("description")
- self.version = md["version"]
- version = "={}".format(self.version)
-
- # Targets
- self.targets = [Target(tgt["kind"][0], tgt["name"]) for tgt in md["targets"]]
-
- # Provides
- # All optional dependencies are also features
- # https://github.com/rust-lang/cargo/issues/4911
- features = itertools.chain((x["name"] for x in md["dependencies"] if x["optional"]),
- md["features"])
- provides = Dependency(self.name, version, features=features, provides=True)
- self.provides = str(provides).split(" and ")
-
- ev = rustcfg.Evaluator.platform()
-
- # Dependencies
- for dep in md["dependencies"]:
- kind = dep["kind"]
- if kind is None:
- requires = self.requires
- elif kind == "build":
- requires = self.build_requires
- elif kind == "dev":
- requires = self.test_requires
- else:
- raise ValueError("Unknown kind: {!r}, please report bug.".format(kind))
- target = dep["target"]
- if target is None:
- pass
+ # dependencies + build-dependencies → runtime
+ deps_by_name = {dep["name"]: Dependency.from_json(dep)
+ for dep in md["dependencies"]
+ if dep["kind"] != "dev"}
+
+ deps_by_feature = {}
+ for feature, f_deps in md["features"].items():
+ features = {None}
+ deps = set()
+ for dep in f_deps:
+ if dep in md["features"]:
+ features.add(dep)
+ else:
+ pkg, _, f = dep.partition("/")
+ dep = copy.deepcopy(deps_by_name[pkg])
+ if f:
+ dep.features = {f}
+ deps.add(dep)
+ deps_by_feature[feature] = (features, deps)
+
+ mandatory_deps = set()
+ for dep in deps_by_name.values():
+ if dep.optional:
+ deps_by_feature[dep.name] = ({None}, {copy.deepcopy(dep)})
else:
- cond = ev.parse_and_eval(target)
- if not cond:
- print(f'Dependency {dep["name"]} for target {target!r} is not needed, ignoring.',
- file=sys.stderr)
- continue
+ mandatory_deps.add(copy.deepcopy(dep))
+ deps_by_feature[None] = (set(), mandatory_deps)
+
+ if "default" not in deps_by_feature:
+ deps_by_feature["default"] = ({None}, set())
- requires.append(Dependency(dep["name"], dep["req"], features=dep["features"]))
+ self.dependencies = deps_by_feature
+ self.dev_dependencies = {Dependency.from_json(dep)
+ for dep in md["dependencies"]
+ if dep["kind"] == "dev"}
+
+ self.targets = {Target(tgt["name"], tgt["kind"][0])
+ for tgt in md["targets"]}
return self
@classmethod
def from_file(cls, path):
metadata = subprocess.check_output(["cargo", "read-manifest",
- "--manifest-path={}".format(path)])
+ f"--manifest-path={path}"])
return cls.from_json(json.loads(metadata))
+
+ @property
+ def all_dependencies(self):
+ return set().union(*(x[1] for x in self.dependencies.values()))
+
+ def provides(self, feature=None):
+ if feature not in self.dependencies:
+ raise KeyError(f"Feature {feature!r} doesn't exist")
+ return Dependency(self.name, f"={self.version}", features={feature})
+
+ @classmethod
+ def _resolve(cls, deps_by_feature, feature):
+ all_features = set()
+ all_deps = set()
+ ff, dd = copy.deepcopy(deps_by_feature[feature])
+ all_features |= ff
+ all_deps |= dd
+ for f in ff:
+ ff1, dd1 = cls._resolve(deps_by_feature, f)
+ all_features |= ff1
+ all_deps |= dd1
+ return all_features, all_deps
+
+ def requires(self, feature=None, resolve=False):
+ if resolve:
+ return self._resolve(self.dependencies, feature)[1]
+ else:
+ features, deps = self.dependencies[feature]
+ fdeps = set(Dependency(self.name, f"={self.version}", features={feature})
+ for feature in features)
+ return fdeps | deps
+
+def normalize_deps(deps):
+ return set().union(*(d.normalize() for d in deps))
diff --git a/rust2rpm/templates/main.spec b/rust2rpm/templates/main.spec
index 7dbcc3f..0d9a80b 100644
--- a/rust2rpm/templates/main.spec
+++ b/rust2rpm/templates/main.spec
@@ -48,82 +48,102 @@ Patch0: {{ patch_file }}
ExclusiveArch: %{rust_arches}
BuildRequires: rust-packaging
-{% if include_build_requires %}
-{% if md.requires|length > 0 %}
-# [dependencies]
-{% for req in md.requires|sort(attribute="name") %}
+{# We will put all non-optional and optional dependencies until
+ https://github.com/rust-lang/cargo/issues/5133
+ is solved
+{% set buildrequires = normalize_deps(md.requires("default", resolve=True))|sort %}
+#}
+{% set buildrequires = normalize_deps(md.all_dependencies)|sort %}
+{% for req in buildrequires %}
BuildRequires: {{ req }}
{% endfor %}
-{% endif %}
-{% if md.build_requires|length > 0 %}
-# [build-dependencies]
-{% for req in md.build_requires|sort(attribute="name") %}
-BuildRequires: {{ req }}
-{% endfor %}
-{% endif %}
-{% if md.test_requires|length > 0 %}
+{% set testrequires = normalize_deps(md.dev_dependencies)|sort %}
+{% if testrequires|length > 0 %}
%if %{with check}
-# [dev-dependencies]
-{% for req in md.test_requires|sort(attribute="name") %}
+ {% for req in testrequires %}
BuildRequires: {{ req }}
-{% endfor %}
+ {% endfor %}
%endif
{% endif %}
-{% endif %}
-%description
+%global _description \
+{% if md.description is none %}
%{summary}.
+{% else %}
+{{ md.description|wordwrap(wrapstring="\\\n")|trim }}
+{% endif %}
+
+%description %{_description}
{% if include_main %}
%package -n %{crate}
Summary: %{summary}
-{% if rust_group is defined %}
+ {% if rust_group is defined %}
Group: # FIXME
-{% endif %}
+ {% endif %}
%description -n %{crate}
%{summary}.
-{% endif %}
+%files -n %{crate}
+ {% if md.license_file is not none %}
+%license {{ md.license_file }}
+ {% endif %}
+ {% if md.readme is not none %}
+%doc {{ md.readme }}
+ {% endif %}
+ {% for bin in bins %}
+%{_bindir}/{{ bin.name }}
+ {% endfor %}
+
+{% endif -%}
+
{% if include_devel %}
-%package devel
+ {% set features = md.dependencies.keys()|list %}
+ {% do features.remove(None) %}
+ {% do features.remove("default") %}
+ {% set features = features|sort %}
+ {% do features.insert(0, None) %}
+ {% do features.insert(1, "default") %}
+ {% for feature in features %}
+ {% set pkg = "-n %%{name}+%s-devel"|format(feature) if feature is not none else " devel" %}
+%package {{ pkg }}
Summary: %{summary}
-{% if rust_group is defined %}
+ {% if rust_group is defined %}
Group: {{ rust_group }}
-{% endif %}
+ {% endif %}
BuildArch: noarch
-{% if include_provides %}
-{% for prv in md.provides %}
-Provides: {{ prv }}
-{% endfor %}
-{% endif %}
-{% if include_requires %}
+ {% if include_provides %}
+Provides: {{ md.provides(feature) }}
+ {% endif %}
+ {% if include_requires %}
Requires: cargo
-{% if md.requires|length > 0 %}
-# [dependencies]
-{% for req in md.requires|sort(attribute="name") %}
-Requires: {{ req }}
-{% endfor %}
-{% endif %}
-{% if md.build_requires|length > 0 %}
-# [build-dependencies]
-{% for req in md.build_requires|sort(attribute="name") %}
+ {% for req in md.requires(feature)|map("string")|sort %}
Requires: {{ req }}
-{% endfor %}
-{% endif %}
-{% endif %}
+ {% endfor %}
+ {% endif %}
-%description devel
-{% if md.description is none %}
-%{summary}.
-{% else %}
-{{ md.description|wordwrap|trim }}
-{% endif %}
+%description {{ pkg }} %{_description}
This package contains library source intended for building other packages
-which use %{crate} from crates.io.
+which use {% if feature is not none %}"{{ feature }}" feature of {% endif %}"%{crate}" crate.
+
+%files {{ pkg }}
+ {% if feature is none %}
+ {% if md.license_file is not none %}
+%license {{ md.license_file }}
+ {% endif %}
+ {% if md.readme is not none %}
+%doc {{ md.readme }}
+ {% endif %}
+%{cargo_registry}/%{crate}-%{version}/
+ {% else %}
+%ghost %{cargo_registry}/%{crate}-%{version}/Cargo.toml
+ {% endif %}
+
+ {% endfor %}
+{% endif -%}
-{% endif %}
%prep
{% if md.name != crate %}
%autosetup -n %{real_crate}-%{version} -p1
@@ -143,29 +163,5 @@ which use %{crate} from crates.io.
%cargo_test
%endif
-{% if include_main %}
-%files -n %{crate}
-{% if md.license_file is not none %}
-%license {{ md.license_file }}
-{% endif %}
-{% if md.readme is not none %}
-%doc {{ md.readme }}
-{% endif %}
-{% for bin in bins %}
-%{_bindir}/{{ bin.name }}
-{% endfor %}
-
-{% endif %}
-{% if include_devel %}
-%files devel
-{% if md.license_file is not none %}
-%license {{ md.license_file }}
-{% endif %}
-{% if md.readme is not none %}
-%doc {{ md.readme }}
-{% endif %}
-%{cargo_registry}/%{crate}-%{version}/
-
-{% endif %}
%changelog
{% include target ~ "-changelog.spec.inc" %}
diff --git a/test.py b/test.py
index b856fdd..30263b4 100644
--- a/test.py
+++ b/test.py
@@ -1,318 +1,43 @@
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-import textwrap
-
import pytest
import rust2rpm
-DUMMY_LIB = """
-pub fn say_hello() {
- println!("Hello, World!");
-}
-"""
-DEPGEN = os.path.join(os.path.dirname(__file__), "cargodeps.py")
-
-
-@pytest.mark.parametrize("req, features, rpmdep", [
- ("=1.0.0", [],
- "crate(test) = 1.0.0"),
- ("=1.0.0", ["feature"],
- "(crate(test) = 1.0.0 with crate(test/feature))"),
- (">=1.0.0,<2.0.0", [],
+@pytest.mark.parametrize("req, rpmdep", [
+ ("^1.2.3",
+ "(crate(test) >= 1.2.3 with crate(test) < 2.0.0)"),
+ ("^1.2",
+ "(crate(test) >= 1.2.0 with crate(test) < 2.0.0)"),
+ ("^1",
"(crate(test) >= 1.0.0 with crate(test) < 2.0.0)"),
- (">=1.0.0,<2.0.0", ["feature"],
- "((crate(test) >= 1.0.0 with crate(test) < 2.0.0) with crate(test/feature))"),
+ ("^0.2.3",
+ "(crate(test) >= 0.2.3 with crate(test) < 0.3.0)"),
+ ("^0.2",
+ "(crate(test) >= 0.2.0 with crate(test) < 0.3.0)"),
+ ("^0.0.3",
+ "(crate(test) >= 0.0.3 with crate(test) < 0.0.4)"),
+ ("^0.0",
+ "(crate(test) >= 0.0.0 with crate(test) < 0.1.0)"),
+ ("^0",
+ "(crate(test) >= 0.0.0 with crate(test) < 1.0.0)"),
+ ("~1.2.3",
+ "(crate(test) >= 1.2.3 with crate(test) < 1.3.0)"),
+ ("~1.2",
+ "(crate(test) >= 1.2.0 with crate(test) < 1.3.0)"),
+ ("~1",
+ "(crate(test) >= 1.0.0 with crate(test) < 2.0.0)"),
+ ("*",
+ "crate(test)"),
+ (">= 1.2.0",
+ "crate(test) >= 1.2.0"),
+ ("> 1",
+ "crate(test) > 1.0.0"),
+ ("< 2",
+ "crate(test) < 2.0.0"),
+ ("= 1.2.3",
+ "crate(test) = 1.2.3"),
+ (">= 1.2, < 1.5",
+ "(crate(test) >= 1.2.0 with crate(test) < 1.5.0)"),
])
-def test_dependency(req, features, rpmdep):
- dep = rust2rpm.Dependency("test", req, features)
+def test_dependency(req, rpmdep):
+ dep = rust2rpm.Dependency("test", req)
assert str(dep) == rpmdep
-
-@pytest.fixture
-def cargo_toml(request):
- def make_cargo_toml(contents):
- toml = os.path.join(tmpdir, "Cargo.toml")
- with open(toml, "w") as fobj:
- fobj.write(textwrap.dedent(contents))
- return toml
-
- tmpdir = tempfile.mkdtemp(prefix="cargo-deps-")
- srcdir = os.path.join(tmpdir, "src")
- os.mkdir(srcdir)
- with open(os.path.join(srcdir, "lib.rs"), "w") as fobj:
- fobj.write(DUMMY_LIB)
-
- def finalize():
- shutil.rmtree(tmpdir)
- request.addfinalizer(finalize)
-
- return make_cargo_toml
-
-@pytest.mark.parametrize("toml, provides, requires", [
-
- # Basic provides
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
- """,
- ["crate(hello) = 0.0.0"],
- []),
-
- # Basic provides for feature
- ("""
- [package]
- name = "hello"
- version = "1.2.3"
-
- [features]
- color = []
- """,
- ["crate(hello) = 1.2.3",
- "crate(hello/color) = 1.2.3"],
- []),
-
- # Provides for optional dependencies
- ("""
- [package]
- name = "hello"
- version = "1.2.3"
-
- [dependencies]
- non_optional = "1"
- serde = { version = "1", optional = true }
- rand = { version = "0.4", optional = true }
-
- [features]
- std = []
- v1 = ["rand"]
- """,
- ["crate(hello) = 1.2.3",
- "crate(hello/rand) = 1.2.3",
- "crate(hello/serde) = 1.2.3",
- "crate(hello/std) = 1.2.3",
- "crate(hello/v1) = 1.2.3"],
- ["(crate(non_optional) >= 1.0.0 with crate(non_optional) < 2.0.0)",
- "(crate(rand) >= 0.4.0 with crate(rand) < 0.5.0)",
- "(crate(serde) >= 1.0.0 with crate(serde) < 2.0.0)"]),
-
- # Caret requirements
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "^0"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 0.0.0 with crate(libc) < 1.0.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "^0.0"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 0.0.0 with crate(libc) < 0.1.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "^0.0.3"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 0.0.3 with crate(libc) < 0.0.4)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "^0.2.3"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 0.2.3 with crate(libc) < 0.3.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "^1"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.0.0 with crate(libc) < 2.0.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "^1.2"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.2.0 with crate(libc) < 2.0.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "^1.2.3"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.2.3 with crate(libc) < 2.0.0)"]),
-
- # Tilde requirements
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "~1"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.0.0 with crate(libc) < 2.0.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "~1.2"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.2.0 with crate(libc) < 1.3.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "~1.2.3"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.2.3 with crate(libc) < 1.3.0)"]),
-
- # Wildcard requirements
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "*"
- """,
- ["crate(hello) = 0.0.0"],
- ["crate(libc)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "1.*"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.0.0 with crate(libc) < 2.0.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "1.2.*"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.2.0 with crate(libc) < 1.3.0)"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "1.*.*"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.0.0 with crate(libc) < 2.0.0)"]),
-
- # Inequality requirements
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = ">= 1.2.0"
- """,
- ["crate(hello) = 0.0.0"],
- ["crate(libc) >= 1.2.0"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "> 1"
- """,
- ["crate(hello) = 0.0.0"],
- ["crate(libc) > 1.0.0"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "< 2"
- """,
- ["crate(hello) = 0.0.0"],
- ["crate(libc) < 2.0.0"]),
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = "= 1.2.3"
- """,
- ["crate(hello) = 0.0.0"],
- ["crate(libc) = 1.2.3"]),
-
- # Multiple requirements
- ("""
- [package]
- name = "hello"
- version = "0.0.0"
-
- [dependencies]
- libc = ">= 1.2, < 1.5"
- """,
- ["crate(hello) = 0.0.0"],
- ["(crate(libc) >= 1.2.0 with crate(libc) < 1.5.0)"]),
-
- # Pre-release requirements
- ("""
- [package]
- name = "hello"
- version = "0.0.0-alpha"
-
- [dependencies]
- foo-bar = "1.2.3-beta"
- """,
- ["crate(hello) = 0.0.0~alpha"],
- ["(crate(foo-bar) >= 1.2.3~beta with crate(foo-bar) < 1.2.3)"]),
-
-])
-def test_depgen(toml, provides, requires, cargo_toml):
- md = rust2rpm.Metadata.from_file(cargo_toml(toml))
- assert [str(x) for x in md.provides] == provides
- assert [str(x) for x in md.requires] == requires
--
2.20.1