109 lines
3.7 KiB
Python
109 lines
3.7 KiB
Python
from typing import Optional
|
|
|
|
from cargo2rpm.metadata import Metadata, Package
|
|
from cargo2rpm.semver import VersionReq, Comparator, Op as Operator
|
|
|
|
from rust2rpm import log
|
|
|
|
|
|
def get_required_features_for_binaries(package: Package) -> set[str]:
|
|
required_features = set()
|
|
|
|
for target in package.targets:
|
|
if "bin" in target.kind and "bin" in target.crate_types:
|
|
if reqs := target.required_features:
|
|
required_features.update(reqs)
|
|
if "cdylib" in target.kind and "cdylib" in target.crate_types:
|
|
if reqs := target.required_features:
|
|
required_features.update(reqs)
|
|
|
|
return required_features
|
|
|
|
|
|
def guess_main_package(metadata: Metadata, hint: Optional[str] = None, interactive: bool = False) -> Package:
|
|
if not metadata.is_workspace():
|
|
return metadata.packages[0]
|
|
|
|
with_bin = []
|
|
with_cdylib = []
|
|
|
|
for package in metadata.packages:
|
|
for target in package.targets:
|
|
if "bin" in target.kind and "bin" in target.crate_types:
|
|
with_bin.append(package)
|
|
if "cdylib" in target.kind and "cdylib" in target.crate_types:
|
|
with_cdylib.append(package)
|
|
|
|
if len(with_cdylib) == 1:
|
|
return with_cdylib[0]
|
|
|
|
if len(with_bin) == 1:
|
|
return with_bin[0]
|
|
|
|
if interactive: # pragma nocover
|
|
choices = [package.name for package in with_bin] + [package.name for package in with_cdylib]
|
|
log.warn(
|
|
'Heuristic for determining the "main" crate of the workspace failed. '
|
|
+ 'Please specify the name of the "main" crate (usually the crate which '
|
|
+ "provides the user-facing CLI) manually."
|
|
)
|
|
log.info("Choices: " + ", ".join(choices))
|
|
hint = input("Crate name: ")
|
|
|
|
if hint not in choices:
|
|
log.error("Invalid crate name.")
|
|
raise ValueError
|
|
|
|
for package in metadata.packages:
|
|
if package.name == hint:
|
|
return package
|
|
|
|
if hint:
|
|
for package in metadata.packages:
|
|
if package.name == hint:
|
|
log.warn(
|
|
f'Using {hint!r} as the name of the "main" crate. If this is not correct, use interactive mode.'
|
|
)
|
|
return package
|
|
|
|
log.error("Heuristic for determining the main crate of the workspace failed.")
|
|
log.error("Please use '--interactive' mode.")
|
|
|
|
raise ValueError
|
|
|
|
|
|
def package_uses_rust_1_60_feature_syntax(features: dict[str, list[str]]) -> bool:
|
|
for name, deps in features.items():
|
|
for dep in deps:
|
|
if "?/" in dep:
|
|
return True
|
|
|
|
if dep.startswith("dep:"):
|
|
if len(deps) != 1:
|
|
return True
|
|
if dep.removeprefix("dep:") != name:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def warn_if_package_uses_restrictive_dependencies(package: Package):
|
|
for dependency in package.dependencies:
|
|
name = dependency.rename or dependency.name
|
|
req = VersionReq.parse(dependency.req)
|
|
for comp in req.comparators:
|
|
if _is_strict_dep(comp):
|
|
log.warn(f"Dependency on {name!r} stricter than necessary: {comp}")
|
|
|
|
|
|
def _is_strict_dep(comp: Comparator) -> bool:
|
|
# dependencies like "~1.0" are stricter than SemVer
|
|
if comp.op == Operator.TILDE and comp.major > 0 and comp.minor is not None:
|
|
return True
|
|
# dependencies like "1.0.*" are stricter than SemVer
|
|
if comp.op == Operator.WILDCARD and comp.major > 0 and comp.minor is not None:
|
|
return True
|
|
# dependencies like "=1.2" are stricter than SemVer
|
|
if comp.op == Operator.EXACT and comp.major > 0 and comp.minor is not None and comp.patch is None:
|
|
return True
|
|
return False
|