From 6fdf5ec5305daeafd3bac787872b4f7ba85745b3 Mon Sep 17 00:00:00 2001 From: Henrik Holst <6200749+hholst80@users.noreply.github.com> Date: Thu, 21 May 2026 15:26:38 +0000 Subject: [PATCH 1/4] Link static libtiff dependencies --- setup.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 6a6bffab6f9..1665b1a8645 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,7 @@ import os import re +import shlex import shutil import struct import subprocess @@ -307,6 +308,41 @@ def _pkg_config(name: str) -> tuple[list[str], list[str]] | None: return None +def _pkg_config_static( + name: str, exclude_libraries: tuple[str | bool | None, ...] = () +) -> tuple[list[str], list[str], list[str]] | None: + command = os.environ.get("PKG_CONFIG", "pkg-config") + for keep_system in (True, False): + try: + command_libs = [command, "--libs", "--static", name] + stderr = None + if keep_system: + command_libs.append("--keep-system-libs") + stderr = subprocess.DEVNULL + if not DEBUG: + command_libs.append("--silence-errors") + libs = [] + library_dirs = [] + extra_link_args = [] + for arg in shlex.split( + subprocess.check_output(command_libs, stderr=stderr) + .decode("utf8") + .strip() + ): + if arg.startswith("-L"): + library_dirs.append(arg[2:]) + elif arg.startswith("-l"): + library = arg[2:] + if library not in exclude_libraries: + libs.append(library) + else: + extra_link_args.append(arg) + return libs, library_dirs, extra_link_args + except Exception: # noqa: PERF203 + pass + return None + + class pil_build_ext(build_ext): class ext_feature: features = [ @@ -441,6 +477,7 @@ def _update_extension( libraries: list[str] | list[str | bool | None], define_macros: list[tuple[str, str | None]] | None = None, sources: list[str] | None = None, + extra_link_args: list[str] | None = None, ) -> None: for extension in self.extensions: if extension.name == name: @@ -449,9 +486,11 @@ def _update_extension( extension.define_macros += define_macros if sources is not None: extension.sources += sources + if extra_link_args is not None: + extension.extra_link_args += extra_link_args if FUZZING_BUILD: extension.language = "c++" - extension.extra_link_args = ["--stdlib=libc++"] + extension.extra_link_args.append("--stdlib=libc++") break def _remove_extension(self, name: str) -> None: @@ -497,6 +536,8 @@ def get_ios_sdk_path(self) -> str: def build_extensions(self) -> None: library_dirs: list[str] = [] include_dirs: list[str] = [] + pkg_config_modules: dict[str, str] = {} + tiff_library: str | None = None pkg_config = None if _cmd_exists(os.environ.get("PKG_CONFIG", "pkg-config")): @@ -531,11 +572,14 @@ def build_extensions(self) -> None: if isinstance(lib_name, str): _dbg("Looking for `%s` using pkg-config.", lib_name) root = pkg_config(lib_name) + if root: + pkg_config_modules[root_name] = lib_name else: for lib_name2 in lib_name: _dbg("Looking for `%s` using pkg-config.", lib_name2) root = pkg_config(lib_name2) if root: + pkg_config_modules[root_name] = lib_name2 break if isinstance(root, tuple): @@ -786,11 +830,12 @@ def build_extensions(self) -> None: if feature.want("tiff"): _dbg("Looking for tiff") if _find_include_file(self, "tiff.h"): - if sys.platform in ["win32", "darwin"] and _find_library_file( - self, "libtiff" - ): + tiff_library = _find_library_file(self, "libtiff") + if sys.platform in ["win32", "darwin"] and tiff_library: feature.set("tiff", "libtiff") - elif _find_library_file(self, "tiff"): + else: + tiff_library = _find_library_file(self, "tiff") + if feature.want("tiff") and tiff_library: feature.set("tiff", "tiff") if feature.want("freetype"): @@ -900,9 +945,24 @@ def build_extensions(self) -> None: libs: list[str | bool | None] = [] defs: list[tuple[str, str | None]] = [] + extra_link_args: list[str] = [] if feature.get("tiff"): libs.append(feature.get("tiff")) defs.append(("HAVE_LIBTIFF", None)) + if tiff_library and tiff_library.endswith(".a"): + pkg_config_module = pkg_config_modules.get("TIFF_ROOT") + if pkg_config_module: + pkg_config_static = _pkg_config_static( + pkg_config_module, (feature.get("tiff"),) + ) + if pkg_config_static: + tiff_libs, tiff_library_dirs, tiff_extra_link_args = ( + pkg_config_static + ) + for library_dir in tiff_library_dirs: + _add_directory(self.compiler.library_dirs, library_dir) + libs.extend(tiff_libs) + extra_link_args.extend(tiff_extra_link_args) if sys.platform == "win32": # This define needs to be defined if-and-only-if it was defined # when compiling LibTIFF. LibTIFF doesn't expose it in `tiffconf.h`, @@ -939,7 +999,9 @@ def build_extensions(self) -> None: defs.append(("PILLOW_VERSION", f'"{PILLOW_VERSION}"')) - self._update_extension("PIL._imaging", libs, defs) + self._update_extension( + "PIL._imaging", libs, defs, extra_link_args=extra_link_args + ) # # additional libraries From 46623d6aa5ecedd259074f8f21b9de398675ebfb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 22 May 2026 12:02:05 +1000 Subject: [PATCH 2/4] Use walrus operator --- setup.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 1665b1a8645..73c5ee1b991 100644 --- a/setup.py +++ b/setup.py @@ -571,14 +571,12 @@ def build_extensions(self) -> None: if root is None and pkg_config: if isinstance(lib_name, str): _dbg("Looking for `%s` using pkg-config.", lib_name) - root = pkg_config(lib_name) - if root: + if root := pkg_config(lib_name): pkg_config_modules[root_name] = lib_name else: for lib_name2 in lib_name: _dbg("Looking for `%s` using pkg-config.", lib_name2) - root = pkg_config(lib_name2) - if root: + if root := pkg_config(lib_name2): pkg_config_modules[root_name] = lib_name2 break @@ -950,8 +948,7 @@ def build_extensions(self) -> None: libs.append(feature.get("tiff")) defs.append(("HAVE_LIBTIFF", None)) if tiff_library and tiff_library.endswith(".a"): - pkg_config_module = pkg_config_modules.get("TIFF_ROOT") - if pkg_config_module: + if pkg_config_module := pkg_config_modules.get("TIFF_ROOT"): pkg_config_static = _pkg_config_static( pkg_config_module, (feature.get("tiff"),) ) From 35351cbd45ffc444494bcd80905710131fb0d9be Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 22 May 2026 12:04:18 +1000 Subject: [PATCH 3/4] Remove unused default argument --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 73c5ee1b991..dd8814bb3b7 100644 --- a/setup.py +++ b/setup.py @@ -309,7 +309,7 @@ def _pkg_config(name: str) -> tuple[list[str], list[str]] | None: def _pkg_config_static( - name: str, exclude_libraries: tuple[str | bool | None, ...] = () + name: str, exclude_libraries: tuple[str | bool | None, ...] ) -> tuple[list[str], list[str], list[str]] | None: command = os.environ.get("PKG_CONFIG", "pkg-config") for keep_system in (True, False): From 2147fa96bb8a2805f03449e6c22ef01385725a84 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 22 May 2026 12:08:43 +1000 Subject: [PATCH 4/4] Simplified argument by changing to exclude_library --- setup.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index dd8814bb3b7..8dddbf7d422 100644 --- a/setup.py +++ b/setup.py @@ -309,7 +309,7 @@ def _pkg_config(name: str) -> tuple[list[str], list[str]] | None: def _pkg_config_static( - name: str, exclude_libraries: tuple[str | bool | None, ...] + name: str, exclude_library: str | None ) -> tuple[list[str], list[str], list[str]] | None: command = os.environ.get("PKG_CONFIG", "pkg-config") for keep_system in (True, False): @@ -333,7 +333,7 @@ def _pkg_config_static( library_dirs.append(arg[2:]) elif arg.startswith("-l"): library = arg[2:] - if library not in exclude_libraries: + if library != exclude_library: libs.append(library) else: extra_link_args.append(arg) @@ -949,8 +949,10 @@ def build_extensions(self) -> None: defs.append(("HAVE_LIBTIFF", None)) if tiff_library and tiff_library.endswith(".a"): if pkg_config_module := pkg_config_modules.get("TIFF_ROOT"): + exclude_library = feature.get("tiff") pkg_config_static = _pkg_config_static( - pkg_config_module, (feature.get("tiff"),) + pkg_config_module, + exclude_library if isinstance(exclude_library, str) else None, ) if pkg_config_static: tiff_libs, tiff_library_dirs, tiff_extra_link_args = (