From 2233404f989685b9813a513ed4f6abdc39629c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 11 Aug 2018 20:13:18 +0200 Subject: [PATCH] Allow generating the spec file from rust project checkout This adds the possibility to generate the spec file from a rust project checkout, by specifying either the toml file or just the directory name, without having a crate at all. This is nice when developing local projects and tweaking the metadata. The expected way to use this is 'rust2rpm .../project/', look at the generated spec file, do fixes to source, regenerate spec file, etc. $ rust2rpm -t fedora --stdout . (when testing) or $ rust2rpm -t fedora /path/to/project (in the dist-git directory) Only args that contain "/" are considered local arguments. An special exception is made for "." and "..", since that's likely to be a common use case and cannot be mistaken for a remote crate name. When --patch is used, the filename is changed from name-version-fix-metadata.diff to name-fix-metadata.diff. This seems more useful, because after the crate version is updated, the patch file would either stay the same or would just need to be rebased, and it is not tied to the crate version. --- rust2rpm/__main__.py | 113 +++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/rust2rpm/__main__.py b/rust2rpm/__main__.py index 2f0758e..86acc37 100644 --- a/rust2rpm/__main__.py +++ b/rust2rpm/__main__.py @@ -1,5 +1,6 @@ import argparse import configparser +import contextlib from datetime import datetime, timezone import difflib import itertools @@ -81,6 +82,16 @@ def file_mtime(path): t = datetime.fromtimestamp(os.stat(path).st_mtime, timezone.utc) return t.astimezone().isoformat() +def local_toml(toml, version): + if os.path.isdir(toml): + toml = os.path.join(toml, 'Cargo.toml') + + return toml, None, version + +def local_crate(crate, version): + cratename, version = os.path.basename(crate)[:-6].rsplit('-', 1) + return crate, cratename, version + def download(crate, version): if version is None: # Now we need to get latest version @@ -105,36 +116,8 @@ def download(crate, version): f.write(chunk) return cratef, crate, version -def local(crate, version): - if version is not None: - raise Exception("Don't specify version when using local crates!") - assert os.path.isfile(crate) - assert crate.endswith('.crate') - cratename,version = os.path.basename(crate)[:-6].rsplit('-', 1) - cratef = crate - return cratef, cratename, version - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("-", "--stdout", action="store_true", - help="Print spec and patches into stdout") - parser.add_argument("-t", "--target", action="store", - choices=("plain", "fedora", "mageia", "opensuse"), default=get_default_target(), - help="Distribution target") - parser.add_argument("-p", "--patch", action="store_true", - help="Do initial patching of Cargo.toml") - parser.add_argument("crate", help="crates.io name") - parser.add_argument("version", nargs="?", help="crates.io version") - args = parser.parse_args() - - if args.patch: - editor = detect_editor() - - if args.crate.endswith('.crate') and os.path.isfile(args.crate): - cratef,crate,version = local(args.crate, args.version) - else: - cratef,crate,version = download(args.crate, args.version) - +@contextlib.contextmanager +def toml_from_crate(cratef, crate, version): with tempfile.TemporaryDirectory() as tmpdir: target_dir = "{}/".format(tmpdir) with tarfile.open(cratef, "r") as archive: @@ -144,26 +127,70 @@ def main(): archive.extractall(target_dir) toml_relpath = "{}-{}/Cargo.toml".format(crate, version) toml = "{}/{}".format(tmpdir, toml_relpath) - assert os.path.isfile(toml) + if not os.path.isfile(toml): + raise IOError('crate does not contain Cargo.toml file') + yield toml - if args.patch: - mtime_before = file_mtime(toml) - with open(toml, "r") as fobj: - toml_before = fobj.readlines() - subprocess.check_call([editor, toml]) - mtime_after = file_mtime(toml) - with open(toml, "r") as fobj: - toml_after = fobj.readlines() - diff = list(difflib.unified_diff(toml_before, toml_after, - fromfile=toml_relpath, tofile=toml_relpath, - fromfiledate=mtime_before, tofiledate=mtime_after)) +def make_patch(toml, enabled=True): + if not enabled: + return [] + editor = detect_editor() + + mtime_before = file_mtime(toml) + toml_before = open(toml).readlines() + subprocess.check_call([editor, toml]) + mtime_after = file_mtime(toml) + toml_after = open(toml).readlines() + toml_relpath = '/'.join(toml.split('/')[-2:]) + diff = list(difflib.unified_diff(toml_before, toml_after, + fromfile=toml_relpath, tofile=toml_relpath, + fromfiledate=mtime_before, tofiledate=mtime_after)) + return diff + +def _is_path(path): + return '/' in path or path in {'.', '..'} + +def make_diff_metadata(crate, version, patch=False): + if _is_path(crate): + # Only things that look like a paths are considered local arguments + if crate.endswith('.crate'): + cratef, crate, version = local_crate(crate, version) + else: + toml, crate, version = local_toml(crate, version) + diff = make_patch(toml, enabled=patch) + metadata = Metadata.from_file(toml) + return metadata.name, diff, metadata + else: + cratef, crate, version = download(crate, version) + + with toml_from_crate(cratef, crate, version) as toml: + diff = make_patch(toml, enabled=patch) metadata = Metadata.from_file(toml) + return crate, diff, metadata + +def main(): + parser = argparse.ArgumentParser('rust2rpm', + formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument("-", "--stdout", action="store_true", + help="Print spec and patches into stdout") + parser.add_argument("-t", "--target", action="store", + choices=("plain", "fedora", "mageia", "opensuse"), default=get_default_target(), + help="Distribution target") + parser.add_argument("-p", "--patch", action="store_true", + help="Do initial patching of Cargo.toml") + parser.add_argument("crate", help="crates.io name\n" + "path/to/local.crate\n" + "path/to/project/") + parser.add_argument("version", nargs="?", help="crates.io version") + args = parser.parse_args() + + crate, diff, metadata = make_diff_metadata(args.crate, args.version, patch=args.patch) template = JINJA_ENV.get_template("main.spec") if args.patch and len(diff) > 0: - patch_file = "{}-{}-fix-metadata.diff".format(crate, version) + patch_file = "{}-fix-metadata.diff".format(crate) else: patch_file = None