6923fb107d
Closes: https://pagure.io/fedora-rust/rust2rpm/issue/11 Signed-off-by: Igor Gnatenko <ignatenkobrain@fedoraproject.org>
206 lines
6.1 KiB
Python
206 lines
6.1 KiB
Python
import argparse
|
|
import os
|
|
import tarfile
|
|
import tempfile
|
|
import subprocess
|
|
import sys
|
|
|
|
import jinja2
|
|
import jinja2.ext
|
|
import jinja2.exceptions
|
|
import requests
|
|
import tqdm
|
|
|
|
from rust2rpm import Metadata
|
|
|
|
# See: http://jinja.pocoo.org/docs/latest/extensions/#example-extension
|
|
class RaiseExtension(jinja2.ext.Extension):
|
|
# a set of names that trigger the extension.
|
|
tags = set(["raise"])
|
|
|
|
def parse(self, parser):
|
|
# the first token is the token that started the tag. In our case
|
|
# we only listen to ``'raise'`` so this will be a name token with
|
|
# `raise` as value. We get the line number so that we can give
|
|
# that line number to the nodes we create by hand.
|
|
lineno = next(parser.stream).lineno
|
|
|
|
# Extract the message from the template
|
|
message_node = parser.parse_expression()
|
|
|
|
return jinja2.nodes.CallBlock(
|
|
self.call_method("_raise", [message_node], lineno=lineno),
|
|
[], [], [], lineno=lineno)
|
|
|
|
def _raise(self, msg, caller):
|
|
raise jinja2.exceptions.TemplateRuntimeError(msg)
|
|
|
|
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/"
|
|
TEMPLATE = """# Generated by rust2rpm
|
|
{% set bins = md.targets|selectattr("kind", "equalto", "bin")|list() %}
|
|
{% set libs = md.targets|selectattr("kind", "equalto", "lib")|list() %}
|
|
{% set is_bin = bins|length > 0 %}
|
|
{% set is_lib = libs|length > 0 %}
|
|
{% if is_bin and not is_lib %}
|
|
{% set include_debug = True %}
|
|
{% set name = "%{crate}" %}
|
|
{% set include_main = True %}
|
|
{% set name_devel = None %}
|
|
{% elif is_lib and not is_bin %}
|
|
{% set include_debug = False %}
|
|
{% set name = "rust-%{crate}" %}
|
|
{% set include_main = False %}
|
|
{% set name_devel = " devel" %}
|
|
{% elif is_bin and is_lib %}
|
|
{% set include_debug = True %}
|
|
{% set name = "%{crate}" %}
|
|
{% set include_main = True %}
|
|
{% set name_devel = "-n rust-%{crate}-devel" %}
|
|
{% else %}
|
|
{% raise "No bins and no libs" %}
|
|
{% endif %}
|
|
%bcond_without check
|
|
{% if not include_debug %}
|
|
%global debug_package %{nil}
|
|
{% endif %}
|
|
|
|
%global crate {{ md.name }}
|
|
|
|
Name: {{ name }}
|
|
Version: {{ md.version }}
|
|
Release: 1%{?dist}
|
|
Summary: # FIXME
|
|
|
|
License: {{ md.license|default("# FIXME") }}
|
|
URL: https://crates.io/crates/{{ md.name }}
|
|
Source0: https://crates.io/api/v1/crates/%{crate}/%{version}/download#/%{crate}-%{version}.crate
|
|
|
|
ExclusiveArch: %{rust_arches}
|
|
|
|
BuildRequires: rust
|
|
BuildRequires: cargo
|
|
{% for req in md.build_requires|sort(attribute="name") %}
|
|
BuildRequires: {{ req }}
|
|
{% endfor %}
|
|
{% for con in md.build_conflicts|sort(attribute="name") %}
|
|
BuildConflicts: {{ con }}
|
|
{% endfor %}
|
|
{% if md.test_requires|length > 0 %}
|
|
%if %{with check}
|
|
{% for req in md.test_requires|sort(attribute="name") %}
|
|
BuildRequires: {{ req }}
|
|
{% endfor %}
|
|
{% for con in md.test_conflicts|sort(attribute="name") %}
|
|
BuildConflicts: {{ con }}
|
|
{% endfor %}
|
|
%endif
|
|
{% endif %}
|
|
|
|
%description
|
|
%{summary}.
|
|
|
|
{% if name_devel is not none %}
|
|
%package {{ name_devel }}
|
|
Summary: %{summary}
|
|
BuildArch: noarch
|
|
{% if target == "epel-7" %}
|
|
{% for prv in md.provides %}
|
|
Provides: {{ prv }}
|
|
{% endfor %}
|
|
{% for req in md.requires|sort(attribute="name") %}
|
|
Requires: {{ req }}
|
|
{% endfor %}
|
|
{% for con in md.conflicts|sort(attribute="name") %}
|
|
Conflicts: {{ con }}
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
%description {{ name_devel }}
|
|
%{summary}.
|
|
|
|
{% endif %}
|
|
%prep
|
|
%autosetup -n %{crate}-%{version}
|
|
%cargo_prep
|
|
|
|
%build
|
|
%cargo_build
|
|
|
|
%install
|
|
%cargo_install
|
|
|
|
%if %{with check}
|
|
%check
|
|
%cargo_test
|
|
%endif
|
|
|
|
{% if include_main %}
|
|
%files
|
|
{% for bin in bins %}
|
|
%{_bindir}/{{ bin.name }}
|
|
{% endfor %}
|
|
|
|
{% endif %}
|
|
{% if name_devel is not none %}
|
|
%files {{ name_devel }}
|
|
{% if md.license_file is not none %}
|
|
%license {{ md.license_file }}
|
|
{% endif %}
|
|
%{cargo_registry}/%{crate}-%{version}/
|
|
|
|
{% endif %}
|
|
%changelog
|
|
"""
|
|
JINJA_ENV = jinja2.Environment(undefined=jinja2.StrictUndefined,
|
|
extensions=[RaiseExtension],
|
|
trim_blocks=True, lstrip_blocks=True)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-t", "--target", choices=("epel-7", "fedora-26"), required=True,
|
|
help="Distribution target")
|
|
parser.add_argument("crate", help="crates.io name")
|
|
parser.add_argument("version", nargs="?", help="crates.io version")
|
|
args = parser.parse_args()
|
|
|
|
if args.version is None:
|
|
# Now we need to get latest version
|
|
url = requests.compat.urljoin(API_URL, "crates/{}/versions".format(args.crate))
|
|
req = requests.get(url)
|
|
req.raise_for_status()
|
|
args.version = req.json()["versions"][0]["num"]
|
|
|
|
if not os.path.isdir(CACHEDIR):
|
|
os.mkdir(CACHEDIR)
|
|
cratef_base = "{}-{}.crate".format(args.crate, args.version)
|
|
cratef = os.path.join(CACHEDIR, cratef_base)
|
|
if not os.path.isfile(cratef):
|
|
url = requests.compat.urljoin(API_URL, "crates/{}/{}/download#".format(args.crate, args.version))
|
|
req = requests.get(url, stream=True)
|
|
req.raise_for_status()
|
|
total = int(req.headers["Content-Length"])
|
|
with open(cratef, "wb") as f:
|
|
for chunk in tqdm.tqdm(req.iter_content(), "Downloading {}".format(cratef_base),
|
|
total=total, unit="B", unit_scale=True):
|
|
f.write(chunk)
|
|
|
|
files = []
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
target_dir = "{}/".format(tmpdir)
|
|
with tarfile.open(cratef, "r") as archive:
|
|
for n in archive.getnames():
|
|
if not os.path.abspath(os.path.join(target_dir, n)).startswith(target_dir):
|
|
raise Exception("Unsafe filenames!")
|
|
archive.extractall(target_dir)
|
|
toml = "{}/{}-{}/Cargo.toml".format(tmpdir, args.crate, args.version)
|
|
assert os.path.isfile(toml)
|
|
|
|
metadata = Metadata.from_file(toml)
|
|
|
|
template = JINJA_ENV.from_string(TEMPLATE)
|
|
print(template.render(target=args.target, md=metadata))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|