Add SIPPackage base class (#12157)
This commit is contained in:
@@ -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
|
||||
|
135
lib/spack/docs/build_systems/sippackage.rst
Normal file
135
lib/spack/docs/build_systems/sippackage.rst
Normal file
@@ -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
|
113
lib/spack/spack/build_systems/sip.py
Normal file
113
lib/spack/spack/build_systems/sip.py
Normal file
@@ -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)
|
@@ -13,6 +13,7 @@
|
||||
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 @@
|
||||
PythonPackage: 'build',
|
||||
PerlPackage: 'build',
|
||||
MesonPackage: 'build',
|
||||
SIPPackage: 'build',
|
||||
}
|
||||
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
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 @@
|
||||
PerlPackage: 'configure',
|
||||
IntelPackage: 'configure',
|
||||
MesonPackage: 'meson',
|
||||
SIPPackage: 'configure',
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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 @@ class IntelPackageTemplate(PackageTemplate):
|
||||
'makefile': MakefilePackageTemplate,
|
||||
'intel': IntelPackageTemplate,
|
||||
'meson': MesonPackageTemplate,
|
||||
'sip': SIPPackageTemplate,
|
||||
'generic': PackageTemplate,
|
||||
}
|
||||
|
||||
@@ -440,6 +463,7 @@ def __call__(self, stage, url):
|
||||
(r'/(GNU)?[Mm]akefile$', 'makefile'),
|
||||
(r'/DESCRIPTION$', 'octave'),
|
||||
(r'/meson\.build$', 'meson'),
|
||||
(r'/configure\.py$', 'sip'),
|
||||
]
|
||||
|
||||
# Peek inside the compressed file.
|
||||
|
@@ -27,6 +27,7 @@
|
||||
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
|
||||
|
||||
|
@@ -27,6 +27,7 @@
|
||||
('makefile', 'makefile'),
|
||||
('Makefile', 'makefile'),
|
||||
('meson.build', 'meson'),
|
||||
('configure.py', 'sip'),
|
||||
('foobar', 'generic')
|
||||
]
|
||||
)
|
||||
|
Reference in New Issue
Block a user