conf: factor out rust2rpm.conf file parsing; add validation and tests
This commit is contained in:
parent
539033b196
commit
fb1944d558
8 changed files with 246 additions and 55 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@ __pycache__/
|
|||
/build/
|
||||
/venv/
|
||||
/.idea/
|
||||
/.coverage
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import configparser
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
|
@ -7,6 +6,7 @@ from cargo2rpm.metadata import FeatureFlags
|
|||
|
||||
from rust2rpm import log
|
||||
from rust2rpm.cli import get_parser
|
||||
from rust2rpm.conf import Rust2RpmConf, Rust2RpmConfError
|
||||
from rust2rpm.crate import process_project
|
||||
from rust2rpm.cratesio import NoVersionsError
|
||||
from rust2rpm.distgit import get_package_info
|
||||
|
@ -84,30 +84,28 @@ def main():
|
|||
|
||||
packager = detect_packager()
|
||||
|
||||
conf = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation())
|
||||
confs = conf.read([".rust2rpm.conf", "_rust2rpm.conf", "rust2rpm.conf"])
|
||||
try:
|
||||
# known file names (only in the current working directory)
|
||||
filenames = [".rust2rpm.conf", "_rust2rpm.conf", "rust2rpm.conf"]
|
||||
|
||||
# clean up configuration files with deprecated names
|
||||
if len(confs) > 1:
|
||||
if not metadata.is_workspace():
|
||||
distconf = Rust2RpmConf.load(filenames, args.target, metadata.packages[0].get_feature_names())
|
||||
else:
|
||||
distconf = Rust2RpmConf.load(filenames, args.target, set())
|
||||
|
||||
except FileExistsError:
|
||||
log.error(
|
||||
"More than one *rust2rpm.conf file is present in this directory. "
|
||||
+ "Ensure that there is only one, and that it has the correct contents."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if ".rust2rpm.conf" in confs and "rust2rpm.conf" not in confs:
|
||||
os.rename(".rust2rpm.conf", "rust2rpm.conf")
|
||||
log.info("Renamed deprecated, hidden .rust2rpm.conf file to rust2rpm.conf.")
|
||||
except Rust2RpmConfError as exc:
|
||||
log.error("Invalid rust2rpm configuration file:")
|
||||
log.error(str(exc))
|
||||
sys.exit(1)
|
||||
|
||||
if "_rust2rpm.conf" in confs and "rust2rpm.conf" not in confs:
|
||||
os.rename("_rust2rpm.conf", "rust2rpm.conf")
|
||||
log.info("Renamed deprecated _rust2rpm.conf file to rust2rpm.conf.")
|
||||
|
||||
if args.target not in conf:
|
||||
conf.add_section(args.target)
|
||||
|
||||
conf_all_features = conf[args.target].getboolean("all-features")
|
||||
if conf_all_features is False and args.all_features:
|
||||
if distconf.all_features is False and args.all_features:
|
||||
log.warn(
|
||||
'Conflicting settings for enabling all features: The setting is "false"'
|
||||
+ 'in rust2rpm.conf but it was enabled with the "--all-features" CLI flag.'
|
||||
|
@ -122,13 +120,14 @@ def main():
|
|||
patch_file_manual=patch_files[1],
|
||||
license_files=license_files,
|
||||
doc_files=doc_files,
|
||||
distconf=conf[args.target],
|
||||
feature_flags=FeatureFlags(all_features=(conf_all_features or args.all_features)),
|
||||
distconf=distconf,
|
||||
feature_flags=FeatureFlags(all_features=(distconf.all_features or args.all_features)),
|
||||
relative_license_paths=args.relative_license_paths,
|
||||
rpmautospec=args.rpmautospec,
|
||||
auto_changelog_entry=args.auto_changelog_entry,
|
||||
packager=packager,
|
||||
)
|
||||
|
||||
else:
|
||||
spec_contents = spec_render_workspace(
|
||||
metadata=metadata,
|
||||
|
@ -136,8 +135,8 @@ def main():
|
|||
rpm_name=rpm_name,
|
||||
license_files=license_files,
|
||||
doc_files=doc_files,
|
||||
distconf=conf[args.target],
|
||||
feature_flags=FeatureFlags(all_features=(conf_all_features or args.all_features)),
|
||||
distconf=distconf,
|
||||
feature_flags=FeatureFlags(all_features=(distconf.all_features or args.all_features)),
|
||||
rpmautospec=args.rpmautospec,
|
||||
auto_changelog_entry=args.auto_changelog_entry,
|
||||
packager=packager,
|
||||
|
|
112
rust2rpm/conf.py
Normal file
112
rust2rpm/conf.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
import configparser
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from rust2rpm import log
|
||||
|
||||
|
||||
def to_list(s):
|
||||
if not s:
|
||||
return []
|
||||
return list(sorted(filter(None, (l.strip() for l in s.splitlines()))))
|
||||
|
||||
|
||||
class Rust2RpmConfError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class Rust2RpmConf:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
all_features: bool = False,
|
||||
unwanted_features: list[str] = None,
|
||||
buildrequires: list[str] = None,
|
||||
testrequires: list[str] = None,
|
||||
bin_requires: list[str] = None,
|
||||
lib_requires: dict[Optional[str], list[str]] = None,
|
||||
):
|
||||
self.all_features: bool = all_features
|
||||
self.unwanted_features: list[str] = unwanted_features or list()
|
||||
self.buildrequires: list[str] = buildrequires or list()
|
||||
self.testrequires: list[str] = testrequires or list()
|
||||
self.bin_requires: list[str] = bin_requires or list()
|
||||
self.lib_requires: dict[Optional[str], list[str]] = lib_requires or dict()
|
||||
|
||||
@staticmethod
|
||||
def load(filenames: list[str], target: str, features: set[str]):
|
||||
conf = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation())
|
||||
confs = conf.read(filenames)
|
||||
|
||||
if len(confs) > 1:
|
||||
raise FileExistsError
|
||||
|
||||
# clean up configuration files with deprecated names
|
||||
if ".rust2rpm.conf" in confs:
|
||||
os.rename(".rust2rpm.conf", "rust2rpm.conf")
|
||||
log.info("Renamed deprecated, hidden .rust2rpm.conf file to rust2rpm.conf.")
|
||||
|
||||
if "_rust2rpm.conf" in confs:
|
||||
os.rename("_rust2rpm.conf", "rust2rpm.conf")
|
||||
log.info("Renamed deprecated _rust2rpm.conf file to rust2rpm.conf.")
|
||||
|
||||
# merge target-specific configuration with default configuration
|
||||
if target not in conf:
|
||||
conf.add_section(target)
|
||||
merged = conf[target]
|
||||
|
||||
# validate configuration file
|
||||
valid_targets = ["fedora", "mageia", "opensuse", "plain"]
|
||||
valid_keys = [
|
||||
"all-features",
|
||||
"unwanted-features",
|
||||
"buildrequires",
|
||||
"testrequires",
|
||||
"lib.requires",
|
||||
"bin.requires",
|
||||
]
|
||||
for feature in features:
|
||||
valid_keys.append(f"lib+{feature}.requires")
|
||||
|
||||
# check section names
|
||||
for section in conf.keys():
|
||||
if section != "DEFAULT" and section not in valid_targets:
|
||||
raise Rust2RpmConfError(f"Invalid section: {section!r}")
|
||||
|
||||
# check setting keys
|
||||
for key in merged.keys():
|
||||
if key not in valid_keys:
|
||||
raise Rust2RpmConfError(f"Invalid key: {key!r}")
|
||||
|
||||
# parse configuration and validate settings
|
||||
settings = dict()
|
||||
|
||||
if all_features := merged.getboolean("all-features") and all_features is not None:
|
||||
settings["all_features"] = all_features
|
||||
|
||||
if unwanted_features := merged.get("unwanted-features"):
|
||||
settings["unwanted_features"] = to_list(unwanted_features)
|
||||
|
||||
for unwanted_feature in settings["unwanted_features"]:
|
||||
if unwanted_feature not in features:
|
||||
raise Rust2RpmConfError(f'Unrecognized "unwanted" feature: {unwanted_feature!r}')
|
||||
|
||||
if buildrequires := merged.get("buildrequires"):
|
||||
settings["buildrequires"] = to_list(buildrequires)
|
||||
if testrequires := merged.get("testrequires"):
|
||||
settings["testrequires"] = to_list(testrequires)
|
||||
if bin_requires := merged.get("bin.requires"):
|
||||
settings["bin_requires"] = to_list(bin_requires)
|
||||
|
||||
if lib_requires := merged.get("lib.requires"):
|
||||
if "lib_requires" not in settings.keys():
|
||||
settings["lib_requires"] = dict()
|
||||
settings["lib_requires"][None] = to_list(lib_requires)
|
||||
|
||||
for feature in features:
|
||||
if lib_feature_requires := merged.get(f"lib+{feature}.requires"):
|
||||
if "lib_requires" not in settings.keys():
|
||||
settings["lib_requires"] = dict()
|
||||
settings["lib_requires"][feature] = to_list(lib_feature_requires)
|
||||
|
||||
return Rust2RpmConf(**settings)
|
|
@ -10,6 +10,7 @@ from cargo2rpm import rpm
|
|||
import jinja2
|
||||
|
||||
from rust2rpm import __version__, log
|
||||
from rust2rpm.conf import Rust2RpmConf
|
||||
from rust2rpm.licensing import translate_license
|
||||
from rust2rpm.metadata import guess_main_package, package_uses_rust_1_60_feature_syntax
|
||||
|
||||
|
@ -108,7 +109,7 @@ def spec_render_crate(
|
|||
patch_file_manual: Optional[str],
|
||||
license_files: list[str],
|
||||
doc_files: list[str],
|
||||
distconf,
|
||||
distconf: Rust2RpmConf,
|
||||
feature_flags: FeatureFlags,
|
||||
relative_license_paths: bool,
|
||||
rpmautospec: bool,
|
||||
|
@ -149,25 +150,15 @@ def spec_render_crate(
|
|||
rpm_requires = {feature: list(sorted(rpm.requires(package, feature))) for feature in features}
|
||||
rpm_provides = {feature: rpm.provides(package, feature) for feature in features}
|
||||
|
||||
conf_buildrequires = to_list(distconf.get("buildrequires"))
|
||||
conf_buildrequires.sort()
|
||||
|
||||
conf_test_requires = to_list(distconf.get("testrequires"))
|
||||
conf_test_requires.sort()
|
||||
|
||||
conf_bin_requires = to_list(distconf.get("bin.requires"))
|
||||
conf_bin_requires.sort()
|
||||
|
||||
conf_lib_requires = dict()
|
||||
for feature in features:
|
||||
if feature is None:
|
||||
conf_key = "lib"
|
||||
else:
|
||||
conf_key = f"lib+{feature}"
|
||||
conf_lib_requires[conf_key] = to_list(distconf.get(f"{conf_key}.requires"))
|
||||
conf_lib_requires[conf_key] = distconf.lib_requires.get(feature) or list()
|
||||
|
||||
conf_unwanted_features = to_list(distconf.get("unwanted-features"))
|
||||
for feature in conf_unwanted_features:
|
||||
for feature in distconf.unwanted_features:
|
||||
features.remove(feature)
|
||||
|
||||
if package_uses_rust_1_60_feature_syntax(package.features):
|
||||
|
@ -219,9 +210,9 @@ def spec_render_crate(
|
|||
"crate_version": package.version,
|
||||
"crate_license": package.license,
|
||||
# Parameters derived from rust2rpm.conf
|
||||
"conf_buildrequires": conf_buildrequires,
|
||||
"conf_test_requires": conf_test_requires,
|
||||
"conf_bin_requires": conf_bin_requires,
|
||||
"conf_buildrequires": distconf.buildrequires,
|
||||
"conf_test_requires": distconf.testrequires,
|
||||
"conf_bin_requires": distconf.bin_requires,
|
||||
"conf_lib_requires": conf_lib_requires,
|
||||
# Parameters derived from command-line flags
|
||||
"cargo_args": cargo_args,
|
||||
|
@ -260,7 +251,7 @@ def spec_render_workspace(
|
|||
rpm_name: str,
|
||||
license_files: list[str],
|
||||
doc_files: list[str],
|
||||
distconf,
|
||||
distconf: Rust2RpmConf,
|
||||
feature_flags: FeatureFlags,
|
||||
rpmautospec: bool,
|
||||
auto_changelog_entry: bool,
|
||||
|
@ -291,15 +282,6 @@ def spec_render_workspace(
|
|||
rpm_buildrequires = list(sorted(buildrequires))
|
||||
rpm_test_requires = list(sorted(test_requires))
|
||||
|
||||
conf_buildrequires = to_list(distconf.get("buildrequires"))
|
||||
conf_buildrequires.sort()
|
||||
|
||||
conf_test_requires = to_list(distconf.get("testrequires"))
|
||||
conf_test_requires.sort()
|
||||
|
||||
conf_bin_requires = to_list(distconf.get("bin.requires"))
|
||||
conf_bin_requires.sort()
|
||||
|
||||
if any(package_uses_rust_1_60_feature_syntax(package.features) for package in metadata.packages):
|
||||
rust_packaging_dep = "cargo-rpm-macros >= 24"
|
||||
else:
|
||||
|
@ -346,9 +328,9 @@ def spec_render_workspace(
|
|||
"rpm_binary_names": binaries,
|
||||
"rpm_cdylib_package": is_cdylib,
|
||||
# Parameters derived from rust2rpm.conf
|
||||
"conf_buildrequires": conf_buildrequires,
|
||||
"conf_test_requires": conf_test_requires,
|
||||
"conf_bin_requires": conf_bin_requires,
|
||||
"conf_buildrequires": distconf.buildrequires,
|
||||
"conf_test_requires": distconf.testrequires,
|
||||
"conf_bin_requires": distconf.bin_requires,
|
||||
# Parameters derived from command-line flags
|
||||
"cargo_args": cargo_args,
|
||||
"use_rpmautospec": rpmautospec,
|
||||
|
|
25
rust2rpm/tests/samples/glib-sys-0.17.2.rust2rpm.conf
Normal file
25
rust2rpm/tests/samples/glib-sys-0.17.2.rust2rpm.conf
Normal file
|
@ -0,0 +1,25 @@
|
|||
[DEFAULT]
|
||||
unwanted-features =
|
||||
v2_76
|
||||
buildrequires =
|
||||
pkgconfig(glib-2.0) >= 2.56
|
||||
lib.requires =
|
||||
pkgconfig(glib-2.0) >= 2.56
|
||||
lib+v2_58.requires =
|
||||
pkgconfig(glib-2.0) >= 2.58
|
||||
lib+v2_60.requires =
|
||||
pkgconfig(glib-2.0) >= 2.60
|
||||
lib+v2_62.requires =
|
||||
pkgconfig(glib-2.0) >= 2.62
|
||||
lib+v2_64.requires =
|
||||
pkgconfig(glib-2.0) >= 2.64
|
||||
lib+v2_66.requires =
|
||||
pkgconfig(glib-2.0) >= 2.66
|
||||
lib+v2_68.requires =
|
||||
pkgconfig(glib-2.0) >= 2.68
|
||||
lib+v2_70.requires =
|
||||
pkgconfig(glib-2.0) >= 2.70
|
||||
lib+v2_72.requires =
|
||||
pkgconfig(glib-2.0) >= 2.72
|
||||
lib+v2_74.requires =
|
||||
pkgconfig(glib-2.0) >= 2.74
|
|
@ -0,0 +1,8 @@
|
|||
[DEFAULT]
|
||||
buildrequires =
|
||||
pkgconfig(sqlcipher)
|
||||
pkgconfig(sqlite3) >= 3.7.16
|
||||
lib.requires =
|
||||
pkgconfig(sqlite3) >= 3.7.16
|
||||
lib+sqlcipher.requires =
|
||||
pkgconfig(sqlcipher)
|
63
rust2rpm/tests/test_conf.py
Normal file
63
rust2rpm/tests/test_conf.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
from importlib import resources
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
|
||||
from rust2rpm.conf import Rust2RpmConf
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"filename,features,all_features,unwanted_features,buildrequires,testrequires,bin_requires,lib_requires",
|
||||
[
|
||||
(
|
||||
"libsqlite3-sys-0.25.2.rust2rpm.conf",
|
||||
{"sqlcipher"},
|
||||
False,
|
||||
list(),
|
||||
["pkgconfig(sqlcipher)", "pkgconfig(sqlite3) >= 3.7.16"],
|
||||
list(),
|
||||
list(),
|
||||
{None: ["pkgconfig(sqlite3) >= 3.7.16"], "sqlcipher": ["pkgconfig(sqlcipher)"]},
|
||||
),
|
||||
(
|
||||
"glib-sys-0.17.2.rust2rpm.conf",
|
||||
{"v2_58", "v2_60", "v2_62", "v2_64", "v2_66", "v2_68", "v2_70", "v2_72", "v2_74", "v2_76"},
|
||||
False,
|
||||
["v2_76"],
|
||||
["pkgconfig(glib-2.0) >= 2.56"],
|
||||
list(),
|
||||
list(),
|
||||
{
|
||||
None: ["pkgconfig(glib-2.0) >= 2.56"],
|
||||
"v2_58": ["pkgconfig(glib-2.0) >= 2.58"],
|
||||
"v2_60": ["pkgconfig(glib-2.0) >= 2.60"],
|
||||
"v2_62": ["pkgconfig(glib-2.0) >= 2.62"],
|
||||
"v2_64": ["pkgconfig(glib-2.0) >= 2.64"],
|
||||
"v2_66": ["pkgconfig(glib-2.0) >= 2.66"],
|
||||
"v2_68": ["pkgconfig(glib-2.0) >= 2.68"],
|
||||
"v2_70": ["pkgconfig(glib-2.0) >= 2.70"],
|
||||
"v2_72": ["pkgconfig(glib-2.0) >= 2.72"],
|
||||
"v2_74": ["pkgconfig(glib-2.0) >= 2.74"],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_rust2rpm_conf_load(
|
||||
filename: str,
|
||||
features: set[str],
|
||||
all_features: bool,
|
||||
unwanted_features: list[str],
|
||||
buildrequires: list[str],
|
||||
testrequires: list[str],
|
||||
bin_requires: list[str],
|
||||
lib_requires: dict[Optional[str], list[str]],
|
||||
):
|
||||
path = str(resources.files("rust2rpm.tests.samples").joinpath(filename))
|
||||
conf = Rust2RpmConf.load(path, "fedora", features)
|
||||
|
||||
assert conf.all_features == all_features
|
||||
assert conf.unwanted_features == unwanted_features
|
||||
assert conf.buildrequires == buildrequires
|
||||
assert conf.testrequires == testrequires
|
||||
assert conf.bin_requires == bin_requires
|
||||
assert conf.lib_requires == lib_requires
|
|
@ -8,6 +8,7 @@ from cargo2rpm.metadata import Metadata, FeatureFlags
|
|||
import pytest
|
||||
|
||||
from rust2rpm.cli import get_parser
|
||||
from rust2rpm.conf import Rust2RpmConf
|
||||
from rust2rpm.generator import to_list, spec_render_crate, spec_render_workspace
|
||||
from rust2rpm.patching import drop_foreign_dependencies
|
||||
from rust2rpm.utils import package_name_suffixed
|
||||
|
@ -51,7 +52,7 @@ def test_spec_file_render_crate(filename: str, target: str, tmp_path: Path):
|
|||
patch_file_manual=f"{crate}-patch2.diff",
|
||||
license_files=["LIC1", "LIC2"],
|
||||
doc_files=["DOC1", "DOC2"],
|
||||
distconf={},
|
||||
distconf=Rust2RpmConf(),
|
||||
feature_flags=FeatureFlags(),
|
||||
relative_license_paths=False,
|
||||
rpmautospec=target == "fedora",
|
||||
|
@ -64,7 +65,7 @@ def test_spec_file_render_crate(filename: str, target: str, tmp_path: Path):
|
|||
|
||||
fixture_path = resources.files("rust2rpm.tests.samples").joinpath(f"{crate_name_version}.{target}.spec")
|
||||
|
||||
if os.getenv("UPDATE_FIXTURES") == "1":
|
||||
if os.getenv("UPDATE_FIXTURES") == "1": # pragma nocover
|
||||
# helper mode to create test data
|
||||
fixture_path.write_text(rendered)
|
||||
|
||||
|
@ -88,7 +89,7 @@ def test_spec_file_render_workspace(filename: str, target: str, tmp_path: Path):
|
|||
rpm_name=crate,
|
||||
license_files=["LIC1", "LIC2"],
|
||||
doc_files=["DOC1", "DOC2"],
|
||||
distconf={},
|
||||
distconf=Rust2RpmConf(),
|
||||
feature_flags=FeatureFlags(),
|
||||
rpmautospec=target == "fedora",
|
||||
auto_changelog_entry=True,
|
||||
|
@ -100,7 +101,7 @@ def test_spec_file_render_workspace(filename: str, target: str, tmp_path: Path):
|
|||
|
||||
fixture_path = resources.files("rust2rpm.tests.samples").joinpath(f"{crate_name_version}.{target}.spec")
|
||||
|
||||
if os.getenv("UPDATE_FIXTURES") == "1":
|
||||
if os.getenv("UPDATE_FIXTURES") == "1": # pragma nocover
|
||||
# helper mode to create test data
|
||||
fixture_path.write_text(rendered)
|
||||
|
||||
|
@ -189,7 +190,7 @@ def test_drop_foreign_dependencies(filename: str, features: set[str], expected:
|
|||
toml_before = before_path.read_text().split("\n")
|
||||
patched = drop_foreign_dependencies(toml_before, features) or toml_before
|
||||
|
||||
if os.getenv("UPDATE_FIXTURES") == "1":
|
||||
if os.getenv("UPDATE_FIXTURES") == "1": # pragma nocover
|
||||
# helper mode to create / update test fixtures
|
||||
after_path.write_text("\n".join(patched))
|
||||
|
||||
|
|
Loading…
Reference in a new issue