88# ------------------------------
99from __future__ import annotations
1010
11+ import distutils .ccompiler
1112import os
1213import re
1314import shutil
1415import struct
1516import subprocess
1617import sys
18+ import tempfile
1719import warnings
1820from collections .abc import Iterator
1921from typing import Any
2022
2123from setuptools import Extension , setup
2224from setuptools .command .build_ext import build_ext
25+ from setuptools .errors import CompileError
2326
2427
2528def get_version () -> str :
@@ -292,6 +295,47 @@ def _pkg_config(name: str) -> tuple[list[str], list[str]] | None:
292295 return None
293296
294297
298+ def _try_compile (compiler : distutils .ccompiler .CCompiler , code : str ) -> bool :
299+ try :
300+ with tempfile .TemporaryDirectory () as d :
301+ fn = os .path .join (d , "test.c" )
302+ with open (fn , "w" ) as f :
303+ f .write (code )
304+ compiler .compile ([fn ], output_dir = d , extra_preargs = ["-Werror" ])
305+ return True
306+ except CompileError :
307+ return False
308+
309+
310+ def _try_compile_attr (compiler : distutils .ccompiler .CCompiler , attr : str ) -> bool :
311+ code = f"""
312+ #pragma GCC diagnostic error "-Wattributes"
313+ #pragma clang diagnostic error "-Wattributes"
314+
315+ int { attr } foo;
316+ int main() {{
317+ return 0;
318+ }}
319+ """
320+
321+ return _try_compile (compiler , code )
322+
323+
324+ def _try_compile_tls_define_macros (
325+ compiler : distutils .ccompiler .CCompiler ,
326+ ) -> list [tuple [str , str | None ]]:
327+ if _try_compile_attr (compiler , "thread_local" ): # C23
328+ return [("HAVE_THREAD_LOCAL" , None )]
329+ elif _try_compile_attr (compiler , "_Thread_local" ): # C11/C17
330+ return [("HAVE__THREAD_LOCAL" , None )]
331+ elif _try_compile_attr (compiler , "__thread" ): # GCC/clang
332+ return [("HAVE___THREAD" , None )]
333+ elif _try_compile_attr (compiler , "__declspec(thread)" ): # MSVC
334+ return [("HAVE___DECLSPEC_THREAD_" , None )]
335+ else :
336+ return []
337+
338+
295339class pil_build_ext (build_ext ):
296340 class ext_feature :
297341 features = [
@@ -426,13 +470,14 @@ def finalize_options(self) -> None:
426470 def _update_extension (
427471 self ,
428472 name : str ,
429- libraries : list [str ] | list [str | bool | None ],
473+ libraries : list [str ] | list [str | bool | None ] | None = None ,
430474 define_macros : list [tuple [str , str | None ]] | None = None ,
431475 sources : list [str ] | None = None ,
432476 ) -> None :
433477 for extension in self .extensions :
434478 if extension .name == name :
435- extension .libraries += libraries
479+ if libraries is not None :
480+ extension .libraries += libraries
436481 if define_macros is not None :
437482 extension .define_macros += define_macros
438483 if sources is not None :
@@ -890,7 +935,10 @@ def build_extensions(self) -> None:
890935
891936 defs .append (("PILLOW_VERSION" , f'"{ PILLOW_VERSION } "' ))
892937
893- self ._update_extension ("PIL._imaging" , libs , defs )
938+ tls_define_macros = _try_compile_tls_define_macros (self .compiler )
939+ self ._update_extension ("PIL._imaging" , libs , defs + tls_define_macros )
940+ self ._update_extension ("PIL._imagingmath" , define_macros = tls_define_macros )
941+ self ._update_extension ("PIL._imagingmorph" , define_macros = tls_define_macros )
894942
895943 #
896944 # additional libraries
@@ -913,7 +961,9 @@ def build_extensions(self) -> None:
913961 libs .append (feature .get ("fribidi" ))
914962 else : # building FriBiDi shim from src/thirdparty
915963 srcs .append ("src/thirdparty/fribidi-shim/fribidi.c" )
916- self ._update_extension ("PIL._imagingft" , libs , defs , srcs )
964+ self ._update_extension (
965+ "PIL._imagingft" , libs , defs + tls_define_macros , srcs
966+ )
917967
918968 else :
919969 self ._remove_extension ("PIL._imagingft" )
@@ -922,19 +972,19 @@ def build_extensions(self) -> None:
922972 libs = [feature .get ("lcms" )]
923973 if sys .platform == "win32" :
924974 libs .extend (["user32" , "gdi32" ])
925- self ._update_extension ("PIL._imagingcms" , libs )
975+ self ._update_extension ("PIL._imagingcms" , libs , tls_define_macros )
926976 else :
927977 self ._remove_extension ("PIL._imagingcms" )
928978
929979 webp = feature .get ("webp" )
930980 if isinstance (webp , str ):
931981 libs = [webp , webp + "mux" , webp + "demux" ]
932- self ._update_extension ("PIL._webp" , libs )
982+ self ._update_extension ("PIL._webp" , libs , tls_define_macros )
933983 else :
934984 self ._remove_extension ("PIL._webp" )
935985
936986 tk_libs = ["psapi" ] if sys .platform in ("win32" , "cygwin" ) else []
937- self ._update_extension ("PIL._imagingtk" , tk_libs )
987+ self ._update_extension ("PIL._imagingtk" , tk_libs , tls_define_macros )
938988
939989 build_ext .build_extensions (self )
940990
@@ -972,7 +1022,7 @@ def summary_report(self, feature: ext_feature) -> None:
9721022 (feature .get ("tiff" ), "LIBTIFF" ),
9731023 (feature .get ("freetype" ), "FREETYPE2" ),
9741024 (feature .get ("raqm" ), "RAQM (Text shaping)" , raqm_extra_info ),
975- (feature .get ("lcms" ), "LITTLECMS2 " ),
1025+ (feature .get ("lcms" ), "LITTLECoMS2 " ),
9761026 (feature .get ("webp" ), "WEBP" ),
9771027 (feature .get ("xcb" ), "XCB (X protocol)" ),
9781028 ]
0 commit comments