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
@@ -292,6 +294,47 @@ def _pkg_config(name: str) -> tuple[list[str], list[str]] | None:
292294 return None
293295
294296
297+ def _try_compile (compiler : distutils .ccompiler .CCompiler , code : str ) -> bool :
298+ try :
299+ with tempfile .TemporaryDirectory () as d :
300+ fn = os .path .join (d , "test.c" )
301+ with open (fn , "w" ) as f :
302+ f .write (code )
303+ compiler .compile ([fn ], output_dir = d , extra_preargs = ["-Werror" ])
304+ return True
305+ except distutils .ccompiler .CompileError :
306+ return False
307+
308+
309+ def _try_compile_attr (compiler : distutils .ccompiler .CCompiler , attr : str ) -> bool :
310+ code = f"""
311+ #pragma GCC diagnostic error "-Wattributes"
312+ #pragma clang diagnostic error "-Wattributes"
313+
314+ int { attr } foo;
315+ int main() {{
316+ return 0;
317+ }}
318+ """
319+
320+ return _try_compile (compiler , code )
321+
322+
323+ def _try_compile_tls_define_macros (
324+ compiler : distutils .ccompiler .CCompiler ,
325+ ) -> list [tuple [str , str | None ]]:
326+ if _try_compile_attr (compiler , "thread_local" ): # C23
327+ return [("HAVE_THREAD_LOCAL" , None )]
328+ elif _try_compile_attr (compiler , "_Thread_local" ): # C11/C17
329+ return [("HAVE__THREAD_LOCAL" , None )]
330+ elif _try_compile_attr (compiler , "__thread" ): # GCC/clang
331+ return [("HAVE___THREAD" , None )]
332+ elif _try_compile_attr (compiler , "__declspec(thread)" ): # MSVC
333+ return [("HAVE___DECLSPEC_THREAD_" , None )]
334+ else :
335+ return []
336+
337+
295338class pil_build_ext (build_ext ):
296339 class ext_feature :
297340 features = [
@@ -426,13 +469,14 @@ def finalize_options(self) -> None:
426469 def _update_extension (
427470 self ,
428471 name : str ,
429- libraries : list [str ] | list [str | bool | None ],
472+ libraries : list [str ] | list [str | bool | None ] | None = None ,
430473 define_macros : list [tuple [str , str | None ]] | None = None ,
431474 sources : list [str ] | None = None ,
432475 ) -> None :
433476 for extension in self .extensions :
434477 if extension .name == name :
435- extension .libraries += libraries
478+ if libraries is not None :
479+ extension .libraries += libraries
436480 if define_macros is not None :
437481 extension .define_macros += define_macros
438482 if sources is not None :
@@ -890,7 +934,10 @@ def build_extensions(self) -> None:
890934
891935 defs .append (("PILLOW_VERSION" , f'"{ PILLOW_VERSION } "' ))
892936
893- self ._update_extension ("PIL._imaging" , libs , defs )
937+ tls_define_macros = _try_compile_tls_define_macros (self .compiler )
938+ self ._update_extension ("PIL._imaging" , libs , defs + tls_define_macros )
939+ self ._update_extension ("PIL._imagingmath" , define_macros = tls_define_macros [:])
940+ self ._update_extension ("PIL._imagingmorph" , define_macros = tls_define_macros [:])
894941
895942 #
896943 # additional libraries
@@ -913,7 +960,9 @@ def build_extensions(self) -> None:
913960 libs .append (feature .get ("fribidi" ))
914961 else : # building FriBiDi shim from src/thirdparty
915962 srcs .append ("src/thirdparty/fribidi-shim/fribidi.c" )
916- self ._update_extension ("PIL._imagingft" , libs , defs , srcs )
963+ self ._update_extension (
964+ "PIL._imagingft" , libs , defs + tls_define_macros [:], srcs
965+ )
917966
918967 else :
919968 self ._remove_extension ("PIL._imagingft" )
@@ -922,19 +971,19 @@ def build_extensions(self) -> None:
922971 libs = [feature .get ("lcms" )]
923972 if sys .platform == "win32" :
924973 libs .extend (["user32" , "gdi32" ])
925- self ._update_extension ("PIL._imagingcms" , libs )
974+ self ._update_extension ("PIL._imagingcms" , libs , tls_define_macros [:] )
926975 else :
927976 self ._remove_extension ("PIL._imagingcms" )
928977
929978 webp = feature .get ("webp" )
930979 if isinstance (webp , str ):
931980 libs = [webp , webp + "mux" , webp + "demux" ]
932- self ._update_extension ("PIL._webp" , libs )
981+ self ._update_extension ("PIL._webp" , libs , tls_define_macros [:] )
933982 else :
934983 self ._remove_extension ("PIL._webp" )
935984
936985 tk_libs = ["psapi" ] if sys .platform in ("win32" , "cygwin" ) else []
937- self ._update_extension ("PIL._imagingtk" , tk_libs )
986+ self ._update_extension ("PIL._imagingtk" , tk_libs , tls_define_macros [:] )
938987
939988 build_ext .build_extensions (self )
940989
0 commit comments