[libgpiod][PATCH v4 1/1] bindings: python: optionally include module in sdist

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Optionally vendor libgpiod source into sdist so that the
Python module can be built from source, even with a missing
or mismatched system libgpiod.

Add optional environment variable "LINK_SYSTEM_LIBGPIOD=1"
so that the sdist package can optionally be built and
linked against a compatible system libgpiod.

eg: LINK_SYSTEM_LIBGPIOD=1 pip install libgpiod

Update build to add an additional sdist target for upload
to pypi. Call setup.py with "GPIOD_VERSION_STR" set, which
triggers a vendored package build. "GPIOD_VERSION_STR" is
saved to "gpiod-version-str.txt" and included in the sdist
for standalone builds.

"GPIOD_VERSION_STR" must be specified in order to produce
a standalone buildable sdist package, this requirement
implicitly preserves the old build behaviour.

Signed-off-by: Phil Howard <phil@xxxxxxxxxxxxx>
---
 bindings/python/MANIFEST.in |   5 ++
 bindings/python/Makefile.am |   3 +
 bindings/python/setup.py    | 122 +++++++++++++++++++++++++++++++-----
 3 files changed, 114 insertions(+), 16 deletions(-)

diff --git a/bindings/python/MANIFEST.in b/bindings/python/MANIFEST.in
index c7124d4..acf9391 100644
--- a/bindings/python/MANIFEST.in
+++ b/bindings/python/MANIFEST.in
@@ -2,6 +2,7 @@
 # SPDX-FileCopyrightText: 2023 Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxx>
 
 include setup.py
+include gpiod-version-str.txt
 
 recursive-include gpiod *.py
 recursive-include tests *.py
@@ -11,3 +12,7 @@ recursive-include gpiod/ext *.h
 
 recursive-include tests/gpiosim *.c
 recursive-include tests/procname *.c
+
+recursive-include lib *.c
+recursive-include lib *.h
+recursive-include include *.h
diff --git a/bindings/python/Makefile.am b/bindings/python/Makefile.am
index 079ceb1..7fadf52 100644
--- a/bindings/python/Makefile.am
+++ b/bindings/python/Makefile.am
@@ -16,6 +16,9 @@ all-local:
 	$(PYTHON) setup.py build_ext --inplace \
 		--include-dirs=$(top_srcdir)/include/:$(top_srcdir)/tests/gpiosim/ \
 		--library-dirs=$(top_builddir)/lib/.libs/:$(top_srcdir)/tests/gpiosim/.libs/
+	GPIOD_VERSION_STR=$(VERSION_STR) \
+	$(PYTHON) setup.py sdist
+
 
 install-exec-local:
 	GPIOD_WITH_TESTS= \
diff --git a/bindings/python/setup.py b/bindings/python/setup.py
index df10e18..168c1af 100644
--- a/bindings/python/setup.py
+++ b/bindings/python/setup.py
@@ -1,10 +1,49 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 # SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@xxxxxxxx>
 
-from os import environ, path
+from os import environ, path, unlink
 from setuptools import setup, Extension, find_packages
 from setuptools.command.build_ext import build_ext as orig_build_ext
-from shutil import rmtree
+from setuptools.command.sdist import sdist as orig_sdist
+from shutil import rmtree, copytree
+
+
+def get_gpiod_version_str():
+    try:
+        return environ["GPIOD_VERSION_STR"]
+    except KeyError:
+        pass
+    try:
+        return open("gpiod-version-str.txt", "r").read()
+    except OSError:
+        return None
+
+
+def copy_libgpiod_files(func):
+    """
+    In order to include the lib and include directories in the sdist
+    we must temporarily copy them up into the python bindings directory.
+
+    If "./lib" exists we are building from an sdist package and will not
+    try to copy the files again.
+    """
+
+    def wrapper(self):
+        copy_src = not path.exists("./lib")
+        if copy_src:
+            gpiod_version_str = get_gpiod_version_str()
+            if gpiod_version_str is not None:
+                open("gpiod-version-str.txt", "w").write(gpiod_version_str)
+            copytree("../../lib", "./lib")
+            copytree("../../include", "./include")
+        func(self)
+        if copy_src:
+            if gpiod_version_str is not None:
+                unlink("gpiod-version-str.txt")
+            rmtree("./lib")
+            rmtree("./include")
+
+    return wrapper
 
 
 class build_ext(orig_build_ext):
