From 69c4d95409fe2957bd3f171363feb15d665362db Mon Sep 17 00:00:00 2001 From: Detlef Steuer Date: Feb 05 2020 14:09:07 +0000 Subject: Merge remote-tracking branch 'upstream/master' --- diff --git a/CHANGELOG b/CHANGELOG index a4a3b09..eb8a7c5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,7 +2,7 @@ # R2spec # # R2spec is made to help to the creation of R specfile -# It works from a source file (*.tar.gz) or a url (http://...*.tar.gz) +# It works from a source file (*.tar.gz) or a url (https://...*.tar.gz) # # # Made the 13th February 2008 @@ -141,11 +141,11 @@ # - Fill the url # * Add %define BioC for the Bioconductor release of Bioconductor packages # * Add check if the package is noarch or not -# * Add oportunity to copy the source in the %_topdir defined in .rpmmacros +# * Add opportunity to copy the source in the %_topdir defined in .rpmmacros # * Add a parameter in the config file for the version on Bioconductor used # * Add a Spec class to write the spec file # * Add a Noarch class that inherit the Spec class and handle the noarch package case -# * Ask what to do if the specfile is alreay in the current working directory +# * Ask what to do if the specfile is already in the current working directory # # -Bugs correction # * Catch the Description in the file DESCRIPTION when it is on several lines @@ -202,10 +202,10 @@ # # Distributed under License GPLv3 or later # You can find a copy of this license on the website -# http://www.gnu.org/licenses/gpl.html +# https://www.gnu.org/licenses/gpl.html # # This software has been based on the guidelines for R packaging -# http://fedoraproject.org/wiki/Packaging/R +# https://docs.fedoraproject.org/en-US/packaging-guidelines/R/ # #*********************************************** diff --git a/INSTALL b/INSTALL index d13dfa3..4273f03 100644 --- a/INSTALL +++ b/INSTALL @@ -28,7 +28,7 @@ There are four ways to install R2spec: ** Install R2spec from the RPM ** Get the latest RPM from - https://fedorahosted.org/r2spec/ + https://pagure.io/r2spec/ Run the command in the directory where your rpm file has been downloaded su -c'rpm -ivh R2spec-*.rpm' @@ -39,7 +39,7 @@ There are four ways to install R2spec: The simplest is then to reconstruct the RPM. Get the latest src.rpm from - https://fedorahosted.org/r2spec/ + https://pagure.io/r2spec/ Make sure that you have rpmbuild available and run this command if needed su -c'yum install rpm-build' @@ -51,14 +51,14 @@ There are four ways to install R2spec: ** Install R2spec from the source in a RPM-based system ** Get the latest source from - https://fedorahosted.org/r2spec/ + https://pagure.io/r2spec/ Run the command in the directory where your source file has been downloaded rpmbuild -ta R2spec-*.tar.gz ** Install R2spec from the source using python installer ** Get the latest source from - https://fedorahosted.org/r2spec/ + https://pagure.io/r2spec/ In the directory where your source file has been downloaded: Open the tarball diff --git a/LICENSE b/LICENSE index 10926e8..f288702 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,12 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. - +. diff --git a/PKG-INFO b/PKG-INFO deleted file mode 100644 index 18b469e..0000000 --- a/PKG-INFO +++ /dev/null @@ -1,11 +0,0 @@ -Metadata-Version: 1.1 -Name: R2spec -Version: 4.2.1 -Summary: R2spec is a small python tool that generates spec file and rpm for R libraries. -Home-page: https://fedorahosted.org/r2spec/ -Author: Pierre-Yves Chibon -Author-email: pingou@pingoured.fr -License: GPLv3+ -Download-URL: https://fedorahosted.org/releases/r/2/r2spec/ -Description: UNKNOWN -Platform: UNKNOWN diff --git a/R2rpm b/R2rpm index 03ea3c8..03ed167 100755 --- a/R2rpm +++ b/R2rpm @@ -19,6 +19,10 @@ """ R2rpm launcher script """ +from __future__ import absolute_import, division, print_function + +import sys + from r2spec.r2spec_obj import R2rpm, setup_parser from r2spec import R2specError @@ -26,6 +30,8 @@ if __name__ == '__main__': PARSER = setup_parser('R2rpm') ARG = PARSER.parse_args() try: - R2rpm().main(ARG) - except R2specError, err: - print err + ret = R2rpm().main(ARG) + except R2specError as err: + print(err) + ret = 1 + sys.exit(ret) diff --git a/R2rpm.1 b/R2rpm.1 index d5c378a..ffb7c4b 100644 --- a/R2rpm.1 +++ b/R2rpm.1 @@ -15,7 +15,7 @@ R2rpm \- Automatically generates RPM for R libraries. .SH DESCRIPTION This tool generate RPM for R libraries according to the Fedora guidelines for R packaging. -These guidelines are available at: https://fedoraproject.org/wiki/Packaging/R +These guidelines are available at: https://docs.fedoraproject.org/en-US/packaging-guidelines/R/ .SH USAGE This program generates RPM file for R libraries for Fedora. @@ -27,7 +27,7 @@ If found, the package will be downloaded, extracted, the information contained in the sources are read (especially those contained in the DESCRIPTION file). The sources are then removed from the current working directory (unless specified otherwise). -Finally the spec file is writen to the filesystem using the information collected. +Finally the spec file is written to the filesystem using the information collected. .HP R2rpm -u diff --git a/R2spec b/R2spec index 97d8720..7ab1c96 100755 --- a/R2spec +++ b/R2spec @@ -19,6 +19,10 @@ """ R2spec launcher script """ +from __future__ import absolute_import, division, print_function + +import sys + from r2spec.r2spec_obj import R2spec, setup_parser from r2spec import R2specError @@ -26,6 +30,8 @@ if __name__ == '__main__': PARSER = setup_parser('R2spec') ARG = PARSER.parse_args() try: - R2spec().main(ARG) - except R2specError, err: - print err + ret = R2spec().main(ARG) + except R2specError as err: + print(err) + ret = 1 + sys.exit(ret) diff --git a/R2spec.1 b/R2spec.1 index 59a70dd..b7c863e 100644 --- a/R2spec.1 +++ b/R2spec.1 @@ -15,7 +15,7 @@ R2spec \- Automatically generates RPM spec file for R libraries. .SH DESCRIPTION This tool generate RPM spec file for R libraries according to the Fedora guidelines for R packaging. -These guidelines are available at: https://fedoraproject.org/wiki/Packaging/R +These guidelines are available at: https://docs.fedoraproject.org/en-US/packaging-guidelines/R/ .SH USAGE This program generates spec file for R libraries for Fedora. @@ -27,7 +27,7 @@ If found, the package will be downloaded, extracted, the information contained in the sources are read (especially those contained in the DESCRIPTION file). The sources are then removed from the current working directory (unless specified otherwise). -Finally the spec file is writen to the filesystem using the information collected. +Finally the spec file is written to the filesystem using the information collected. .HP R2spec -u diff --git a/R2spec.spec b/R2spec.spec index 9a85405..9bb44f2 100644 --- a/R2spec.spec +++ b/R2spec.spec @@ -2,14 +2,14 @@ %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} Name: R2spec -Version: 4.2.1 +Version: 5.0.0 Release: 1%{?dist} Summary: Python script to generate R spec file Group: Development/Languages License: GPLv3+ -URL: https://fedorahosted.org/r2spec/ -Source0: https://fedorahosted.org/releases/r/2/r2spec/R2spec-%{version}.tar.gz +URL: https://pagure.io/r2spec +Source0: https://releases.pagure.org/r2spec/R2spec-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch @@ -56,6 +56,18 @@ rm -rf %{buildroot} %{_mandir}/man1/R2rpm.1.gz %changelog +* Mon Sep 09 2019 Pierre-Yves Chibon - 5.0.0-1 +- Update to 5.0.0 +- Port to python3 +- Use textwrap to format package description +- Update the spec file for more recent standards +- Use https links +- Use rpm Suggests for R Suggests +- Disable explicit Requires/Suggests by default +- Use encoding from DESCRIPTION when reading it +- Update CRAN URL to use canonical form +- Check NeedsCompilation for arch dependence if available + * Thu Jun 04 2015 Pierre-Yves Chibon - 4.2.1-1 - Bump the version diff --git a/README b/README index eed686f..0444fae 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ Name: R2spec Author: Pierre-Yves Chibon -R2spec generates RPMs spec file for R libraries according to the R guidelines of Fedora available at http://fedoraproject.org/wiki/Packaging/R. -It creates the spec file from an input which could be either a source file (*.tar.gz) or a url (http://*.tar.gz). +R2spec generates RPMs spec file for R libraries according to the R guidelines of Fedora available at https://docs.fedoraproject.org/en-US/packaging-guidelines/R/. +It creates the spec file from an input which could be either a source file (*.tar.gz) or a url (https://*.tar.gz). diff --git a/r2spec/__init__.py b/r2spec/__init__.py index 706932d..91abb56 100644 --- a/r2spec/__init__.py +++ b/r2spec/__init__.py @@ -19,6 +19,7 @@ """ Miscellaneous functions used in R2spec. """ +from __future__ import absolute_import, division, print_function import logging import shutil @@ -27,7 +28,7 @@ from subprocess import Popen, PIPE logging.basicConfig() LOG = logging.getLogger('R2spec') -VERSION = '4.2.1' +VERSION = '5.0.0' def get_logger(): @@ -40,7 +41,8 @@ def get_rpm_tag(tag): Code from José Matos. :arg tag, the rpm tag to find the value of """ - dirname = Popen(["rpm", "-E", '%' + tag], stdout=PIPE).stdout.read()[:-1] + dirname = Popen(["rpm", "-E", '%' + tag], + stdout=PIPE, universal_newlines=True).stdout.read()[:-1] return dirname @@ -48,7 +50,8 @@ def get_mock_root(): """" Calls mock to retrieve the root path and return it minus the last two levels. """ - dirname = Popen(["mock", "--print-root-path"], stdout=PIPE).stdout.read()[:-1] + dirname = Popen(["mock", "--print-root-path"], + stdout=PIPE, universal_newlines=True).stdout.read()[:-1] return dirname.rsplit('/', 3)[0] + '/' @@ -64,7 +67,7 @@ def move_sources(fullpath, sources): shutil.copyfile(fullpath, dest) -class R2specError: +class R2specError(Exception): """ R2specError class Template for all the error of the project """ diff --git a/r2spec/build.py b/r2spec/build.py index b62c236..3292fb6 100644 --- a/r2spec/build.py +++ b/r2spec/build.py @@ -19,6 +19,7 @@ """ Build related class and methods. """ +from __future__ import absolute_import, division, print_function import os import subprocess @@ -57,12 +58,12 @@ class Build: if mock_config is None or mock_config is False: cmd = 'LANG=C rpmbuild -%s %s > %s 2>&1' % (rpmarg, specname, self.buildlog) - self.log.debug(cmd) + self.log.debug('Running %s', cmd) self.outcode = subprocess.call(cmd, shell=True) else: cmd = 'LANG=C rpmbuild -bs %s > %s 2>&1' % ( specname, self.buildlog) - self.log.debug(cmd) + self.log.debug('Running %s', cmd) self.outcode = subprocess.call(cmd, shell=True) self.get_rpm() @@ -74,7 +75,7 @@ class Build: mock_resultdir, name) cmd = 'LANG=C %s >> %s.build.log 2>&1' % ( mockcommand, specname) - self.log.debug(cmd) + self.log.debug('Running %s', cmd) self.outcode = subprocess.call(cmd, shell=True) if self.outcode == 30: # When mock stop while building the cache, then check @@ -90,7 +91,7 @@ class Build: directory = get_mock_root() self.buildlog = '/%s/%s-%s/result/%s' % ( directory, mock_config, name, filename) - self.log.debug('buildlog %s' % self.buildlog) + self.log.debug('buildlog %s', self.buildlog) def get_rpm(self): """ Parses the build.log to extract the rpm generated. @@ -108,9 +109,9 @@ class Build: self.rpm.append(rpm) else: self.rpm.append(rpm) - except IOError, err: - self.log.info('Could not read the file "%s"' % self.buildlog) - self.log.debug('ERROR: %s' % err) + except IOError as err: + self.log.info('Could not read the file "%s"', self.buildlog) + self.log.debug('ERROR: %s', err) def remove_logs(self): """ Remove the log file generated while building the RPM. """ @@ -131,10 +132,10 @@ class Build: stream = open(self.buildlog, 'r') log = stream.read() stream.close() - except IOError, err: - self.log.info('An error occured during the build') - self.log.debug('ERROR: %s' % err) - print 'Stopping' + except IOError as err: + self.log.info('An error occurred during the build') + self.log.debug('ERROR: %s', err) + print('Stopping') return(1) logs = log.split('\n') @@ -168,6 +169,6 @@ class Build: if len(self.deps) == 0 and len(self.files) == 0 and self.outcode == 1: log = '%s.build.log' % specname raise BuildError( - 'An error occured at build time, see the log in %s' % log) + 'An error occurred at build time, see the log in %s' % log) return self.files diff --git a/r2spec/r2spec_obj.py b/r2spec/r2spec_obj.py index 4d84d30..9f0f076 100644 --- a/r2spec/r2spec_obj.py +++ b/r2spec/r2spec_obj.py @@ -20,15 +20,19 @@ Main functions for R2spec """ +from __future__ import absolute_import, division, print_function import argparse -import ConfigParser import logging import os import re import sys import pwd -from urlparse import urlparse + +try: + import configparser +except ImportError: + import ConfigParser as configparser from r2spec.build import Build from r2spec.rpackage import RPackage @@ -62,12 +66,13 @@ def setup_parser(prog): help='Path to local sources of a R package.') parser.add_argument('--no-check', action='store_true', help='Do not include the %%check section in the generated spec file.') - parser.add_argument('--no-suggest', action='store_true', - help='Do not include the dependencies marked as \'Suggest\' by the source as dependencies of the RPM.') + parser.add_argument('--with-deps', action='store_true', + help='Include the dependencies marked by the source as dependencies of the RPM.') parser.add_argument('--force-dl', action='store_true', help='Enforce the download of the source, even if they are already on the system.') parser.add_argument('--keep-sources', action='store_true', - help='The extracted source on the current directory will not be removed.') + help='The source will be extracted in the current ' + 'directory.') parser.add_argument('--keep-logs', action='store_true', help='Keep the log file generated while building the rpm') parser.add_argument('--repo', default=None, @@ -135,7 +140,7 @@ class Settings(object): except OSError: # os.getlogin() raises an exception when the session is not # in /var/run/utmp (non-login sessions) - if os.environ.has_key('LOGNAME'): + if 'LOGNAME' in os.environ: packager = os.environ['LOGNAME'] else: packager = pwd.getpwuid(os.getuid())[0] @@ -145,7 +150,7 @@ class Settings(object): def __init__(self): """Constructor of the Settings object. - This instanciate the Settings object and load into the _dict + This instantiates the Settings object and loads into the _dict attributes the default configuration which each available option. """ self._dict = { @@ -160,7 +165,7 @@ class Settings(object): :arg configfile, name of the configuration file loaded. :arg sec, section of the configuration retrieved. """ - parser = ConfigParser.ConfigParser() + parser = configparser.RawConfigParser() configfile = os.environ['HOME'] + "/" + configfile is_new = create_conf(configfile) parser.read(configfile) @@ -176,7 +181,7 @@ class Settings(object): :arg key, name of the parameter to set from the settings. :arg value, value of the parameter to set from the settings. """ - if not key in self._dict.keys(): + if key not in self._dict: raise KeyError(key) self._dict[key] = value @@ -185,7 +190,7 @@ class Settings(object): :arg key, name of the parameter to retrieve from the settings. """ - if not key in self._dict.keys(): + if key not in self._dict: raise KeyError(key) return self._dict[key] @@ -200,7 +205,7 @@ class Settings(object): else: opts = set() - for name in self._dict.iterkeys(): + for name in self._dict: value = None if name in opts: value = parser.get(section, name) @@ -220,7 +225,7 @@ class R2spec(object): self.log = get_logger() def main(self, args): - """ main fonction for R2spec. + """ Main function for R2spec. This is the method which actually runs R2spec and generate the spec file according to the argument given. """ @@ -232,7 +237,7 @@ class R2spec(object): settings.set('email', args.email) if args.package: - pack = RPackage(re.sub('^R-', '', args.package)) + pack = RPackage(re.sub(r'^R-', '', args.package)) pack.search_package_in_repo() elif args.url: pack = RPackage(source0=args.url) @@ -274,14 +279,13 @@ class R2spec(object): if args.package or args.url: pack.download(args.force_dl) - pack.extract_sources() + if args.keep_sources: + pack.extract_sources() pack.get_description() pack.determine_arch() - if not args.keep_sources: - pack.remove_sources() spec = Spec(settings, pack, no_check=args.no_check, - no_suggest=args.no_suggest) + with_deps=args.with_deps) spec.fill_spec_info() spec.get_template() spec.write_spec(True) @@ -305,21 +309,21 @@ class R2rpm(R2spec): def __check_build_output(self): """ Handle the output from the build, if it succeeded we stop otherwise the parse the error and act accordingly. - Return False if the build was successful, retun True otherwise. + Return False if the build was successful, return True otherwise. """ specfile = self.spec.get_specfile() if self.build.outcode == 0: - self.log.info("RPM %s done" % specfile) + self.log.info("RPM %s done", specfile) # Get the list of rpm generated self.build.get_rpm() - print "RPM generated:" - print "\n".join(self.build.rpm) + print("RPM generated:") + print("\n".join(self.build.rpm)) return False else: return True def main(self, args): - """ main fonction for R2rpm. + """ Main function for R2rpm. This is the method which actually runs R2rpm, it generates the spec files, cleans its %files section, build it and fill again the %files section. @@ -327,7 +331,7 @@ class R2rpm(R2spec): specfile = '' if args.package: - self.pack.name = re.sub('^R-', '', args.package) + self.pack.name = re.sub(r'^R-', '', args.package) self.spec.package = self.pack specfile = self.spec.get_specfile() self.pack.search_package_in_repo() @@ -362,8 +366,9 @@ class R2rpm(R2spec): # Generate the spec file self.pack = super(R2rpm, self).main(args) else: - self.log.info('Spec file "%s" is already there, no need to regenerate it' - % specfile) + self.log.info( + 'Spec file "%s" is already there, no need to regenerate it', + specfile) if args.package or args.url: self.pack.download(args.force_dl) @@ -374,7 +379,7 @@ class R2rpm(R2spec): self.spec.clean_files_section() self.spec.write_spec() - print 'Building... %s' % self.pack.name + print('Building... %s' % self.pack.name) self.build.build(specfile, mock_config=args.mock_config, mock_resultdir=args.mock_resultdir) if self.__check_build_output(): @@ -389,17 +394,17 @@ class R2rpm(R2spec): # Rebuild the package when build has failed the first time if self.build.outcode: # == 1 when build failed and 0 when build passed - print 'Re-building... %s' % self.pack.name + print('Re-building... %s' % self.pack.name) self.build.build(specfile, mock_config=args.mock_config, mock_resultdir=args.mock_resultdir) self.__check_build_output() - except BuildDepencenciesError, err: - print 'Missing dependencies to build %s' % self.pack.name - self.log.info(err) + except BuildDepencenciesError as err: + print('Missing dependencies to build %s' % self.pack.name) + self.log.info('%s', err) return 1 - except BuildError, err: - print 'An error occured during the build of %s' % self.pack.name - self.log.debug('Build ERROR: %s' % err) + except BuildError as err: + print('An error occurred during the build of %s' % self.pack.name) + self.log.debug('Build ERROR: %s', err) return 1 if not args.keep_logs: self.build.remove_logs() diff --git a/r2spec/rpackage.py b/r2spec/rpackage.py index 77a4c9a..98f9ecb 100644 --- a/r2spec/rpackage.py +++ b/r2spec/rpackage.py @@ -20,16 +20,21 @@ R packages class. """ +from __future__ import absolute_import, division, print_function -import ConfigParser import os import re -import shutil import sys import tarfile -import urllib2 -from tarfile import TarError +try: + import configparser +except ImportError: + import ConfigParser as configparser +try: + import urllib.request as urllib_request +except ImportError: + import urllib2 as urllib_request from r2spec import get_logger, get_rpm_tag, R2specError @@ -44,21 +49,21 @@ def package_in_repo(url, name): """ log = get_logger() try: - stream = urllib2.urlopen(url) - content = stream.read() + stream = urllib_request.urlopen(url) + content = stream.read().decode() stream.close() - sourcemotif = re.compile("Package:\s+%s\n" % name) + sourcemotif = re.compile(r"Package:\s+%s\n" % name) result = sourcemotif.search(content) if result is not None: - log.info("Package found in : %s" % url) - versionmotif = re.compile("Package:\s+%s\nVersion:(.*)" % name) + log.info("Package found in : %s", url) + versionmotif = re.compile(r"Package:\s+%s\nVersion:(.*)" % name) version = versionmotif.search(content).group(1).strip() return (version) else: - log.info("Not Found: %s in %s" % (name, url)) - except IOError, ex: - print 'Could not contact the repository at url: %s' % url - log.debug('Error: %s' % ex) + log.info("Not Found: %s in %s", name, url) + except IOError as ex: + print('Could not contact the repository at url: %s' % url) + log.debug('Error: %s', ex) return None @@ -68,7 +73,7 @@ class RPackage(object): def __init__(self, name=None, url=None, source0=None): """ Constructor. """ self.name = name - parser = ConfigParser.ConfigParser() + parser = configparser.RawConfigParser() parser.read('/etc/R2spec/repos.cfg') self.config = parser self.log = get_logger() @@ -84,11 +89,19 @@ class RPackage(object): def determine_arch(self): """ Determine if the package is arch or noarch by looking at the sources. - Set arch to True if the package is arch dependant. + Set arch to True if the package is arch dependent. Set arch to False if the package is noarch. Let arch to None if could not determine. """ - self.log.info('Determining if the package is arch dependant or not') + self.log.info('Determining if the package is arch dependent or not') + try: + self.arch = self.description['NeedsCompilation'].lower() != 'no' + except KeyError: + pass + else: + self.log.info('Package is %sarch dependent', + '' if self.arch else 'not ') + return extensions = ['c', 'C', 'cp', 'cpp', 'h', 'H',] if os.path.exists(self.name): for root, dirs, files in os.walk(self.name): @@ -99,10 +112,10 @@ class RPackage(object): or 'f' in extension \ or 'F' in extension: self.arch = True - self.log.info('Package is arch dependant') + self.log.info('Package is arch dependent') return self.arch = False - self.log.info('Package is not arch dependant') + self.log.info('Package is not arch dependent') return else: self.log.info( @@ -129,26 +142,35 @@ class RPackage(object): return url = self.source0.rsplit('/', 1)[0] - url = '%s/%s' % (url,self.source) - self.log.info('Downloading %s' % url) + url = '%s/%s' % (url, self.source) + self.log.info('Downloading %s', url) - remotefile = urllib2.urlopen(url) - localfile = open(sources, 'w') - localfile.write(remotefile.read()) - localfile.close() + remotefile = urllib_request.urlopen(url) + with open(sources, 'wb') as localfile: + localfile.write(remotefile.read()) - def extract_sources(self): - """ Extract the sources into the current directory. """ + def open_sources(self): + """ Open the source tarball. """ sourcedir = get_rpm_tag('_sourcedir') tarball = "%s/%s" % (sourcedir, self.source) - self.log.info("Opening: %s" % tarball) + self.log.info("Opening: %s", tarball) try: - tar = tarfile.open(tarball) - tar.extractall() - tar.close() - except TarError, err: + return tarfile.open(tarball) + except tarfile.TarError as err: self.log.debug("Error while extracting the tarball") - self.log.debug("ERROR: %s" % err) + self.log.debug("ERROR: %s", err) + + def extract_sources(self): + """ Extract the sources into the current directory. """ + tar = self.open_sources() + if tar is None: + return + try: + with tar: + tar.extractall() + except tarfile.TarError as err: + self.log.debug("Error while extracting the tarball") + self.log.debug("ERROR: %s", err) def get(self, key): """ Retrieve the given key from the description information known @@ -158,7 +180,7 @@ class RPackage(object): :arg key, the key to retrieve from the DESCRIPTION file of the R package """ - if key and key in self.description.keys(): + if key and key in self.description: return self.description[key] else: return '' @@ -168,51 +190,58 @@ class RPackage(object): from in them. """ description = '%s/DESCRIPTION' % self.name - self.log.info('Loading "%s"' % description) + content = None if os.path.exists(self.name) and os.path.isfile(description): + self.log.info('Loading "%s" from extracted sources', description) try: - stream = open(description, 'r') - content = stream.read() - stream.close() - except IOError, err: + with open(description, 'rb') as stream: + content = stream.read() + except IOError as err: self.log.info( - 'An error occured while reading the DESCRIPTION file: %s' \ - % description) - self.log.debug('ERROR: %s' % err) - key = None - for row in content.split('\n'): - if row.strip(): - pattern = re.compile("\w:*") - if pattern.match(row): - key, value = row.split(':', 1) - self.description[key.strip()] = value.strip() - else: - self.description[key] = self.description[key] + ' ' + \ - row.strip() + 'An error occurred while reading the DESCRIPTION file: %s', + description) + self.log.debug('ERROR: %s', err) + else: + self.log.info('Loading "%s" from tarball', description) + tar = self.open_sources() + if tar is not None: + try: + with tar: + with tar.extractfile(description) as stream: + content = stream.read() + except tarfile.TarError as err: + self.log.debug("Error while extracting the DESCRIPTION " + "file from the tarball") + self.log.debug("ERROR: %s", err) + else: + self.log.info('Could not find a DESCRIPTION file "%s" to read', + description) + return + + encoding = re.search(b'^Encoding: (.+)$', content, re.MULTILINE) + if encoding is not None: + content = content.decode(encoding.group(1).decode().strip()) else: - self.log.info('Could not find a DESCRIPTION file "%s" to read' \ - % description) + content = content.decode('ascii') + key = None + for row in content.split('\n'): + if row.strip(): + pattern = re.compile(r"\w:*") + if pattern.match(row): + key, value = row.split(':', 1) + self.description[key.strip()] = value.strip() + else: + self.description[key] = self.description[key] + ' ' + \ + row.strip() def read_config(self): """ Read the general configuration containing the repo information """ - parser = ConfigParser.ConfigParser() + parser = configparser.RawConfigParser() configfile = '/etc/R2spec/config' parser.read(configfile) self.config = parser - def remove_sources(self): - """ Remove the source we extracted in the current working - directory. - """ - self.log.info('Removing extracted sources: "%s"' % self.name) - try: - shutil.rmtree(self.name) - except (IOError, OSError), err: - self.log.info('Could not remove the extracted sources: "%s"'\ - % self.name) - self.log.debug('ERROR: %s' % err) - def search_package_in_repo(self): """ Search a package in all R repositories listed in the general configuration file. @@ -228,8 +257,8 @@ class RPackage(object): break if version is None: self.log.info( - 'Could not find package "%s" in any of the congifure repos' \ - % self.name) + 'Could not find package "%s" in any of the configured repos', + self.name) raise R2specError( 'Could not find package "%s" in any of the configured repos' \ % self.name) @@ -237,9 +266,7 @@ class RPackage(object): self.down_version = version.replace('-', '.') self.url = self.config.get(repo, 'url') self.source0 = self.config.get(repo, 'source') - if self.up_version != self.down_version: - self.source0 = self.source0.replace('%{version}', self.up_version) - self.source = '%s_%s.tar.gz' % (self.name,self.up_version) + self.source = '%s_%s.tar.gz' % (self.name, self.up_version) def set_repo(self, reponame): """ This function find the URL and Source0 tag for the spec file @@ -249,7 +276,6 @@ class RPackage(object): """ for section in self.config.sections(): if section == 'repo:%s' % reponame: - """ Not sure why this format is getting filled out...""" - self.url = self.config.get(section, 'url' % (self.name)) + self.url = self.config.get(section, 'url') self.source0 = self.config.get(section, 'source') break diff --git a/r2spec/spec.py b/r2spec/spec.py index 1321c5d..d526c87 100644 --- a/r2spec/spec.py +++ b/r2spec/spec.py @@ -20,63 +20,61 @@ Spec class, handles the read/write of the spec file """ +from __future__ import absolute_import, division, print_function import datetime import os -import re import sys -from jinja2 import Template +import textwrap +from collections import namedtuple + +from jinja2 import Environment, FileSystemLoader from r2spec import get_logger, get_rpm_tag, R2specError -def format_description(description): - """ Format the description as required by rpm """ - step = 75 - cnt = 0 - out = [] - char = 0 - while cnt < len(description) and char < description.rfind(" "): - if len(description[cnt:]) >= step: - char = description[cnt: cnt + step].rfind(" ") + cnt - out.append(description[cnt: char]) - cnt = char + 1 +class Package(namedtuple('Package', 'name version')): + def __str__(self): + if self.version: + return self.name + ' ' + self.version else: - out.append(description[cnt:]) - cnt += len(description[cnt:]) + return self.name - return "\n".join(out) + @property + def rpm_version(self): + if self.version: + return self.name + ' ' + self.version.replace('-', '.') + else: + return self.name def format_dependencies(dependencies): """ Format the dependencies cleanning them as much as possible for rpm. """ ignorelist = ['R'] - # Regular expression used to determine whether the string is a - # version number - versionmotif = re.compile('\d\.\d\.?\d?') char = { '\r': '', '(': ' ', ')': ' ', - ',': ' ', ' ': ' ', } - for key in char.keys(): + for key in char: dependencies = dependencies.replace(key, char[key]) dep_list = [] - for dep in dependencies.split(' '): - if dep.strip(): - if not ">" in dep \ - and not "<" in dep \ - and not "=" in dep \ - and len(versionmotif.findall(dep)) == 0 \ - and dep.strip() not in ignorelist: - dep = 'R-%s' % dep.strip() - dep_list.append(dep) + for dep in dependencies.split(','): + dep = dep.strip() + if dep: + if ' ' in dep: + name, version = dep.split(' ', 1) + else: + name = dep + version = '' + if name not in ignorelist: + name = 'R-%s' % name.strip() + dep_list.append(Package(name, version.strip())) - return ' '.join(dep_list).strip() + return dep_list class Spec: @@ -86,16 +84,17 @@ class Spec: """ def __init__(self, settings, package=None, no_check=False, - no_suggest=False): + with_deps=False): """ Constructor. """ self.package = package self.settings = settings - self.__dict = {} + self.__dict = { + 'with_deps': with_deps + } self.log = get_logger() self.spec = None self.no_check = no_check - self.no_suggest = no_suggest def add_files(self, files): """ Add to a spec file the given files list. @@ -151,6 +150,7 @@ class Spec: """ Fills the different variable required for the spec file. """ self.log.info('Filling spec variable from info collected') self.__dict['packname'] = self.package.name + self.__dict['packver'] = self.package.up_version self.__dict['arch'] = self.package.arch self.__dict['version'] = self.package.down_version self.__dict['summary'] = self.package.get('Title') @@ -161,13 +161,11 @@ class Spec: self.package.get('Depends')) self.__dict['imports'] = format_dependencies( self.package.get('Imports')) - if not self.no_suggest: - self.__dict['suggests'] = format_dependencies( - self.package.get('Suggests')) - else: - self.__dict['suggests'] = "" - self.__dict['description'] = format_description( - self.package.get('Description')) + self.__dict['suggests'] = format_dependencies( + self.package.get('Suggests')) + self.__dict['description'] = textwrap.fill( + self.package.get('Description'), + width=75) self.__dict['date'] = datetime.datetime.now( ).strftime("%a %b %d %Y") self.__dict['name'] = self.settings.get('packager') @@ -222,17 +220,15 @@ class Spec: """ Read the empty template and fills it with the information retrieved. """ - template = '%s/specfile.tpl' % os.path.dirname(__file__) self.log.info('Filling spec template') + loader = FileSystemLoader(os.path.dirname(__file__)) + env = Environment(loader=loader, lstrip_blocks=True, trim_blocks=True) try: - stream = open(template, 'r') - tplfile = stream.read() - stream.close() - mytemplate = Template(tplfile) + mytemplate = env.get_template('specfile.tpl') self.spec = mytemplate.render(self.__dict) - except IOError, err: - self.log.debug('ERROR: %s' % err) - raise R2specError('Cannot read the file %s' % template) + except IOError as err: + self.log.debug('ERROR: %s', err) + raise R2specError('Cannot read the file %s' % (err, )) def get_specfile(self): """ Return the path to the spec file. @@ -246,27 +242,27 @@ class Spec: """ specfile = self.get_specfile() if os.path.exists(specfile) and os.path.isfile(specfile): - self.log.info('Reading file %s' % specfile) + self.log.info('Reading file %s', specfile) try: stream = open(specfile, 'r') self.spec = stream.read() stream.close() - except IOError, err: - self.log.info('Cannot read the file %s' % specfile) - self.log.debug('ERROR: %s' % err) + except IOError as err: + self.log.info('Cannot read the file %s', specfile) + self.log.debug('ERROR: %s', err) def write_spec(self, verbose=False): """ Write down the spec to the spec directory as returned by rpm. """ specfile = self.get_specfile() - self.log.info('Writing file %s' % specfile) + self.log.info('Writing file %s', specfile) try: stream = open(specfile, 'w') stream.write(self.spec) stream.close() - self.log.debug('Spec file writen: %s' % specfile) + self.log.debug('Spec file written: %s', specfile) if verbose: - print 'Spec file writen: %s' % specfile - except IOError, err: - self.log.info('Cannot write the file %s' % specfile) - self.log.debug('ERROR: %s' % err) + print('Spec file written: %s' % specfile) + except IOError as err: + self.log.info('Cannot write the file %s', specfile) + self.log.debug('ERROR: %s', err) diff --git a/r2spec/specfile.tpl b/r2spec/specfile.tpl index dc23e58..8c3b76e 100644 --- a/r2spec/specfile.tpl +++ b/r2spec/specfile.tpl @@ -1,6 +1,9 @@ -%global packname {{ packname }} -{% if (arch == False) %}%global rlibdir %{_datadir}/R/library -{% else %}%global rlibdir %{_libdir}/R/library +%global packname {{ packname }} +%global packver {{ packver }} +{% if arch %} +%global rlibdir %{_libdir}/R/library +{% else %} +%global rlibdir %{_datadir}/R/library {% endif %} Name: R-%{packname} @@ -8,52 +11,65 @@ Version: {{version}} Release: 1%{?dist} Summary: {{summary}} -Group: Applications/Engineering License: {{license}} URL: {{URL}} Source0: {{source0}} -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) # Here's the R view of the dependencies world: -# Depends: {{depends}} -# Imports: {{imports}} -# Suggests: {{suggests}} +# Depends: {{ depends | join(', ') }} +# Imports: {{ imports | join(', ') }} +# Suggests: {{ suggests | join(', ') }} # LinkingTo: # Enhances: -{% if (arch == False) %}BuildArch: noarch +{% if not arch %} +BuildArch: noarch +{% if with_deps %} Requires: R-core {% endif %} -{% if depends != "" %}Requires: {{depends}} {% endif %} -{% if imports != "" %}Requires: {{imports}} {% endif %} -{% if suggests != "" %}Requires: {{suggests}} {% endif %} -BuildRequires: R-devel tex(latex) {{depends}} -{% if imports != "" %}BuildRequires: {{imports}} {% endif %} -{% if suggests != "" %}BuildRequires: {{suggests}} {% endif %} +{% endif %} +{% if with_deps %} +{% for dep in depends + imports %} +Requires: {{dep.rpm_version}} +{% endfor %} +{% for dep in suggests %} +Suggests: {{dep.rpm_version}} +{% endfor %} +{% endif %} +BuildRequires: R-devel +BuildRequires: tex(latex) +{% for dep in depends + imports %} +BuildRequires: {{dep.rpm_version}} +{% endfor %} +{% for dep in suggests %} +BuildRequires: {{dep.rpm_version}} +{% endfor %} %description {{description}} + %prep %setup -q -c -n %{packname} + %build + %install -rm -rf %{buildroot} mkdir -p %{buildroot}%{rlibdir} %{_bindir}/R CMD INSTALL -l %{buildroot}%{rlibdir} %{packname} test -d %{packname}/src && (cd %{packname}/src; rm -f *.o *.so) rm -f %{buildroot}%{rlibdir}/R.css -{% if (no_check == False) %} + + +{% if not no_check %} %check %{_bindir}/R CMD check %{packname} -{% endif %} -%clean -rm -rf %{buildroot} + +{% endif %} %files -%defattr(-, root, root, -) %dir %{rlibdir}/%{packname} %doc %{rlibdir}/%{packname}/doc %doc %{rlibdir}/%{packname}/html @@ -67,5 +83,5 @@ rm -rf %{buildroot} %changelog -* {{date}} {{name}} <{{email}}> {{version}}-1 +* {{date}} {{name}} <{{email}}> - {{version}}-1 - initial package for Fedora diff --git a/repos.cfg b/repos.cfg index a9f2479..58d2fd5 100644 --- a/repos.cfg +++ b/repos.cfg @@ -4,22 +4,22 @@ source = http://cran.r-project.org/src/contrib/%{packname}_%{version}.tar.gz package = http://cran.r-project.org/src/contrib/PACKAGES [repo:bioconductor] -url = http://bioconductor.org/packages/release/bioc/html/%{packname}.html -source = http://bioconductor.org/packages/release/bioc/src/contrib/%{packname}_%{version}.tar.gz -package = http://bioconductor.org/packages/release/bioc/src/contrib/PACKAGES +url = https://bioconductor.org/packages/release/bioc/html/%{packname}.html +source = https://bioconductor.org/packages/release/bioc/src/contrib/%{packname}_%{packver}.tar.gz +package = https://bioconductor.org/packages/release/bioc/src/contrib/PACKAGES [repo:bioconductor-annotation] -url = http://bioconductor.org/packages/release/data/annotation/html/%{packname}.html -source = http://bioconductor.org/packages/release/data/annotation/src/contrib/%{packname}_%{version}.tar.gz -package = http://bioconductor.org/packages/release/data/annotation/src/contrib/PACKAGES +url = https://bioconductor.org/packages/release/data/annotation/html/%{packname}.html +source = https://bioconductor.org/packages/release/data/annotation/src/contrib/%{packname}_%{packver}.tar.gz +package = https://bioconductor.org/packages/release/data/annotation/src/contrib/PACKAGES [repo:bioconductor-experiment] -url = http://bioconductor.org/packages/release/data/experiment/html/%{packname}.html -source = http://bioconductor.org/packages/release/data/experiment/src/contrib/%{packname}_%{version}.tar.gz -package = http://bioconductor.org/packages/release/data/experiment/src/contrib/PACKAGES +url = https://bioconductor.org/packages/release/data/experiment/html/%{packname}.html +source = https://bioconductor.org/packages/release/data/experiment/src/contrib/%{packname}_%{packver}.tar.gz +package = https://bioconductor.org/packages/release/data/experiment/src/contrib/PACKAGES [repo:r-forge] url = https://r-forge.r-project.org/projects/%{packname}/index.html -source = https://r-forge.r-project.org/src/contrib/%{packname}_%{version}.tar.gz +source = https://r-forge.r-project.org/src/contrib/%{packname}_%{packver}.tar.gz package = https://r-forge.r-project.org/src/contrib/PACKAGES diff --git a/setup.py b/setup.py index 372f22d..3ced326 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,9 @@ Setup script """ -from distutils.core import setup +from __future__ import absolute_import, division, print_function + +from setuptools import setup from r2spec import VERSION setup( @@ -18,9 +20,10 @@ setup( maintainer = 'Pierre-Yves Chibon', maintainer_email = 'pingou@pingoured.fr', license = 'GPLv3+', - download_url = 'https://fedorahosted.org/releases/r/2/r2spec/', - url = 'https://fedorahosted.org/r2spec/', + download_url='https://pagure.io/r2spec/releases', + url='https://pagure.io/r2spec', package_dir = {'R2spec': 'r2spec'}, packages = ['r2spec'], + include_package_data=True, scripts=['R2spec', 'R2rpm'], ) diff --git a/test/test_R2rpm.py b/test/test_R2rpm.py index c9dc710..36f4484 100644 --- a/test/test_R2rpm.py +++ b/test/test_R2rpm.py @@ -19,6 +19,7 @@ """ R2rpm tests script """ +from __future__ import absolute_import, division, print_function # Cases: # -p @@ -41,7 +42,11 @@ import os import shutil import sys import unittest -import urllib2 + +try: + import urllib.request as urllib_request +except ImportError: + import urllib2 as urllib_request sys.path.insert(0, os.path.abspath('../')) from r2spec.r2spec_obj import R2rpm, setup_parser @@ -67,10 +72,9 @@ def download_sources(url): :arg url, url to the object to download """ sources = url.rsplit('/', 1)[1] - remotefile = urllib2.urlopen(url) - localfile = open(sources, 'w') - localfile.write(remotefile.read()) - localfile.close() + remotefile = urllib_request.urlopen(url) + with open(sources, 'wb') as localfile: + localfile.write(remotefile.read()) return sources @@ -95,7 +99,7 @@ class R2rpmtests(unittest.TestCase): arg = parser.parse_args() try: R2rpm().main(arg) - except R2specError, err: + except R2specError as err: self.assertEqual('Not enough argument given, see -h/--help', err.value) diff --git a/test/test_R2spec.py b/test/test_R2spec.py index 9a52fba..c45d190 100644 --- a/test/test_R2spec.py +++ b/test/test_R2spec.py @@ -19,6 +19,7 @@ """ R2spec tests script """ +from __future__ import absolute_import, division, print_function # Cases: # -p @@ -42,7 +43,11 @@ import os import shutil import sys import unittest -import urllib2 + +try: + import urllib.request as urllib_request +except ImportError: + import urllib2 as urllib_request sys.path.insert(0, os.path.abspath('../')) from r2spec.r2spec_obj import R2spec, setup_parser @@ -68,10 +73,9 @@ def download_sources(url): :arg url, url to the object to download """ sources = url.rsplit('/', 1)[1] - remotefile = urllib2.urlopen(url) - localfile = open(sources, 'w') - localfile.write(remotefile.read()) - localfile.close() + remotefile = urllib_request.urlopen(url) + with open(sources, 'wb') as localfile: + localfile.write(remotefile.read()) return sources @@ -96,7 +100,7 @@ class R2spectests(unittest.TestCase): arg = parser.parse_args() try: R2spec().main(arg) - except R2specError, err: + except R2specError as err: self.assertEqual('Not enough argument given, see -h/--help', err.value) diff --git a/tests.py b/tests.py index 1954691..b1920d6 100755 --- a/tests.py +++ b/tests.py @@ -19,6 +19,8 @@ """ R2spec launcher script """ +from __future__ import absolute_import, division, print_function + import unittest from test.test_R2spec import R2spectests from test.test_R2rpm import R2rpmtests