diff options
author | Adam J. Stewart <ajstewart426@gmail.com> | 2019-07-30 17:12:48 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-30 17:12:48 -0500 |
commit | b34da4e108b7441d26ca6c02871cba65a0530566 (patch) | |
tree | e120912824c3cf4929c999ad157a58d1e75910c3 | |
parent | f487f87e3edefdf2a8cffa95d72f2ce44ad5c0e0 (diff) | |
download | spack-b34da4e108b7441d26ca6c02871cba65a0530566.tar.gz spack-b34da4e108b7441d26ca6c02871cba65a0530566.tar.bz2 spack-b34da4e108b7441d26ca6c02871cba65a0530566.tar.xz spack-b34da4e108b7441d26ca6c02871cba65a0530566.zip |
Add SIPPackage base class (#12157)
18 files changed, 371 insertions, 44 deletions
diff --git a/lib/spack/docs/build_systems.rst b/lib/spack/docs/build_systems.rst index 513082d9b8..93b8e5c7a8 100644 --- a/lib/spack/docs/build_systems.rst +++ b/lib/spack/docs/build_systems.rst @@ -40,6 +40,7 @@ on these ideas for each distinct build system that Spack supports: build_systems/cmakepackage build_systems/mesonpackage build_systems/qmakepackage + build_systems/sippackage .. toctree:: :maxdepth: 1 diff --git a/lib/spack/docs/build_systems/sippackage.rst b/lib/spack/docs/build_systems/sippackage.rst new file mode 100644 index 0000000000..cb744e858f --- /dev/null +++ b/lib/spack/docs/build_systems/sippackage.rst @@ -0,0 +1,135 @@ +.. Copyright 2013-2019 Lawrence Livermore National Security, LLC and other + Spack Project Developers. See the top-level COPYRIGHT file for details. + + SPDX-License-Identifier: (Apache-2.0 OR MIT) + +.. _sippackage: + +---------- +SIPPackage +---------- + +SIP is a tool that makes it very easy to create Python bindings for C and C++ +libraries. It was originally developed to create PyQt, the Python bindings for +the Qt toolkit, but can be used to create bindings for any C or C++ library. + +SIP comprises a code generator and a Python module. The code generator +processes a set of specification files and generates C or C++ code which is +then compiled to create the bindings extension module. The SIP Python module +provides support functions to the automatically generated code. + +^^^^^^ +Phases +^^^^^^ + +The ``SIPPackage`` base class comes with the following phases: + +#. ``configure`` - configure the package +#. ``build`` - build the package +#. ``install`` - install the package + +By default, these phases run: + +.. code-block:: console + + $ python configure.py --bindir ... --destdir ... + $ make + $ make install + + +^^^^^^^^^^^^^^^ +Important files +^^^^^^^^^^^^^^^ + +Each SIP package comes with a custom ``configure.py`` build script, +written in Python. This script contains instructions to build the project. + +^^^^^^^^^^^^^^^^^^^^^^^^^ +Build system dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^ + +``SIPPackage`` requires several dependencies. Python is needed to run +the ``configure.py`` build script, and to run the resulting Python +libraries. Qt is needed to provide the ``qmake`` command. SIP is also +needed to build the package. All of these dependencies are automatically +added via the base class + +.. code-block:: python + + extends('python') + + depends_on('qt', type='build') + depends_on('py-sip', type='build') + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Passing arguments to ``configure.py`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Each phase comes with a ``<phase_args>`` function that can be used to pass +arguments to that particular phase. For example, if you need to pass +arguments to the configure phase, you can use: + +.. code-block:: python + + def configure_args(self, spec, prefix): + return ['--no-python-dbus'] + + +A list of valid options can be found by running ``python configure.py --help``. + +^^^^^^^ +Testing +^^^^^^^ + +Just because a package successfully built does not mean that it built +correctly. The most reliable test of whether or not the package was +correctly installed is to attempt to import all of the modules that +get installed. To get a list of modules, run the following command +in the site-packages directory: + +.. code-block:: console + + $ python + >>> import setuptools + >>> setuptools.find_packages() + ['QtPy5'] + + +Large, complex packages like ``QtPy5`` will return a long list of +packages, while other packages may return an empty list. These packages +only install a single ``foo.py`` file. In Python packaging lingo, +a "package" is a directory containing files like: + +.. code-block:: none + + foo/__init__.py + foo/bar.py + foo/baz.py + + +whereas a "module" is a single Python file. Since ``find_packages`` +only returns packages, you'll have to determine the correct module +names yourself. You can now add these packages and modules to the +package like so: + +.. code-block:: python + + import_modules = ['PyQt5'] + + +When you run ``spack install --test=root py-pyqt5``, Spack will attempt +to import the ``PyQt5`` module after installation. + +These tests most often catch missing dependencies and non-RPATHed +libraries. + +^^^^^^^^^^^^^^^^^^^^^^ +External documentation +^^^^^^^^^^^^^^^^^^^^^^ + +For more information on the SIP build system, see: + +* https://www.riverbankcomputing.com/software/sip/intro +* https://www.riverbankcomputing.com/static/Docs/sip/ +* https://wiki.python.org/moin/SIP diff --git a/lib/spack/spack/build_systems/sip.py b/lib/spack/spack/build_systems/sip.py new file mode 100644 index 0000000000..6d51ff57bd --- /dev/null +++ b/lib/spack/spack/build_systems/sip.py @@ -0,0 +1,113 @@ +# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +import inspect +import os + +from llnl.util.filesystem import working_dir +from spack.directives import depends_on, extends +from spack.package import PackageBase, run_after + + +class SIPPackage(PackageBase): + """Specialized class for packages that are built using the + SIP build system. See https://www.riverbankcomputing.com/software/sip/intro + for more information. + + This class provides the following phases that can be overridden: + + * configure + * build + * install + + The configure phase already adds a set of default flags. To see more + options, run ``python configure.py --help``. + """ + # Default phases + phases = ['configure', 'build', 'install'] + + # To be used in UI queries that require to know which + # build-system class we are using + build_system_class = 'SIPPackage' + + #: Callback names for install-time test + install_time_test_callbacks = ['import_module_test'] + + extends('python') + + depends_on('qt') + depends_on('py-sip') + + def configure_file(self): + """Returns the name of the configure file to use.""" + return 'configure.py' + + def python(self, *args, **kwargs): + """The python ``Executable``.""" + inspect.getmodule(self).python(*args, **kwargs) + + def configure(self, spec, prefix): + """Configure the package.""" + configure = self.configure_file() + + args = self.configure_args() + + args.extend([ + '--verbose', + '--confirm-license', + '--qmake', spec['qt'].prefix.bin.qmake, + '--sip', spec['py-sip'].prefix.bin.sip, + '--sip-incdir', os.path.join( + spec['py-sip'].prefix, + spec['python'].package.python_include_dir + ), + '--bindir', prefix.bin, + '--destdir', inspect.getmodule(self).site_packages_dir, + ]) + + self.python(configure, *args) + + def configure_args(self): + """Arguments to pass to configure.""" + return [] + + def build(self, spec, prefix): + """Build the package.""" + args = self.build_args() + + inspect.getmodule(self).make(*args) + + def build_args(self): + """Arguments to pass to build.""" + return [] + + def install(self, spec, prefix): + """Install the package.""" + args = self.install_args() + + inspect.getmodule(self).make('install', parallel=False, *args) + + def install_args(self): + """Arguments to pass to install.""" + return [] + + # Testing + + def import_module_test(self): + """Attempts to import the module that was just installed. + + This test is only run if the package overrides + :py:attr:`import_modules` with a list of module names.""" + + # Make sure we are importing the installed modules, + # not the ones in the current directory + with working_dir('spack-test', create=True): + for module in self.import_modules: + self.python('-c', 'import {0}'.format(module)) + + run_after('install')(PackageBase._run_default_install_time_test_callbacks) + + # 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 bbace88b31..84d8221090 100644 --- a/lib/spack/spack/cmd/build.py +++ b/lib/spack/spack/cmd/build.py @@ -13,6 +13,7 @@ from spack.build_systems.waf import WafPackage from spack.build_systems.python import PythonPackage from spack.build_systems.perl import PerlPackage from spack.build_systems.meson import MesonPackage +from spack.build_systems.sip import SIPPackage description = 'stops at build stage when installing a package, if possible' section = "build" @@ -28,6 +29,7 @@ build_system_to_phase = { PythonPackage: 'build', PerlPackage: 'build', MesonPackage: 'build', + SIPPackage: 'build', } diff --git a/lib/spack/spack/cmd/configure.py b/lib/spack/spack/cmd/configure.py index 64608b4643..b06d4edf1c 100644 --- a/lib/spack/spack/cmd/configure.py +++ b/lib/spack/spack/cmd/configure.py @@ -16,6 +16,7 @@ from spack.build_systems.waf import WafPackage from spack.build_systems.perl import PerlPackage from spack.build_systems.intel import IntelPackage from spack.build_systems.meson import MesonPackage +from spack.build_systems.sip import SIPPackage description = 'stage and configure a package but do not install' section = "build" @@ -30,6 +31,7 @@ build_system_to_phase = { PerlPackage: 'configure', IntelPackage: 'configure', MesonPackage: 'meson', + SIPPackage: 'configure', } diff --git a/lib/spack/spack/cmd/create.py b/lib/spack/spack/cmd/create.py index 792a437f29..59e3274bdb 100644 --- a/lib/spack/spack/cmd/create.py +++ b/lib/spack/spack/cmd/create.py @@ -350,6 +350,28 @@ class IntelPackageTemplate(PackageTemplate): # FIXME: Override `setup_environment` if necessary.""" +class SIPPackageTemplate(PackageTemplate): + """Provides appropriate overrides for SIP packages.""" + + base_class_name = 'SIPPackage' + + body = """\ + def configure_args(self, spec, prefix): + # FIXME: Add arguments other than --bindir and --destdir + # FIXME: If not needed delete this function + args = [] + return args""" + + def __init__(self, name, *args): + # If the user provided `--name py-pyqt4`, don't rename it py-py-pyqt4 + if not name.startswith('py-'): + # Make it more obvious that we are renaming the package + tty.msg("Changing package name from {0} to py-{0}".format(name)) + name = 'py-{0}'.format(name) + + super(SIPPackageTemplate, self).__init__(name, *args) + + templates = { 'autotools': AutotoolsPackageTemplate, 'autoreconf': AutoreconfPackageTemplate, @@ -366,6 +388,7 @@ templates = { 'makefile': MakefilePackageTemplate, 'intel': IntelPackageTemplate, 'meson': MesonPackageTemplate, + 'sip': SIPPackageTemplate, 'generic': PackageTemplate, } @@ -440,6 +463,7 @@ class BuildSystemGuesser: (r'/(GNU)?[Mm]akefile$', 'makefile'), (r'/DESCRIPTION$', 'octave'), (r'/meson\.build$', 'meson'), + (r'/configure\.py$', 'sip'), ] # Peek inside the compressed file. diff --git a/lib/spack/spack/pkgkit.py b/lib/spack/spack/pkgkit.py index 85c1ee264e..ff3288f48a 100644 --- a/lib/spack/spack/pkgkit.py +++ b/lib/spack/spack/pkgkit.py @@ -27,6 +27,7 @@ from spack.build_systems.r import RPackage from spack.build_systems.perl import PerlPackage from spack.build_systems.intel import IntelPackage from spack.build_systems.meson import MesonPackage +from spack.build_systems.sip import SIPPackage from spack.mixins import filter_compiler_wrappers diff --git a/lib/spack/spack/test/build_system_guess.py b/lib/spack/spack/test/build_system_guess.py index 80dbb35f15..cd6216a907 100644 --- a/lib/spack/spack/test/build_system_guess.py +++ b/lib/spack/spack/test/build_system_guess.py @@ -27,6 +27,7 @@ import spack.stage ('makefile', 'makefile'), ('Makefile', 'makefile'), ('meson.build', 'meson'), + ('configure.py', 'sip'), ('foobar', 'generic') ] ) diff --git a/var/spack/repos/builtin/packages/py-guidata/package.py b/var/spack/repos/builtin/packages/py-guidata/package.py index 13856e6926..80108cd400 100644 --- a/var/spack/repos/builtin/packages/py-guidata/package.py +++ b/var/spack/repos/builtin/packages/py-guidata/package.py @@ -16,6 +16,6 @@ class PyGuidata(PythonPackage): version('1.7.5', '915188c02ad3c89951ee260db65d84a7') depends_on('py-setuptools', type='build') - depends_on('py-pyqt@4:', type=('build', 'run')) + depends_on('py-pyqt4', type=('build', 'run')) depends_on('py-spyder@2.0:2.9.9', type=('build', 'run')) depends_on('py-h5py', type=('build', 'run')) diff --git a/var/spack/repos/builtin/packages/py-pymol/package.py b/var/spack/repos/builtin/packages/py-pymol/package.py index ca88919a48..6499a60516 100644 --- a/var/spack/repos/builtin/packages/py-pymol/package.py +++ b/var/spack/repos/builtin/packages/py-pymol/package.py @@ -28,5 +28,5 @@ class PyPymol(PythonPackage): depends_on('freetype') depends_on('libxml2') depends_on('msgpack-c') - depends_on('py-pyqt', type=('build', 'run')) + depends_on('py-pyqt4', type=('build', 'run')) depends_on('freeglut') diff --git a/var/spack/repos/builtin/packages/py-pyqt/package.py b/var/spack/repos/builtin/packages/py-pyqt/package.py deleted file mode 100644 index b33d882049..0000000000 --- a/var/spack/repos/builtin/packages/py-pyqt/package.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other -# Spack Project Developers. See the top-level COPYRIGHT file for details. -# -# SPDX-License-Identifier: (Apache-2.0 OR MIT) - -from spack import * - - -class PyPyqt(Package): - """PyQt is a set of Python v2 and v3 bindings for Digia's Qt - application framework and runs on all platforms supported by Qt - including Windows, MacOS/X and Linux.""" - homepage = "http://www.riverbankcomputing.com/software/pyqt/intro" - url = "http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.3/PyQt-x11-gpl-4.11.3.tar.gz" - - version('4.11.3', '997c3e443165a89a559e0d96b061bf70') - - extends('python') - depends_on('py-sip', type=('build', 'run')) - - # TODO: allow qt5 when conditional deps are supported. - # TODO: Fix version matching so that @4 works like @:4 - depends_on('qt@:4+phonon+dbus') - - def install(self, spec, prefix): - python('configure.py', - '--confirm-license', - '--destdir=%s' % site_packages_dir) - make() - make('install') diff --git a/var/spack/repos/builtin/packages/py-pyqt4/package.py b/var/spack/repos/builtin/packages/py-pyqt4/package.py new file mode 100644 index 0000000000..45bb6ac5c3 --- /dev/null +++ b/var/spack/repos/builtin/packages/py-pyqt4/package.py @@ -0,0 +1,31 @@ +# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack import * + + +class PyPyqt4(SIPPackage): + """PyQt is a set of Python v2 and v3 bindings for The Qt Company's Qt + application framework and runs on all platforms supported by Qt including + Windows, OS X, Linux, iOS and Android. PyQt4 supports Qt v4 and will build + against Qt v5.""" + + homepage = "https://www.riverbankcomputing.com/software/pyqt/intro" + url = "http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.12.3/PyQt4_gpl_x11-4.12.3.tar.gz" + import_modules = ['PyQt4'] + + version('4.12.3', sha256='a00f5abef240a7b5852b7924fa5fdf5174569525dc076cd368a566619e56d472') + version('4.11.3', '997c3e443165a89a559e0d96b061bf70', + url='http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.3/PyQt-x11-gpl-4.11.3.tar.gz') + + # Supposedly can also be built with Qt 5 compatibility layer + depends_on('qt@:4+phonon+dbus') + + # https://www.riverbankcomputing.com/static/Docs/PyQt4/installation.html + def configure_file(self): + return 'configure-ng.py' + + def configure_args(self): + return ['--pyuic4-interpreter', self.spec['python'].command.path] diff --git a/var/spack/repos/builtin/packages/py-pyqt5/package.py b/var/spack/repos/builtin/packages/py-pyqt5/package.py new file mode 100644 index 0000000000..6de6c7c53d --- /dev/null +++ b/var/spack/repos/builtin/packages/py-pyqt5/package.py @@ -0,0 +1,28 @@ +# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack import * + + +class PyPyqt5(SIPPackage): + """PyQt is a set of Python v2 and v3 bindings for The Qt Company's Qt + application framework and runs on all platforms supported by Qt including + Windows, OS X, Linux, iOS and Android. PyQt5 supports Qt v5.""" + + homepage = "https://www.riverbankcomputing.com/software/pyqt/intro" + url = "https://www.riverbankcomputing.com/static/Downloads/PyQt5/5.13.0/PyQt5_gpl-5.13.0.tar.gz" + list_url = "https://www.riverbankcomputing.com/software/pyqt/download5" + import_modules = ['PyQt5'] + + version('5.13.0', sha256='0cdbffe5135926527b61cc3692dd301cd0328dd87eeaf1313e610787c46faff9') + + # Without opengl support, I got the following error: + # sip: QOpenGLFramebufferObject is undefined + depends_on('qt@5:+opengl') + depends_on('python@2.6:', type=('build', 'run')) + + # https://www.riverbankcomputing.com/static/Docs/PyQt5/installation.html + def configure_args(self): + return ['--pyuic5-interpreter', self.spec['python'].command.path] diff --git a/var/spack/repos/builtin/packages/py-pythonqwt/package.py b/var/spack/repos/builtin/packages/py-pythonqwt/package.py index 9c637b3f51..2bc32b69c4 100644 --- a/var/spack/repos/builtin/packages/py-pythonqwt/package.py +++ b/var/spack/repos/builtin/packages/py-pythonqwt/package.py @@ -19,5 +19,5 @@ class PyPythonqwt(PythonPackage): depends_on('py-setuptools', type='build') depends_on('py-numpy@1.3:', type=('build', 'run')) depends_on('py-sip', type=('build', 'run')) - depends_on('py-pyqt@4:', type=('build', 'run')) + depends_on('py-pyqt4', type=('build', 'run')) depends_on('py-sphinx@1.1:', type=('build', 'run'), when='+docs') diff --git a/var/spack/repos/builtin/packages/py-qtpy/package.py b/var/spack/repos/builtin/packages/py-qtpy/package.py index 773ddef379..7e46682111 100644 --- a/var/spack/repos/builtin/packages/py-qtpy/package.py +++ b/var/spack/repos/builtin/packages/py-qtpy/package.py @@ -15,4 +15,4 @@ class PyQtpy(PythonPackage): version('1.2.1', 'e2f783fb7f8e502815237bd8d30c6d11') depends_on('py-setuptools', type='build') - depends_on('py-pyqt@4:', type=('build', 'run')) + depends_on('py-pyqt4', type=('build', 'run')) diff --git a/var/spack/repos/builtin/packages/py-sip/package.py b/var/spack/repos/builtin/packages/py-sip/package.py index 72d1171d70..fc481eabdf 100644 --- a/var/spack/repos/builtin/packages/py-sip/package.py +++ b/var/spack/repos/builtin/packages/py-sip/package.py @@ -4,26 +4,45 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) from spack import * -import os class PySip(Package): """SIP is a tool that makes it very easy to create Python bindings for C and C++ libraries.""" - homepage = "http://www.riverbankcomputing.com/software/sip/intro" - url = "http://sourceforge.net/projects/pyqt/files/sip/sip-4.16.5/sip-4.16.5.tar.gz" + homepage = "https://www.riverbankcomputing.com/software/sip/intro" + url = "https://www.riverbankcomputing.com/static/Downloads/sip/4.19.18/sip-4.19.18.tar.gz" + list_url = "https://www.riverbankcomputing.com/software/sip/download" + hg = "https://www.riverbankcomputing.com/hg/sip" + + version('develop', hg=hg) # wasn't actually able to clone this + version('4.19.18', sha256='c0bd863800ed9b15dcad477c4017cdb73fa805c25908b0240564add74d697e1e') version('4.19.13', sha256='e353a7056599bf5fbd5d3ff9842a6ab2ea3cf4e0304a0f925ec5862907c0d15e') version('4.16.7', '32abc003980599d33ffd789734de4c36') version('4.16.5', '6d01ea966a53e4c7ae5c5e48c40e49e5') extends('python') - def install(self, spec, prefix): + # https://www.riverbankcomputing.com/static/Docs/sip/installation.html + phases = ['configure', 'build', 'install'] + + depends_on('flex', type='build', when='@develop') + depends_on('bison', type='build', when='@develop') + + @run_before('configure') + def prepare(self): + if self.spec.satisfies('@develop'): + python('build.py', 'prepare') + + def configure(self, spec, prefix): python('configure.py', - '--destdir=%s' % site_packages_dir, - '--bindir=%s' % spec.prefix.bin, - '--incdir=%s' % python_include_dir, - '--sipdir=%s' % os.path.join(spec.prefix.share, 'sip')) + '--bindir={0}'.format(prefix.bin), + '--destdir={0}'.format(site_packages_dir), + '--incdir={0}'.format(python_include_dir), + '--sipdir={0}'.format(prefix.share.sip)) + + def build(self, spec, prefix): make() + + def install(self, spec, prefix): make('install') diff --git a/var/spack/repos/builtin/packages/py-spyder/package.py b/var/spack/repos/builtin/packages/py-spyder/package.py index 0b47646fbe..ced5e85266 100644 --- a/var/spack/repos/builtin/packages/py-spyder/package.py +++ b/var/spack/repos/builtin/packages/py-spyder/package.py @@ -36,7 +36,7 @@ class PySpyder(PythonPackage): depends_on('py-psutil', type=('build', 'run')) depends_on('py-qtawesome@0.4.1:', type=('build', 'run')) depends_on('py-qtpy@1.1.0:', type=('build', 'run')) - # technically this is a transitive dependency in order for py-pyqt + # technically this is a transitive dependency in order for py-pyqt4 # to pick up webkit, but this is the easier solution (see #9207) depends_on('qt+webkit', type=('build', 'run')) depends_on('py-pickleshare', type=('build', 'run')) diff --git a/var/spack/repos/builtin/packages/py-tuiview/package.py b/var/spack/repos/builtin/packages/py-tuiview/package.py index 75658eeb26..d21c71e49e 100644 --- a/var/spack/repos/builtin/packages/py-tuiview/package.py +++ b/var/spack/repos/builtin/packages/py-tuiview/package.py @@ -16,6 +16,6 @@ class PyTuiview(PythonPackage): version('1.1.7', '4b3b38a820cc239c8ab4a181ac5d4c30') - depends_on("py-pyqt", type=('build', 'run')) + depends_on("py-pyqt4", type=('build', 'run')) depends_on("py-numpy", type=('build', 'run')) depends_on("gdal") |