summaryrefslogblamecommitdiff
path: root/var/spack/repos/builtin/packages/pypy/package.py
blob: 3f1ec58764a62bec12150b8745b5c85d57040995 (plain) (tree)





































                                                                                                 
                                        



































































































































                                                                                                          
# 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)

from spack.package import *


class Pypy(Package):
    """A fast, compliant alternative implementation of Python."""

    homepage = "https://www.pypy.org/"
    url = "https://downloads.python.org/pypy/pypy3.10-v7.3.12-src.tar.bz2"
    hg = "https://foss.heptapod.net/pypy/pypy"

    maintainers("adamjstewart")

    version(
        "3.10-v7.3.12", sha256="86e4e4eacc36046c6182f43018796537fe33a60e1d2a2cc6b8e7f91a5dcb3e42"
    )

    variant("ctypes", default=True, description="Build ctypes module")
    variant("zlib", default=True, description="Build zlib module")
    variant("bz2", default=True, description="Build bz2 module")
    variant("pyexpat", default=True, description="Build pyexpat module")
    variant("sqlite3", default=True, description="Build sqlite3 module")
    variant("ssl", default=True, description="Build ssl module")
    variant("curses", default=True, description="Build curses module")
    variant("dbm", default=True, description="Build dbm module")
    variant("tkinter", default=False, description="Build tkinter module")
    variant("lzma", default=True, description="Build lzma module")

    # https://doc.pypy.org/en/latest/build.html#install-build-time-dependencies
    depends_on("pypy-bootstrap", type="build")  # any Python 2 executable
    # depends_on("py-cffi", type="build")  # only for CPython

    depends_on("libffi", when="+ctypes")
    depends_on("pkgconfig", when="+ctypes", type="build")
    depends_on("zlib-api", when="+zlib")
    depends_on("bzip2", when="+bz2")
    depends_on("expat", when="+pyexpat")
    depends_on("sqlite", when="+sqlite3")
    depends_on("openssl", when="+ssl")
    depends_on("ncurses", when="+curses")
    depends_on("gdbm", when="+dbm")
    depends_on("tcl", when="+tkinter")
    depends_on("tk", when="+tkinter")
    depends_on("libx11", when="+tkinter")
    depends_on("xz", when="+lzma")

    # TODO: add testing
    # https://doc.pypy.org/en/latest/contributing.html#testing
    # depends_on("bdw-gc@7.4:", type="test")
    # depends_on("openssl", type="test")
    # depends_on("py-pycparser", type="test")
    # depends_on("py-hypothesis", type="test")

    phases = ["translate", "build", "package", "install"]

    @property
    def build_directory(self):
        return join_path(self.stage.source_path, "pypy", "goal")

    def patch(self):
        # Fix detection of tcl/tk
        tklib_build = FileFilter(join_path("lib_pypy", "_tkinter", "tklib_build.py"))
        if "+tkinter" in self.spec:
            incs = self.spec["tcl"].headers + self.spec["tk"].headers
            libs = self.spec["tcl"].libs + self.spec["tk"].libs
            tklib_build.filter("incdirs = .*", f"incdirs = {incs.directories}")
            tklib_build.filter("linklibs = .*", f"linklibs = {libs.names}")
            tklib_build.filter("libdirs = .*", f"libdirs = {libs.directories}")

    def setup_build_environment(self, env):
        # https://doc.pypy.org/en/latest/build.html#set-environment-variables-that-will-affect-translation
        env.set("PYPY_USESSION_DIR", self.stage.source_path)
        env.prepend_path("PYTHONPATH", self.stage.source_path)

    def translate_args(self):
        args = ["--opt=jit", "--shared", "--make-jobs", str(make_jobs), "targetpypystandalone.py"]

        variant_to_flag = {
            "ctypes": "_cffi_backend",
            "bz2": "bz2",
            "zlib": "zlib",
            "pyexpat": "pyexpat",
        }

        for variant, flag in variant_to_flag.items():
            if f"+{variant}" in self.spec:
                args.append(f"--withmod-{flag}")
            else:
                args.append(f"--withoutmod-{flag}")

        return args

    def translate(self, spec, prefix):
        # https://doc.pypy.org/en/latest/build.html#run-the-translation
        rpython = join_path(self.stage.source_path, "rpython", "bin", "rpython")
        with working_dir(self.build_directory):
            pypy(rpython, *self.translate_args())

    def build_args(self):
        modules = ["audioop", "syslog", "grp", "resource", "_posixshmem"]

        if "+ctypes" in self.spec:
            modules.extend(["_ctypes._ctypes_cffi", "_pypy_util_cffi_inner"])

        if "+ssl" in self.spec:
            modules.extend(["_blake2", "_ssl", "_sha3"])

        if "+sqlite3" in self.spec:
            modules.append("sqlite3")

        if "+tkinter" in self.spec:
            modules.append("_tkinter")

        if "+curses" in self.spec:
            modules.append("curses")

        if "+dbm" in self.spec:
            modules.append("_gdbm")

        if "+lzma" in self.spec:
            modules.append("lzma")

        return ["--only=" + ",".join(modules)]

    def build(self, spec, prefix):
        # https://doc.pypy.org/en/latest/build.html#build-cffi-import-libraries-for-the-stdlib
        build_cffi_imports = join_path(
            self.stage.source_path, "lib_pypy", "pypy_tools", "build_cffi_imports.py"
        )
        pypy_c = Executable(join_path(self.build_directory, f"pypy{self.version.up_to(2)}-c"))
        with working_dir(self.build_directory):
            pypy_c(build_cffi_imports, *self.build_args())

    def package_args(self):
        args = [
            f"--builddir={self.build_directory}",
            "--no-embedded-dependencies",
            "--no-make-portable",
        ]

        variant_to_flag = {
            "dbm": "_gdbm",
            "ssl": "_ssl",
            "curses": "curses",
            "lzma": "lzma",
            "sqlite3": "sqlite3",
            "tkinter": "_tkinter",
            "ctypes": "cffi",
        }

        for variant, flag in variant_to_flag.items():
            if f"~{variant}" in self.spec:
                args.append(f"--without-{flag}")

        return args

    def package(self, spec, prefix):
        # https://doc.pypy.org/en/latest/build.html#packaging-preparing-for-installation
        package = join_path(self.stage.source_path, "pypy", "tool", "release", "package.py")
        pypy(package, *self.package_args())

    def install(self, spec, prefix):
        # https://doc.pypy.org/en/latest/build.html#installation
        with working_dir(join_path(self.build_directory, "pypy-nightly")):
            install_tree("bin", prefix.bin)
            install_tree("include", prefix.include)
            install_tree("lib", prefix.lib)