@@ -14,24 +53,78 @@ class build_ext(orig_build_ext):
     were built (and possibly copied to the source directory if inplace is set).
     """
 
+    @copy_libgpiod_files
     def run(self):
         super().run()
         rmtree(path.join(self.build_lib, "tests"), ignore_errors=True)
 
 
+class sdist(orig_sdist):
+    """
+    Wrap sdist so that we can copy the lib and include files into . where
+    MANIFEST.in will include them in the source package.
+    """
+
+    @copy_libgpiod_files
+    def run(self):
+        super().run()
+
+
+with open("gpiod/version.py", "r") as fd:
+    exec(fd.read())
+
+sources = [
+    # gpiod Python bindings
+    "gpiod/ext/chip.c",
+    "gpiod/ext/common.c",
+    "gpiod/ext/line-config.c",
+    "gpiod/ext/line-settings.c",
+    "gpiod/ext/module.c",
+    "gpiod/ext/request.c",
+]
+
+extra_compile_args = [
+    "-Wall",
+    "-Wextra",
+]
+
+libraries = ["gpiod"]
+include_dirs = ["gpiod"]
+
+if environ.get("LINK_SYSTEM_LIBGPIOD") == "1":
+    print("linking system libgpiod (requested by LINK_SYSTEM_LIBGPIOD)")
+elif get_gpiod_version_str() is None:
+    print("warning: linking system libgpiod (GPIOD_VERSION_STR not specified)")
+else:
+    print("vendoring libgpiod into standalone library")
+    sources += [
+        # gpiod library
+        "lib/chip.c",
+        "lib/chip-info.c",
+        "lib/edge-event.c",
+        "lib/info-event.c",
+        "lib/internal.c",
+        "lib/line-config.c",
+        "lib/line-info.c",
+        "lib/line-request.c",
+        "lib/line-settings.c",
+        "lib/misc.c",
+        "lib/request-config.c",
+    ]
+    libraries = []
+    include_dirs = ["include", "lib", "gpiod/ext"]
+    extra_compile_args += [
+        '-DGPIOD_VERSION_STR="{}"'.format(get_gpiod_version_str()),
+    ]
+
+
 gpiod_ext = Extension(
     "gpiod._ext",
-    sources=[
-        "gpiod/ext/chip.c",
-        "gpiod/ext/common.c",
-        "gpiod/ext/line-config.c",
-        "gpiod/ext/line-settings.c",
-        "gpiod/ext/module.c",
-        "gpiod/ext/request.c",
-    ],
+    libraries=libraries,
+    sources=sources,
     define_macros=[("_GNU_SOURCE", "1")],
-    libraries=["gpiod"],
-    extra_compile_args=["-Wall", "-Wextra"],
+    include_dirs=include_dirs,
+    extra_compile_args=extra_compile_args,
 )
 
 gpiosim_ext = Extension(
@@ -54,15 +147,12 @@ if environ.get("GPIOD_WITH_TESTS") == "1":
     extensions.append(gpiosim_ext)
     extensions.append(procname_ext)
 
-with open("gpiod/version.py", "r") as fd:
-    exec(fd.read())
-
 setup(
     name="libgpiod",
     packages=find_packages(exclude=["tests", "tests.*"]),
     python_requires=">=3.9.0",
     ext_modules=extensions,
-    cmdclass={"build_ext": build_ext},
+    cmdclass={"build_ext": build_ext, "sdist": sdist},
     version=__version__,
     author="Bartosz Golaszewski",
     author_email="brgl@xxxxxxxx",
-- 
2.34.1




[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux