From 9e43ff821c1549d5813eebd5a9ca81a7869786e7 Mon Sep 17 00:00:00 2001 From: Milton Woods Date: Fri, 31 Mar 2017 09:38:58 +1000 Subject: Extendable Perl (#3614) * perl: make extendable and add Module::Build package * perl: allow 'spack create' to identify perl packages from their contents * perl-module-build: fix indenting of package docstring * perl: split install() method for extensions into phases * perl: auto-detect build method (Makefile.PL vs Build.PL) and define a 'check' method * PerlPackage: use import statements similar to those in AutotoolsPackage * PerlModule: fix detection of Build.PL * PerlPackageTemplate: remove extraneous lines to avoid flake8 warnings * PerlPackageTemplate: split into separate templates for Makefile.PL and Build.PL * PerlPackage: add cross-references to docstrings * AutotoolsPackage: fix ambiguous cross-references to avoid errors in doc tests * PerlbuildPackageTemplate: depend on perl-module-build if Build.PL exists --- lib/spack/docs/packaging_guide.rst | 4 + lib/spack/spack/__init__.py | 4 +- lib/spack/spack/build_systems/autotools.py | 8 +- lib/spack/spack/build_systems/perl.py | 117 +++++++++++++++++++++++++++++ lib/spack/spack/cmd/build.py | 3 +- lib/spack/spack/cmd/configure.py | 3 +- lib/spack/spack/cmd/create.py | 45 ++++++++++- lib/spack/spack/test/build_system_guess.py | 2 + 8 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 lib/spack/spack/build_systems/perl.py (limited to 'lib') diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst index 729ea5d656..1a64c7db4a 100644 --- a/lib/spack/docs/packaging_guide.rst +++ b/lib/spack/docs/packaging_guide.rst @@ -2043,6 +2043,10 @@ The classes that are currently provided by Spack are: | :py:class:`.PythonPackage` | Specialized class for | | | :py:class:`.Python` extensions | +------------------------------------+----------------------------------+ + | :py:class:`.PerlPackage` | Specialized class for | + | | :py:class:`.Perl` extensions | + +------------------------------------+----------------------------------+ + diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py index 6a28fbb2b0..b0f2faed76 100644 --- a/lib/spack/spack/__init__.py +++ b/lib/spack/spack/__init__.py @@ -162,6 +162,7 @@ from spack.build_systems.autotools import AutotoolsPackage from spack.build_systems.cmake import CMakePackage from spack.build_systems.python import PythonPackage from spack.build_systems.r import RPackage +from spack.build_systems.perl import PerlPackage __all__ += [ 'run_before', @@ -172,7 +173,8 @@ __all__ += [ 'AutotoolsPackage', 'MakefilePackage', 'PythonPackage', - 'RPackage' + 'RPackage', + 'PerlPackage' ] from spack.version import Version, ver diff --git a/lib/spack/spack/build_systems/autotools.py b/lib/spack/spack/build_systems/autotools.py index a11a84acd0..76dfd0d16f 100644 --- a/lib/spack/spack/build_systems/autotools.py +++ b/lib/spack/spack/build_systems/autotools.py @@ -49,7 +49,8 @@ class AutotoolsPackage(PackageBase): 4. :py:meth:`~.AutotoolsPackage.install` They all have sensible defaults and for many packages the only thing - necessary will be to override the helper method :py:meth:`.configure_args`. + necessary will be to override the helper method + :py:meth:`~.AutotoolsPackage.configure_args`. For a finer tuning you may also override: +-----------------------------------------------+--------------------+ @@ -234,7 +235,7 @@ class AutotoolsPackage(PackageBase): appropriately, otherwise raises an error. :raises RuntimeError: if a configure script is not found in - :py:meth:`~.configure_directory` + :py:meth:`~AutotoolsPackage.configure_directory` """ # Check if a configure script is there. If not raise a RuntimeError. if not os.path.exists(self.configure_abs_path): @@ -255,7 +256,8 @@ class AutotoolsPackage(PackageBase): return [] def configure(self, spec, prefix): - """Runs configure with the arguments specified in :py:meth:`.configure_args` + """Runs configure with the arguments specified in + :py:meth:`~.AutotoolsPackage.configure_args` and an appropriately set prefix. """ options = ['--prefix={0}'.format(prefix)] + self.configure_args() diff --git a/lib/spack/spack/build_systems/perl.py b/lib/spack/spack/build_systems/perl.py new file mode 100644 index 0000000000..78184c85dc --- /dev/null +++ b/lib/spack/spack/build_systems/perl.py @@ -0,0 +1,117 @@ +############################################################################## +# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/llnl/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## + +import inspect +import os + +from llnl.util.filesystem import join_path +from spack.directives import extends +from spack.package import PackageBase, run_after +from spack.util.executable import Executable + + +class PerlPackage(PackageBase): + """Specialized class for packages that are built using Perl. + + This class provides four phases that can be overridden if required: + + 1. :py:meth:`~.PerlPackage.configure` + 2. :py:meth:`~.PerlPackage.build` + 3. :py:meth:`~.PerlPackage.check` + 4. :py:meth:`~.PerlPackage.install` + + The default methods use, in order of preference: + (1) Makefile.PL, + (2) Build.PL. + + Some packages may need to override + :py:meth:`~.PerlPackage.configure_args`, + which produces a list of arguments for + :py:meth:`~.PerlPackage.configure`. + Arguments should not include the installation base directory. + """ + #: Phases of a Perl package + phases = ['configure', 'build', 'install'] + + #: This attribute is used in UI queries that need to know the build + #: system base class + build_system_class = 'PerlPackage' + + #: Callback names for build-time test + build_time_test_callbacks = ['check'] + + extends('perl') + + def configure_args(self): + """Produces a list containing the arguments that must be passed to + :py:meth:`~.PerlPackage.configure`. Arguments should not include + the installation base directory, which is prepended automatically. + + :return: list of arguments for Makefile.PL or Build.PL + """ + return [] + + def configure(self, spec, prefix): + """Runs Makefile.PL or Build.PL with arguments consisting of + an appropriate installation base directory followed by the + list returned by :py:meth:`~.PerlPackage.configure_args`. + + :raise RuntimeError: if neither Makefile.PL or Build.PL exist + """ + if os.path.isfile('Makefile.PL'): + self.build_method = 'Makefile.PL' + self.build_executable = inspect.getmodule(self).make + elif os.path.isfile('Build.PL'): + self.build_method = 'Build.PL' + self.build_executable = Executable( + join_path(self.stage.source_path, 'Build')) + else: + raise RuntimeError('Unknown build_method for perl package') + + if self.build_method == 'Makefile.PL': + options = ['Makefile.PL', 'INSTALL_BASE={0}'.format(prefix)] + elif self.build_method == 'Build.PL': + options = ['Build.PL', '--install_base', prefix] + options += self.configure_args() + + inspect.getmodule(self).perl(*options) + + def build(self, spec, prefix): + """Builds a Perl package.""" + self.build_executable() + + # Ensure that tests run after build (if requested): + run_after('build')(PackageBase._run_default_build_time_test_callbacks) + + def check(self): + """Runs built-in tests of a Perl package.""" + self.build_executable('test') + + def install(self, spec, prefix): + """Installs a Perl package.""" + self.build_executable('install') + + # Check that self.prefix is there after installation + run_after('install')(PackageBase.sanity_check_prefix) diff --git a/lib/spack/spack/cmd/build.py b/lib/spack/spack/cmd/build.py index 6a90af907d..90157a85af 100644 --- a/lib/spack/spack/cmd/build.py +++ b/lib/spack/spack/cmd/build.py @@ -31,7 +31,8 @@ description = 'stops at build stage when installing a package, if possible' build_system_to_phase = { CMakePackage: 'build', AutotoolsPackage: 'build', - PythonPackage: 'build' + PythonPackage: 'build', + PerlPackage: 'build' } diff --git a/lib/spack/spack/cmd/configure.py b/lib/spack/spack/cmd/configure.py index 7b1ef04522..b6669f33a2 100644 --- a/lib/spack/spack/cmd/configure.py +++ b/lib/spack/spack/cmd/configure.py @@ -36,7 +36,8 @@ description = 'stops at configuration stage when installing a package, if possib build_system_to_phase = { CMakePackage: 'cmake', - AutotoolsPackage: 'configure' + AutotoolsPackage: 'configure', + PerlPackage: 'configure' } diff --git a/lib/spack/spack/cmd/create.py b/lib/spack/spack/cmd/create.py index 14b213a756..cc90669158 100644 --- a/lib/spack/spack/cmd/create.py +++ b/lib/spack/spack/cmd/create.py @@ -268,6 +268,45 @@ class RPackageTemplate(PackageTemplate): super(RPackageTemplate, self).__init__(name, *args) +class PerlmakePackageTemplate(PackageTemplate): + """Provides appropriate overrides for Perl extensions + that come with a Makefile.PL""" + base_class_name = 'PerlPackage' + + dependencies = """\ + # FIXME: Add dependencies if required: + # depends_on('perl-foo') + # depends_on('barbaz', type=('build', 'link', 'run'))""" + + body = """\ + # FIXME: If non-standard arguments are used for configure step: + # def configure_args(self): + # return ['my', 'configure', 'args'] + + # FIXME: in unusual cases, it may be necessary to override methods for + # configure(), build(), check() or install().""" + + def __init__(self, name, *args): + # If the user provided `--name perl-cpp`, don't rename it perl-perl-cpp + if not name.startswith('perl-'): + # Make it more obvious that we are renaming the package + tty.msg("Changing package name from {0} to perl-{0}".format(name)) + name = 'perl-{0}'.format(name) + + super(PerlmakePackageTemplate, self).__init__(name, *args) + + +class PerlbuildPackageTemplate(PerlmakePackageTemplate): + """Provides appropriate overrides for Perl extensions + that come with a Build.PL instead of a Makefile.PL""" + dependencies = """\ + depends_on('perl-module-build', type='build') + + # FIXME: Add additional dependencies if required: + # depends_on('perl-foo') + # depends_on('barbaz', type=('build', 'link', 'run'))""" + + class OctavePackageTemplate(PackageTemplate): """Provides appropriate overrides for octave packages""" @@ -305,6 +344,8 @@ templates = { 'bazel': BazelPackageTemplate, 'python': PythonPackageTemplate, 'r': RPackageTemplate, + 'perlmake': PerlmakePackageTemplate, + 'perlbuild': PerlbuildPackageTemplate, 'octave': OctavePackageTemplate, 'generic': PackageTemplate } @@ -363,7 +404,9 @@ class BuildSystemGuesser: (r'/SConstruct$', 'scons'), (r'/setup.py$', 'python'), (r'/NAMESPACE$', 'r'), - (r'/WORKSPACE$', 'bazel') + (r'/WORKSPACE$', 'bazel'), + (r'/Build.PL$', 'perlbuild'), + (r'/Makefile.PL$', 'perlmake'), ] # Peek inside the compressed file. diff --git a/lib/spack/spack/test/build_system_guess.py b/lib/spack/spack/test/build_system_guess.py index 82bf1964b2..e6fb84b37d 100644 --- a/lib/spack/spack/test/build_system_guess.py +++ b/lib/spack/spack/test/build_system_guess.py @@ -38,6 +38,8 @@ import spack.stage ('setup.py', 'python'), ('NAMESPACE', 'r'), ('WORKSPACE', 'bazel'), + ('Makefile.PL', 'perlmake'), + ('Build.PL', 'perlbuild'), ('foobar', 'generic') ] ) -- cgit v1.2.3-70-g09d2