summaryrefslogtreecommitdiff
path: root/lib/spack/spack/build_systems/sip.py
blob: 884c740d561d4f7b1033ab4b22346212adba19f8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# Copyright 2013-2023 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
import re

import llnl.util.tty as tty
from llnl.util.filesystem import find, working_dir

import spack.builder
import spack.install_test
import spack.package_base
from spack.directives import build_system, depends_on, extends
from spack.multimethod import when
from spack.util.executable import Executable

from ._checks import BaseBuilder, execute_install_time_tests


class SIPPackage(spack.package_base.PackageBase):
    """Specialized class for packages that are built using the
    SIP build system. See https://www.riverbankcomputing.com/software/sip/intro
    for more information.
    """

    # To be used in UI queries that require to know which
    # build-system class we are using
    build_system_class = "SIPPackage"

    #: Name of private sip module to install alongside package
    sip_module = "sip"

    #: Callback names for install-time testing
    install_time_test_callbacks = ["test_imports"]
    #: Legacy buildsystem attribute used to deserialize and install old specs
    legacy_buildsystem = "sip"

    build_system("sip")

    with when("build_system=sip"):
        extends("python", type=("build", "link", "run"))
        depends_on("py-sip", type="build")

    @property
    def import_modules(self):
        """Names of modules that the Python package provides.

        These are used to test whether or not the installation succeeded.
        These names generally come from running:

        .. code-block:: python

           >> import setuptools
           >> setuptools.find_packages()

        in the source tarball directory. If the module names are incorrectly
        detected, this property can be overridden by the package.

        Returns:
            list: list of strings of module names
        """
        modules = []
        root = os.path.join(self.prefix, self.spec["python"].package.platlib)

        # Some Python libraries are packages: collections of modules
        # distributed in directories containing __init__.py files
        for path in find(root, "__init__.py", recursive=True):
            modules.append(
                path.replace(root + os.sep, "", 1)
                .replace(os.sep + "__init__.py", "")
                .replace("/", ".")
            )

        # Some Python libraries are modules: individual *.py files
        # found in the site-packages directory
        for path in find(root, "*.py", recursive=False):
            modules.append(path.replace(root + os.sep, "", 1).replace(".py", "").replace("/", "."))

        modules = [mod for mod in modules if re.match("[a-zA-Z0-9._]+$", mod)]

        tty.debug("Detected the following modules: {0}".format(modules))

        return modules

    def python(self, *args, **kwargs):
        """The python ``Executable``."""
        inspect.getmodule(self).python(*args, **kwargs)

    def test_imports(self):
        """Attempts to import modules of the installed package."""

        # Make sure we are importing the installed modules,
        # not the ones in the source directory
        python = inspect.getmodule(self).python
        for module in self.import_modules:
            with spack.install_test.test_part(
                self,
                "test_imports_{0}".format(module),
                purpose="checking import of {0}".format(module),
                work_dir="spack-test",
            ):
                python("-c", "import {0}".format(module))


@spack.builder.builder("sip")
class SIPBuilder(BaseBuilder):
    """The SIP builder 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 ``sip-build --help``.
    """

    phases = ("configure", "build", "install")

    #: Names associated with package methods in the old build-system format
    legacy_methods = ("configure_args", "build_args", "install_args")

    #: Names associated with package attributes in the old build-system format
    legacy_attributes = (
        "build_targets",
        "install_targets",
        "build_time_test_callbacks",
        "install_time_test_callbacks",
        "build_directory",
    )

    build_directory = "build"

    def configure(self, pkg, spec, prefix):
        """Configure the package."""

        # https://www.riverbankcomputing.com/static/Docs/sip/command_line_tools.html
        args = ["--verbose", "--target-dir", inspect.getmodule(self.pkg).python_platlib]
        args.extend(self.configure_args())

        sip_build = Executable(spec["py-sip"].prefix.bin.join("sip-build"))
        sip_build(*args)

    def configure_args(self):
        """Arguments to pass to configure."""
        return []

    def build(self, pkg, spec, prefix):
        """Build the package."""
        args = self.build_args()

        with working_dir(self.build_directory):
            inspect.getmodule(self.pkg).make(*args)

    def build_args(self):
        """Arguments to pass to build."""
        return []

    def install(self, pkg, spec, prefix):
        """Install the package."""
        args = self.install_args()

        with working_dir(self.build_directory):
            inspect.getmodule(self.pkg).make("install", *args)

    def install_args(self):
        """Arguments to pass to install."""
        return []

    spack.builder.run_after("install")(execute_install_time_tests)