mirror of
https://github.com/python/cpython.git
synced 2025-08-31 05:58:33 +00:00
bpo-34956: Fix macOS _tkinter use of Tcl/Tk in /Library/Frameworks (GH-20171)
_tkinter now builds and links with non-system Tcl and Tk frameworks if they are installed in /Library/Frameworks as had been the case on older releases of macOS. If a macOS SDK is explicitly configured, by using ./configure --enable-universalsdk= or -isysroot, only a Library/Frameworks directory in the SDK itself is searched. The default behavior can still be overridden with configure --with-tcltk-includes and --with-tcltk-libs.
This commit is contained in:
parent
58205a0217
commit
1731d6da26
2 changed files with 116 additions and 43 deletions
|
@ -0,0 +1,6 @@
|
||||||
|
_tkinter now builds and links with non-system Tcl and Tk frameworks if they
|
||||||
|
are installed in /Library/Frameworks as had been the case on older releases
|
||||||
|
of macOS. If a macOS SDK is explicitly configured, by using ./configure
|
||||||
|
--enable-universalsdk= or -isysroot, only a Library/Frameworks directory in
|
||||||
|
the SDK itself is searched. The default behavior can still be overridden with
|
||||||
|
configure --with-tcltk-includes and --with-tcltk-libs.
|
153
setup.py
153
setup.py
|
@ -150,7 +150,9 @@ def sysroot_paths(make_vars, subdirs):
|
||||||
break
|
break
|
||||||
return dirs
|
return dirs
|
||||||
|
|
||||||
|
|
||||||
MACOS_SDK_ROOT = None
|
MACOS_SDK_ROOT = None
|
||||||
|
MACOS_SDK_SPECIFIED = None
|
||||||
|
|
||||||
def macosx_sdk_root():
|
def macosx_sdk_root():
|
||||||
"""Return the directory of the current macOS SDK.
|
"""Return the directory of the current macOS SDK.
|
||||||
|
@ -162,8 +164,9 @@ def macosx_sdk_root():
|
||||||
(The SDK may be supplied via Xcode or via the Command Line Tools).
|
(The SDK may be supplied via Xcode or via the Command Line Tools).
|
||||||
The SDK paths used by Apple-supplied tool chains depend on the
|
The SDK paths used by Apple-supplied tool chains depend on the
|
||||||
setting of various variables; see the xcrun man page for more info.
|
setting of various variables; see the xcrun man page for more info.
|
||||||
|
Also sets MACOS_SDK_SPECIFIED for use by macosx_sdk_specified().
|
||||||
"""
|
"""
|
||||||
global MACOS_SDK_ROOT
|
global MACOS_SDK_ROOT, MACOS_SDK_SPECIFIED
|
||||||
|
|
||||||
# If already called, return cached result.
|
# If already called, return cached result.
|
||||||
if MACOS_SDK_ROOT:
|
if MACOS_SDK_ROOT:
|
||||||
|
@ -173,8 +176,10 @@ def macosx_sdk_root():
|
||||||
m = re.search(r'-isysroot\s*(\S+)', cflags)
|
m = re.search(r'-isysroot\s*(\S+)', cflags)
|
||||||
if m is not None:
|
if m is not None:
|
||||||
MACOS_SDK_ROOT = m.group(1)
|
MACOS_SDK_ROOT = m.group(1)
|
||||||
|
MACOS_SDK_SPECIFIED = MACOS_SDK_ROOT != '/'
|
||||||
else:
|
else:
|
||||||
MACOS_SDK_ROOT = '/'
|
MACOS_SDK_ROOT = '/'
|
||||||
|
MACOS_SDK_SPECIFIED = False
|
||||||
cc = sysconfig.get_config_var('CC')
|
cc = sysconfig.get_config_var('CC')
|
||||||
tmpfile = '/tmp/setup_sdk_root.%d' % os.getpid()
|
tmpfile = '/tmp/setup_sdk_root.%d' % os.getpid()
|
||||||
try:
|
try:
|
||||||
|
@ -203,6 +208,28 @@ def macosx_sdk_root():
|
||||||
return MACOS_SDK_ROOT
|
return MACOS_SDK_ROOT
|
||||||
|
|
||||||
|
|
||||||
|
def macosx_sdk_specified():
|
||||||
|
"""Returns true if an SDK was explicitly configured.
|
||||||
|
|
||||||
|
True if an SDK was selected at configure time, either by specifying
|
||||||
|
--enable-universalsdk=(something other than no or /) or by adding a
|
||||||
|
-isysroot option to CFLAGS. In some cases, like when making
|
||||||
|
decisions about macOS Tk framework paths, we need to be able to
|
||||||
|
know whether the user explicitly asked to build with an SDK versus
|
||||||
|
the implicit use of an SDK when header files are no longer
|
||||||
|
installed on a running system by the Command Line Tools.
|
||||||
|
"""
|
||||||
|
global MACOS_SDK_SPECIFIED
|
||||||
|
|
||||||
|
# If already called, return cached result.
|
||||||
|
if MACOS_SDK_SPECIFIED:
|
||||||
|
return MACOS_SDK_SPECIFIED
|
||||||
|
|
||||||
|
# Find the sdk root and set MACOS_SDK_SPECIFIED
|
||||||
|
macosx_sdk_root()
|
||||||
|
return MACOS_SDK_SPECIFIED
|
||||||
|
|
||||||
|
|
||||||
def is_macosx_sdk_path(path):
|
def is_macosx_sdk_path(path):
|
||||||
"""
|
"""
|
||||||
Returns True if 'path' can be located in an OSX SDK
|
Returns True if 'path' can be located in an OSX SDK
|
||||||
|
@ -1830,31 +1857,73 @@ class PyBuildExt(build_ext):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def detect_tkinter_darwin(self):
|
def detect_tkinter_darwin(self):
|
||||||
# The _tkinter module, using frameworks. Since frameworks are quite
|
# Build default _tkinter on macOS using Tcl and Tk frameworks.
|
||||||
# different the UNIX search logic is not sharable.
|
#
|
||||||
|
# The macOS native Tk (AKA Aqua Tk) and Tcl are most commonly
|
||||||
|
# built and installed as macOS framework bundles. However,
|
||||||
|
# for several reasons, we cannot take full advantage of the
|
||||||
|
# Apple-supplied compiler chain's -framework options here.
|
||||||
|
# Instead, we need to find and pass to the compiler the
|
||||||
|
# absolute paths of the Tcl and Tk headers files we want to use
|
||||||
|
# and the absolute path to the directory containing the Tcl
|
||||||
|
# and Tk frameworks for linking.
|
||||||
|
#
|
||||||
|
# We want to handle here two common use cases on macOS:
|
||||||
|
# 1. Build and link with system-wide third-party or user-built
|
||||||
|
# Tcl and Tk frameworks installed in /Library/Frameworks.
|
||||||
|
# 2. Build and link using a user-specified macOS SDK so that the
|
||||||
|
# built Python can be exported to other systems. In this case,
|
||||||
|
# search only the SDK's /Library/Frameworks (normally empty)
|
||||||
|
# and /System/Library/Frameworks.
|
||||||
|
#
|
||||||
|
# Any other use case should be able to be handled explicitly by
|
||||||
|
# using the options described above in detect_tkinter_explicitly().
|
||||||
|
# In particular it would be good to handle here the case where
|
||||||
|
# you want to build and link with a framework build of Tcl and Tk
|
||||||
|
# that is not in /Library/Frameworks, say, in your private
|
||||||
|
# $HOME/Library/Frameworks directory or elsewhere. It turns
|
||||||
|
# out to be difficult to make that work automtically here
|
||||||
|
# without bringing into play more tools and magic. That case
|
||||||
|
# can be hamdled using a recipe with the right arguments
|
||||||
|
# to detect_tkinter_explicitly().
|
||||||
|
#
|
||||||
|
# Note also that the fallback case here is to try to use the
|
||||||
|
# Apple-supplied Tcl and Tk frameworks in /System/Library but
|
||||||
|
# be forewarned that they are deprecated by Apple and typically
|
||||||
|
# out-of-date and buggy; their use should be avoided if at
|
||||||
|
# all possible by installing a newer version of Tcl and Tk in
|
||||||
|
# /Library/Frameworks before bwfore building Python without
|
||||||
|
# an explicit SDK or by configuring build arguments explicitly.
|
||||||
|
|
||||||
from os.path import join, exists
|
from os.path import join, exists
|
||||||
framework_dirs = [
|
|
||||||
'/Library/Frameworks',
|
|
||||||
'/System/Library/Frameworks/',
|
|
||||||
join(os.getenv('HOME'), '/Library/Frameworks')
|
|
||||||
]
|
|
||||||
|
|
||||||
sysroot = macosx_sdk_root()
|
sysroot = macosx_sdk_root() # path to the SDK or '/'
|
||||||
|
|
||||||
# Find the directory that contains the Tcl.framework and Tk.framework
|
if macosx_sdk_specified():
|
||||||
# bundles.
|
# Use case #2: an SDK other than '/' was specified.
|
||||||
# XXX distutils should support -F!
|
# Only search there.
|
||||||
|
framework_dirs = [
|
||||||
|
join(sysroot, 'Library', 'Frameworks'),
|
||||||
|
join(sysroot, 'System', 'Library', 'Frameworks'),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
# Use case #1: no explicit SDK selected.
|
||||||
|
# Search the local system-wide /Library/Frameworks,
|
||||||
|
# not the one in the default SDK, othewise fall back to
|
||||||
|
# /System/Library/Frameworks whose header files may be in
|
||||||
|
# the default SDK or, on older systems, actually installed.
|
||||||
|
framework_dirs = [
|
||||||
|
join('/', 'Library', 'Frameworks'),
|
||||||
|
join(sysroot, 'System', 'Library', 'Frameworks'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Find the directory that contains the Tcl.framework and
|
||||||
|
# Tk.framework bundles.
|
||||||
for F in framework_dirs:
|
for F in framework_dirs:
|
||||||
# both Tcl.framework and Tk.framework should be present
|
# both Tcl.framework and Tk.framework should be present
|
||||||
|
|
||||||
|
|
||||||
for fw in 'Tcl', 'Tk':
|
for fw in 'Tcl', 'Tk':
|
||||||
if is_macosx_sdk_path(F):
|
if not exists(join(F, fw + '.framework')):
|
||||||
if not exists(join(sysroot, F[1:], fw + '.framework')):
|
break
|
||||||
break
|
|
||||||
else:
|
|
||||||
if not exists(join(F, fw + '.framework')):
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
# ok, F is now directory with both frameworks. Continure
|
# ok, F is now directory with both frameworks. Continure
|
||||||
# building
|
# building
|
||||||
|
@ -1864,24 +1933,16 @@ class PyBuildExt(build_ext):
|
||||||
# will now resume.
|
# will now resume.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# For 8.4a2, we must add -I options that point inside the Tcl and Tk
|
|
||||||
# frameworks. In later release we should hopefully be able to pass
|
|
||||||
# the -F option to gcc, which specifies a framework lookup path.
|
|
||||||
#
|
|
||||||
include_dirs = [
|
include_dirs = [
|
||||||
join(F, fw + '.framework', H)
|
join(F, fw + '.framework', H)
|
||||||
for fw in ('Tcl', 'Tk')
|
for fw in ('Tcl', 'Tk')
|
||||||
for H in ('Headers', 'Versions/Current/PrivateHeaders')
|
for H in ('Headers',)
|
||||||
]
|
]
|
||||||
|
|
||||||
# For 8.4a2, the X11 headers are not included. Rather than include a
|
# Add the base framework directory as well
|
||||||
# complicated search, this is a hard-coded path. It could bail out
|
compile_args = ['-F', F]
|
||||||
# if X11 libs are not found...
|
|
||||||
include_dirs.append('/usr/X11R6/include')
|
|
||||||
frameworks = ['-framework', 'Tcl', '-framework', 'Tk']
|
|
||||||
|
|
||||||
# All existing framework builds of Tcl/Tk don't support 64-bit
|
# Do not build tkinter for archs that this Tk was not built with.
|
||||||
# architectures.
|
|
||||||
cflags = sysconfig.get_config_vars('CFLAGS')[0]
|
cflags = sysconfig.get_config_vars('CFLAGS')[0]
|
||||||
archs = re.findall(r'-arch\s+(\w+)', cflags)
|
archs = re.findall(r'-arch\s+(\w+)', cflags)
|
||||||
|
|
||||||
|
@ -1889,13 +1950,9 @@ class PyBuildExt(build_ext):
|
||||||
if not os.path.exists(self.build_temp):
|
if not os.path.exists(self.build_temp):
|
||||||
os.makedirs(self.build_temp)
|
os.makedirs(self.build_temp)
|
||||||
|
|
||||||
# Note: cannot use os.popen or subprocess here, that
|
run_command(
|
||||||
# requires extensions that are not available here.
|
"file {}/Tk.framework/Tk | grep 'for architecture' > {}".format(F, tmpfile)
|
||||||
if is_macosx_sdk_path(F):
|
)
|
||||||
run_command("file %s/Tk.framework/Tk | grep 'for architecture' > %s"%(os.path.join(sysroot, F[1:]), tmpfile))
|
|
||||||
else:
|
|
||||||
run_command("file %s/Tk.framework/Tk | grep 'for architecture' > %s"%(F, tmpfile))
|
|
||||||
|
|
||||||
with open(tmpfile) as fp:
|
with open(tmpfile) as fp:
|
||||||
detected_archs = []
|
detected_archs = []
|
||||||
for ln in fp:
|
for ln in fp:
|
||||||
|
@ -1904,16 +1961,26 @@ class PyBuildExt(build_ext):
|
||||||
detected_archs.append(ln.split()[-1])
|
detected_archs.append(ln.split()[-1])
|
||||||
os.unlink(tmpfile)
|
os.unlink(tmpfile)
|
||||||
|
|
||||||
|
arch_args = []
|
||||||
for a in detected_archs:
|
for a in detected_archs:
|
||||||
frameworks.append('-arch')
|
arch_args.append('-arch')
|
||||||
frameworks.append(a)
|
arch_args.append(a)
|
||||||
|
|
||||||
|
compile_args += arch_args
|
||||||
|
link_args = [','.join(['-Wl', '-F', F, '-framework', 'Tcl', '-framework', 'Tk']), *arch_args]
|
||||||
|
|
||||||
|
# The X11/xlib.h file bundled in the Tk sources can cause function
|
||||||
|
# prototype warnings from the compiler. Since we cannot easily fix
|
||||||
|
# that, suppress the warnings here instead.
|
||||||
|
if '-Wstrict-prototypes' in cflags.split():
|
||||||
|
compile_args.append('-Wno-strict-prototypes')
|
||||||
|
|
||||||
self.add(Extension('_tkinter', ['_tkinter.c', 'tkappinit.c'],
|
self.add(Extension('_tkinter', ['_tkinter.c', 'tkappinit.c'],
|
||||||
define_macros=[('WITH_APPINIT', 1)],
|
define_macros=[('WITH_APPINIT', 1)],
|
||||||
include_dirs=include_dirs,
|
include_dirs=include_dirs,
|
||||||
libraries=[],
|
libraries=[],
|
||||||
extra_compile_args=frameworks[2:],
|
extra_compile_args=compile_args,
|
||||||
extra_link_args=frameworks))
|
extra_link_args=link_args))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def detect_tkinter(self):
|
def detect_tkinter(self):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue