From 4fbc36c7ff5ce2a3114993a7a72c8fecb9426f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Fri, 19 Mar 2021 14:41:43 +0000 Subject: [PATCH] More R package support. --- ognibuild/buildsystem.py | 29 ++++++++++++++++++++++++++++- ognibuild/outputs.py | 12 ++++++++++++ ognibuild/requirements.py | 25 +++++++++++++++++++++++++ ognibuild/resolver/__init__.py | 18 ++++++++++++++++-- 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/ognibuild/buildsystem.py b/ognibuild/buildsystem.py index a3c7a74..7a03938 100644 --- a/ognibuild/buildsystem.py +++ b/ognibuild/buildsystem.py @@ -27,6 +27,7 @@ from . import shebang_binary, UnidentifiedError from .outputs import ( BinaryOutput, PythonPackageOutput, + RPackageOutput, ) from .requirements import ( BinaryRequirement, @@ -34,6 +35,7 @@ from .requirements import ( PerlModuleRequirement, NodePackageRequirement, CargoCrateRequirement, + RPackageRequirement, ) from .fix_build import run_with_build_fixers @@ -456,16 +458,41 @@ class R(BuildSystem): return "%s(%r)" % (type(self).__name__, self.path) def build(self, session, resolver, fixers): + pass + + def dist(self, session, resolver, fixers, quiet=False): run_with_build_fixers(session, ["R", "CMD", "build", "."], fixers) + def install(self, session, resolver, fixers, install_target): + run_with_build_fixers(session, ["R", "CMD", "INSTALL", "."], fixers) + def test(self, session, resolver, fixers): - run_with_build_fixers(session, ["R", "CMD", "test", "."], fixers) + run_with_build_fixers(session, ["R", "CMD", "check", "."], fixers) @classmethod def probe(cls, path): if os.path.exists(os.path.join(path, 'DESCRIPTION')): return cls(path) + def _read_description(self): + path = os.path.join(self.path, 'DESCRIPTION') + from email.parser import BytesParser + with open(path, 'rb') as f: + return BytesParser().parse(f) + + def get_declared_dependencies(self, session, fixers=None): + description = self._read_description() + if 'Suggests' in description: + suggests = [s.strip() for s in description['Suggests'].split(',') if s.strip()] + for s in suggests: + # TODO(jelmer): Look at version + yield "build", RPackageRequirement.from_str(s) + + def get_declared_outputs(self, session, fixers=None): + description = self._read_description() + if 'Package' in description: + yield RPackageOutput(description['Package']) + class Meson(BuildSystem): diff --git a/ognibuild/outputs.py b/ognibuild/outputs.py index ba1bf85..a39caa9 100644 --- a/ognibuild/outputs.py +++ b/ognibuild/outputs.py @@ -46,3 +46,15 @@ class PythonPackageOutput(UpstreamOutput): self.name, self.python_version, ) + + +class RPackageOutput(UpstreamOutput): + def __init__(self, name): + super(RPackageOutput, self).__init__("r-package") + self.name = name + + def __str__(self): + return "R package: %s" % self.name + + def __repr__(self): + return "%s(%r)" % (type(self).__name__, self.name) diff --git a/ognibuild/requirements.py b/ognibuild/requirements.py index 92730e8..b789845 100644 --- a/ognibuild/requirements.py +++ b/ognibuild/requirements.py @@ -17,6 +17,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import posixpath +import re import subprocess from typing import Optional, List, Tuple, Set @@ -249,6 +250,30 @@ class RPackageRequirement(Requirement): self.package = package self.minimum_version = minimum_version + def __repr__(self): + return "%s(%r, minimum_version=%r)" % ( + type(self).__name__, + self.package, + self.minimum_version, + ) + + def __str__(self): + if self.minimum_version: + return "R package: %s (>= %s)" % (self.package, self.minimum_version) + else: + return "R package: %s" % (self.package,) + + @classmethod + def from_str(cls, text): + # TODO(jelmer): More complex parser + m = re.fullmatch(r'(.*) \(>= (.*)\)', text) + if m: + return cls(m.group(1), m.group(2)) + m = re.fullmatch(r'([^ ]+)', text) + if m: + return cls(m.group(1)) + raise ValueError(text) + class LibraryRequirement(Requirement): diff --git a/ognibuild/resolver/__init__.py b/ognibuild/resolver/__init__.py index a0c0e71..d3ebee6 100644 --- a/ognibuild/resolver/__init__.py +++ b/ognibuild/resolver/__init__.py @@ -76,8 +76,8 @@ class CPANResolver(Resolver): raise UnsatisfiedRequirements(missing) -class CRANResolver(Resolver): - def __init__(self, session, repos='"http://cran.r-project.org'): +class RResolver(Resolver): + def __init__(self, session, repos): self.session = session self.repos = repos @@ -114,6 +114,19 @@ class CRANResolver(Resolver): raise UnsatisfiedRequirements(missing) +class CRANResolver(RResolver): + + def __init__(self, session): + super(CRANResolver, self).__init__(session, 'http://cran.r-project.org') + + +class BioconductorResolver(RResolver): + + def __init__(self, session): + super(BioconductorResolver, self).__init__( + session, 'https://hedgehog.fhcrc.org/bioconductor') + + class HackageResolver(Resolver): def __init__(self, session): self.session = session @@ -307,6 +320,7 @@ NATIVE_RESOLVER_CLS = [ GoResolver, HackageResolver, CRANResolver, + BioconductorResolver, ]