使用 setup.py / pyproject.toml 在可编辑安装中编译库

问题描述 投票:0回答:1

我正在使用

setuptools
pyproject.toml
设置一个 Python 包。 Python 代码依赖于需要与代码一起编译和安装的 C 库(这是一个
make
项目)。

我已经将一些适用于

pip install .
python -m build
的东西放在一起,以制作可分发的:

# pyproject.toml

[project]
name = "mypackage"

[build-system]
requires = ["setuptools >= 61.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.setuptools]
packages = ["mypackage"]
package-dir = { "" = "src" }
# setup.py

from pathlib import Path
from setuptools import setup
from setuptools.command.install import install
from setuptools.command.develop import develop
from setuptools.command.build import build
import os
import subprocess


mylib_relative = "mylib"
mylib_root = Path(__file__).parent.absolute() / mylib_relative


def create_binaries():
    subprocess.call(["make", "-C", mylib_relative])


def remove_binaries():
    patterns = (
        "*.a",
        "**/*.o",
        "*.bin",
        "*.so",
    )
    for pattern in patterns:
        for file in mylib_root.glob(pattern):
            os.remove(file)


class CustomBuild(build):
    def run(self):
        print("\nCustomBuild!\n")

        remove_binaries()
        create_binaries()
        super().run()


class CustomDevelop(develop):
    def run(self):
        print("\nCustomDevelop!\n")

        remove_binaries()
        create_binaries()
        super().run()


class CustomInstall(install):
    def run(self):

        print("\n\nCustomInstall\n\n")

        mylib_lib = mylib_root / "adslib.so"
        mylib_dest = Path(self.install_lib)
        if not mylib_dest.exists():
            mylib_dest.mkdir(parents=True)
        self.copy_file(
            str(mylib_lib),
            str(mylib_dest),
        )
        super().run()


setup(
    cmdclass={
        "build": CustomBuild,
        "develop": CustomDevelop,
        "install": CustomInstall,
    },
)

但是,当我使用 pip 和

pip install -e . [-v]
进行可编辑安装时,该库未编译和安装,仅将 Python 源添加到 venv 路径中。但如果没有库,该包将无法工作。

你可以看到我已经在

develop
中添加了
setup.py
命令,但看起来它根本没有被调用过。

如何自定义可编辑安装以首先编译我的库?

python setuptools distribution setup.py
1个回答
0
投票

我找到了一个可以使用的解决方案/解决方法。在

pip install -e .
之后,包含您的包的目录(通常为
src/
)将被附加到 PATH(通过从我的包中打印
sys.path
进行测试)。

因此,如果我只是确保将编译的库放入

src/
,则在可编辑安装后它也将在 PATH 上可用,就像正常安装一样。如果
.so
文件位于
.gitignore
下,那么它不应该因为位于
src/
而不是库目录中而打扰任何人。

完整

setup.py

from pathlib import Path
from setuptools import setup
from setuptools.command.install import install
from setuptools.command.build_py import build_py
import os
import subprocess


src_folder = Path(__file__).parent.absolute() / "src"
# ^ This will be on PATH for editable install
mylib_folder = Path(__file__).parent.absolute() / "mylib"
mylib_file = src_folder / "mylib.so"


class CustomBuildPy(build_py):
    """Custom command for `build_py`.

    This command class is used because it is always run, also for an editable install.
    """

    @classmethod
    def compile_mylib(cls):
        """Return `True` if mylib was actually compiled."""
        cls._clean_library()
        cls._compile_library()

    @staticmethod
    def _compile_library():
        """Use `make` to build mylib - build is done in-place."""
        # Produce `mylib.so`:
        subprocess.call(["make", "-C", "mylib"])

    @staticmethod
    def _clean_library():
        """Remove all compilation artifacts."""
        patterns = (
            "*.a",
            "**/*.o",
            "*.bin",
            "*.so",
        )
        for pattern in patterns:
            for file in mylib_folder.glob(pattern):
                os.remove(file)

        if mylib_file.is_file():
            os.remove(mylib_file)

    def run(self):
        # Move .so file into src/ to have it on PATH:
        self.compile_adslib()
        self.move_file(
            str(mylib_folder / "mylib.so"),
            str(mylib_file),
        )

        super().run()


class CustomInstall(install):
    """Install compiled mylib (but only for Linux)."""
    def run(self):
        mylib_dest = Path(self.install_lib)
        if not mylib_dest.exists():
            mylib_dest.mkdir(parents=True)
        self.copy_file(
            str(mylib_file),
            str(mylib_dest),
        )
        super().run()


setup(
    cmdclass={
        "build_py": CustomBuildPy,
        "install": CustomInstall,
    },
)
# See `pyproject.toml` for all package information

# Also see `MANIFEST.in`
© www.soinside.com 2019 - 2024. All rights reserved.