From 8c8b74d2fee9fc22d7ffbf2d0bc435f9f64b391f Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 27 Nov 2021 10:25:32 +0100 Subject: [PATCH] bpo-45847: Port _tkinter to PY_STDLIB_MOD --- Makefile.pre.in | 6 - Modules/Setup.stdlib.in | 2 + Modules/_tkinter.c | 2 +- configure | 228 ++++++++++++++++++++++++------ configure.ac | 91 +++++++----- setup.py | 306 +--------------------------------------- 6 files changed, 252 insertions(+), 383 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 7b6f54a9ae0a7a..836dc114f459c7 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -302,10 +302,6 @@ _PYTHON_HOST_PLATFORM=@_PYTHON_HOST_PLATFORM@ BUILD_GNU_TYPE= @build@ HOST_GNU_TYPE= @host@ -# Tcl and Tk config info from --with-tcltk-includes and -libs options -TCLTK_INCLUDES= @TCLTK_INCLUDES@ -TCLTK_LIBS= @TCLTK_LIBS@ - # The task to run while instrumented when building the profile-opt target. # To speed up profile generation, we don't run the full unit test suite # by default. The default is "-m test --pgo". To run more tests, use @@ -737,10 +733,8 @@ sharedmods: $(BUILDPYTHON) pybuilddir.txt @LIBMPDEC_INTERNAL@ @LIBEXPAT_INTERNAL *) quiet="";; \ esac; \ echo "$(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' \ - _TCLTK_INCLUDES='$(TCLTK_INCLUDES)' _TCLTK_LIBS='$(TCLTK_LIBS)' \ $(PYTHON_FOR_BUILD) $(srcdir)/setup.py $$quiet build"; \ $(RUNSHARED) CC='$(CC)' LDSHARED='$(BLDSHARED)' OPT='$(OPT)' \ - _TCLTK_INCLUDES='$(TCLTK_INCLUDES)' _TCLTK_LIBS='$(TCLTK_LIBS)' \ $(PYTHON_FOR_BUILD) $(srcdir)/setup.py $$quiet build diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 73f041eb2fba9f..7e8976f811ab67 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -156,6 +156,8 @@ # Linux: -luuid, BSD/AIX: libc's uuid_create() @MODULE__UUID_TRUE@_uuid _uuidmodule.c +@MODULE__TKINTER_TRUE@_tkinter _tkinter.c tkappinit.c + ############################################################################ # macOS specific modules diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index cbef9ce0066db5..b5a78b869dba35 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -55,7 +55,7 @@ Copyright (C) 1994 Steen Lumholt. #include "tkinter.h" #if TK_HEX_VERSION < 0x08040200 -#error "Tk older than 8.4 not supported" +#error "Tk older than 8.4.2 not supported" #endif #if TK_HEX_VERSION >= 0x08050208 && TK_HEX_VERSION < 0x08060000 || \ diff --git a/configure b/configure index a4a876e7ea2eaf..c3d8d2b588af25 100755 --- a/configure +++ b/configure @@ -654,6 +654,8 @@ MODULE_BINASCII_FALSE MODULE_BINASCII_TRUE MODULE_ZLIB_FALSE MODULE_ZLIB_TRUE +MODULE__TKINTER_FALSE +MODULE__TKINTER_TRUE MODULE__UUID_FALSE MODULE__UUID_TRUE MODULE__SQLITE3_FALSE @@ -818,7 +820,7 @@ DTRACE GDBM_LIBS GDBM_CFLAGS TCLTK_LIBS -TCLTK_INCLUDES +TCLTK_CFLAGS LIBSQLITE3_LIBS LIBSQLITE3_CFLAGS LIBNSL_LIBS @@ -1028,8 +1030,6 @@ with_system_ffi with_system_libmpdec with_decimal_contextvar enable_loadable_sqlite_extensions -with_tcltk_includes -with_tcltk_libs with_dbmliborder enable_ipv6 with_doc_strings @@ -1073,6 +1073,8 @@ LIBNSL_CFLAGS LIBNSL_LIBS LIBSQLITE3_CFLAGS LIBSQLITE3_LIBS +TCLTK_CFLAGS +TCLTK_LIBS GDBM_CFLAGS GDBM_LIBS ZLIB_CFLAGS @@ -1795,10 +1797,6 @@ Optional Packages: --with-decimal-contextvar build _decimal module using a coroutine-local rather than a thread-local context (default is yes) - --with-tcltk-includes='-I...' - override search for Tcl and Tk include files - --with-tcltk-libs='-L...' - override search for Tcl and Tk libs --with-dbmliborder=db1:db2:... override order to check db backends for dbm; a valid value is a colon separated string with the backend @@ -1874,6 +1872,9 @@ Some influential environment variables: C compiler flags for LIBSQLITE3, overriding pkg-config LIBSQLITE3_LIBS linker flags for LIBSQLITE3, overriding pkg-config + TCLTK_CFLAGS + C compiler flags for TCLTK, overriding pkg-config + TCLTK_LIBS linker flags for TCLTK, overriding pkg-config GDBM_CFLAGS C compiler flags for gdbm GDBM_LIBS additional linker flags for gdbm ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config @@ -12275,50 +12276,157 @@ $as_echo "#define PY_SQLITE_ENABLE_LOAD_EXTENSION 1" >>confdefs.h fi -# Check for --with-tcltk-includes=path and --with-tcltk-libs=path - +for _QUERY in \ + "tcl >= 8.4.2 tk >= 8.4.2" \ + "tcl8.6 tk8.6" \ + "tcl86 tk86" \ + "tcl8.5 tk8.5" \ + "tcl85 tk85" \ + "tcl8.4 >= 8.4.2 tk8.4 >= 8.4.2" \ + "tcl84 >= 8.4.2 tk84 >= 8.4.2" \ +; do -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-tcltk-includes" >&5 -$as_echo_n "checking for --with-tcltk-includes... " >&6; } +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TCLTK" >&5 +$as_echo_n "checking for TCLTK... " >&6; } -# Check whether --with-tcltk-includes was given. -if test "${with_tcltk_includes+set}" = set; then : - withval=$with_tcltk_includes; +if test -n "$TCLTK_CFLAGS"; then + pkg_cv_TCLTK_CFLAGS="$TCLTK_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$_QUERY\""; } >&5 + ($PKG_CONFIG --exists --print-errors "$_QUERY") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_TCLTK_CFLAGS=`$PKG_CONFIG --cflags "$_QUERY" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes else - with_tcltk_includes="default" + pkg_failed=yes fi + else + pkg_failed=untried +fi +if test -n "$TCLTK_LIBS"; then + pkg_cv_TCLTK_LIBS="$TCLTK_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$_QUERY\""; } >&5 + ($PKG_CONFIG --exists --print-errors "$_QUERY") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_TCLTK_LIBS=`$PKG_CONFIG --libs "$_QUERY" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_tcltk_includes" >&5 -$as_echo "$with_tcltk_includes" >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-tcltk-libs" >&5 -$as_echo_n "checking for --with-tcltk-libs... " >&6; } -# Check whether --with-tcltk-libs was given. -if test "${with_tcltk_libs+set}" = set; then : - withval=$with_tcltk_libs; +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes else - with_tcltk_libs="default" + _pkg_short_errors_supported=no fi + if test $_pkg_short_errors_supported = yes; then + TCLTK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$_QUERY" 2>&1` + else + TCLTK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$_QUERY" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$TCLTK_PKG_ERRORS" >&5 -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_tcltk_libs" >&5 -$as_echo "$with_tcltk_libs" >&6; } -if test "x$with_tcltk_includes" = xdefault || test "x$with_tcltk_libs" = xdefault -then - if test "x$with_tcltk_includes" != "x$with_tcltk_libs" - then - as_fn_error $? "use both --with-tcltk-includes='...' and --with-tcltk-libs='...' or neither" "$LINENO" 5 - fi - if test -n "$PKG_CONFIG" && "$PKG_CONFIG" --exists tcl tk; then - TCLTK_INCLUDES="`"$PKG_CONFIG" tcl tk --cflags-only-I 2>/dev/null`" - TCLTK_LIBS="`"$PKG_CONFIG" tcl tk --libs 2>/dev/null`" - else - TCLTK_INCLUDES="" - TCLTK_LIBS="" - fi + found_tcltk=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + found_tcltk=no else - TCLTK_INCLUDES="$with_tcltk_includes" - TCLTK_LIBS="$with_tcltk_libs" + TCLTK_CFLAGS=$pkg_cv_TCLTK_CFLAGS + TCLTK_LIBS=$pkg_cv_TCLTK_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break fi +done + +if test "x$found_tcltk" = xno; then : + + TCLTK_CFLAGS=${TCLTK_CFLAGS-""} + TCLTK_LIBS=${TCLTK_LIBS-""} + +fi + +save_CFLAGS=$CFLAGS +save_CPPFLAGS=$CPPFLAGS +save_LDFLAGS=$LDFLAGS +save_LIBS=$LIBS + + + CPPFLAGS="$TCLTK_CFLAGS $CFLAGS" + LDFLAGS="$TCLTK_LIBS $LDFLAGS" + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + #include + #include + #if defined(TK_HEX_VERSION) + # if TK_HEX_VERSION < 0x08040200 + # error "Tk older than 8.4.2 not supported" + # endif + #endif + #if (TCL_MAJOR_VERSION < 8) || \ + ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION < 4)) || \ + ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 4) && (TCL_RELEASE_SERIAL < 2)) + # error "Tcl older than 8.4.2 not supported" + #endif + #if (TK_MAJOR_VERSION < 8) || \ + ((TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION < 4)) || \ + ((TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION == 4) && (TK_RELEASE_SERIAL < 2)) + # error "Tk older than 8.4.2 not supported" + #endif + +int +main () +{ + + void *x1 = Tcl_Init; + void *x2 = Tk_Init; + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + have_tcltk=yes + as_fn_append TCLTK_CFLAGS " -Wno-strict-prototypes -DWITH_APPINIT=1" + +else + + have_tcltk=no + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +CFLAGS=$save_CFLAGS +CPPFLAGS=$save_CPPFLAGS +LDFLAGS=$save_LDFLAGS +LIBS=$save_LIBS + + @@ -23097,6 +23205,42 @@ fi $as_echo "$py_cv_module__uuid" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _tkinter" >&5 +$as_echo_n "checking for stdlib extension module _tkinter... " >&6; } + case $py_stdlib_not_available in #( + *_tkinter*) : + py_cv_module__tkinter=n/a ;; #( + *) : + if true; then : + if test "$have_tcltk" = "yes"; then : + py_cv_module__tkinter=yes +else + py_cv_module__tkinter=missing +fi +else + py_cv_module__tkinter=disabled +fi + ;; +esac + as_fn_append MODULE_BLOCK "MODULE__TKINTER=$py_cv_module__tkinter$as_nl" + if test "x$py_cv_module__tkinter" = xyes; then : + + as_fn_append MODULE_BLOCK "MODULE__TKINTER_CFLAGS=$TCLTK_CFLAGS$as_nl" + as_fn_append MODULE_BLOCK "MODULE__TKINTER_LDFLAGS=$TCLTK_LIBS$as_nl" + +fi + if test "$py_cv_module__tkinter" = yes; then + MODULE__TKINTER_TRUE= + MODULE__TKINTER_FALSE='#' +else + MODULE__TKINTER_TRUE='#' + MODULE__TKINTER_FALSE= +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__tkinter" >&5 +$as_echo "$py_cv_module__tkinter" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module zlib" >&5 $as_echo_n "checking for stdlib extension module zlib... " >&6; } @@ -23985,6 +24129,10 @@ if test -z "${MODULE__UUID_TRUE}" && test -z "${MODULE__UUID_FALSE}"; then as_fn_error $? "conditional \"MODULE__UUID\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${MODULE__TKINTER_TRUE}" && test -z "${MODULE__TKINTER_FALSE}"; then + as_fn_error $? "conditional \"MODULE__TKINTER\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${MODULE_ZLIB_TRUE}" && test -z "${MODULE_ZLIB_FALSE}"; then as_fn_error $? "conditional \"MODULE_ZLIB\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/configure.ac b/configure.ac index c8720cc8b65791..2acb2f6c71ffc9 100644 --- a/configure.ac +++ b/configure.ac @@ -3499,38 +3499,63 @@ AS_VAR_IF([enable_loadable_sqlite_extensions], [yes], [ [Define to 1 to build the sqlite module with loadable extensions support.]) ]) -# Check for --with-tcltk-includes=path and --with-tcltk-libs=path -AC_SUBST(TCLTK_INCLUDES) -AC_SUBST(TCLTK_LIBS) -AC_MSG_CHECKING(for --with-tcltk-includes) -AC_ARG_WITH(tcltk-includes, - AS_HELP_STRING([--with-tcltk-includes='-I...'], [override search for Tcl and Tk include files]), - [], - [with_tcltk_includes="default"]) -AC_MSG_RESULT($with_tcltk_includes) -AC_MSG_CHECKING(for --with-tcltk-libs) -AC_ARG_WITH(tcltk-libs, - AS_HELP_STRING([--with-tcltk-libs='-L...'], [override search for Tcl and Tk libs]), - [], - [with_tcltk_libs="default"]) -AC_MSG_RESULT($with_tcltk_libs) -if test "x$with_tcltk_includes" = xdefault || test "x$with_tcltk_libs" = xdefault -then - if test "x$with_tcltk_includes" != "x$with_tcltk_libs" - then - AC_MSG_ERROR([use both --with-tcltk-includes='...' and --with-tcltk-libs='...' or neither]) - fi - if test -n "$PKG_CONFIG" && "$PKG_CONFIG" --exists tcl tk; then - TCLTK_INCLUDES="`"$PKG_CONFIG" tcl tk --cflags-only-I 2>/dev/null`" - TCLTK_LIBS="`"$PKG_CONFIG" tcl tk --libs 2>/dev/null`" - else - TCLTK_INCLUDES="" - TCLTK_LIBS="" - fi -else - TCLTK_INCLUDES="$with_tcltk_includes" - TCLTK_LIBS="$with_tcltk_libs" -fi +dnl +dnl Detect Tcl/Tk. Use pkg-config if available. +dnl +for _QUERY in \ + "tcl >= 8.4.2 tk >= 8.4.2" \ + "tcl8.6 tk8.6" \ + "tcl86 tk86" \ + "tcl8.5 tk8.5" \ + "tcl85 tk85" \ + "tcl8.4 >= 8.4.2 tk8.4 >= 8.4.2" \ + "tcl84 >= 8.4.2 tk84 >= 8.4.2" \ +; do + PKG_CHECK_MODULES([TCLTK], [$_QUERY], [break], [found_tcltk=no]) +done + +AS_VAR_IF([found_tcltk], [no], [ + TCLTK_CFLAGS=${TCLTK_CFLAGS-""} + TCLTK_LIBS=${TCLTK_LIBS-""} +]) + +WITH_SAVE_ENV([ + CPPFLAGS="$TCLTK_CFLAGS $CFLAGS" + LDFLAGS="$TCLTK_LIBS $LDFLAGS" + + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ + #include + #include + #if defined(TK_HEX_VERSION) + # if TK_HEX_VERSION < 0x08040200 + # error "Tk older than 8.4.2 not supported" + # endif + #endif + #if (TCL_MAJOR_VERSION < 8) || \ + ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION < 4)) || \ + ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 4) && (TCL_RELEASE_SERIAL < 2)) + # error "Tcl older than 8.4.2 not supported" + #endif + #if (TK_MAJOR_VERSION < 8) || \ + ((TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION < 4)) || \ + ((TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION == 4) && (TK_RELEASE_SERIAL < 2)) + # error "Tk older than 8.4.2 not supported" + #endif + ], [ + void *x1 = Tcl_Init; + void *x2 = Tk_Init; + ]) + ], [ + have_tcltk=yes + dnl The X11/xlib.h file bundled in the Tk sources can cause function + dnl prototype warnings from the compiler. Since we cannot easily fix + dnl that, suppress the warnings here instead. + AS_VAR_APPEND([TCLTK_CFLAGS], [" -Wno-strict-prototypes -DWITH_APPINIT=1"]) + ], [ + have_tcltk=no + ]) +]) dnl check for _gdbmmodule dependencies dnl NOTE: gdbm does not provide a pkgconf file. @@ -6596,6 +6621,8 @@ dnl PY_STDLIB_MOD([_tkinter], [], [], [], []) PY_STDLIB_MOD([_uuid], [], [test "$have_uuid" = "yes"], [$LIBUUID_CFLAGS], [$LIBUUID_LIBS]) +PY_STDLIB_MOD([_tkinter], [], + [test "$have_tcltk" = "yes"], [$TCLTK_CFLAGS], [$TCLTK_LIBS]) dnl compression libs PY_STDLIB_MOD([zlib], [], [test "$have_zlib" = yes], diff --git a/setup.py b/setup.py index e47b2ab90958b9..c5eb25248058f6 100644 --- a/setup.py +++ b/setup.py @@ -1428,8 +1428,7 @@ def detect_modules(self): self.detect_decimal() self.detect_ctypes() self.detect_multiprocessing() - if not self.detect_tkinter(): - self.missing.append('_tkinter') + self.detect_tkinter() self.detect_uuid() # Uncomment the next line if you want to play with xxmodule.c @@ -1438,309 +1437,8 @@ def detect_modules(self): self.addext(Extension('xxlimited', ['xxlimited.c'])) self.addext(Extension('xxlimited_35', ['xxlimited_35.c'])) - def detect_tkinter_fromenv(self): - # Build _tkinter using the Tcl/Tk locations specified by - # the _TCLTK_INCLUDES and _TCLTK_LIBS environment variables. - # This method is meant to be invoked by detect_tkinter(). - # - # The variables can be set via one of the following ways. - # - # - Automatically, at configuration time, by using pkg-config. - # The tool is called by the configure script. - # Additional pkg-config configuration paths can be set via the - # PKG_CONFIG_PATH environment variable. - # - # PKG_CONFIG_PATH=".../lib/pkgconfig" ./configure ... - # - # - Explicitly, at configuration time by setting both - # --with-tcltk-includes and --with-tcltk-libs. - # - # ./configure ... \ - # --with-tcltk-includes="-I/path/to/tclincludes \ - # -I/path/to/tkincludes" - # --with-tcltk-libs="-L/path/to/tcllibs -ltclm.n \ - # -L/path/to/tklibs -ltkm.n" - # - # - Explicitly, at compile time, by passing TCLTK_INCLUDES and - # TCLTK_LIBS to the make target. - # This will override any configuration-time option. - # - # make TCLTK_INCLUDES="..." TCLTK_LIBS="..." - # - # This can be useful for building and testing tkinter with multiple - # versions of Tcl/Tk. Note that a build of Tk depends on a particular - # build of Tcl so you need to specify both arguments and use care when - # overriding. - - # The _TCLTK variables are created in the Makefile sharedmods target. - tcltk_includes = os.environ.get('_TCLTK_INCLUDES') - tcltk_libs = os.environ.get('_TCLTK_LIBS') - if not (tcltk_includes and tcltk_libs): - # Resume default configuration search. - return False - - extra_compile_args = tcltk_includes.split() - extra_link_args = tcltk_libs.split() - self.add(Extension('_tkinter', ['_tkinter.c', 'tkappinit.c'], - define_macros=[('WITH_APPINIT', 1)], - extra_compile_args = extra_compile_args, - extra_link_args = extra_link_args)) - return True - - def detect_tkinter_darwin(self): - # Build default _tkinter on macOS using Tcl and Tk frameworks. - # This method is meant to be invoked by detect_tkinter(). - # - # 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 cases are handled either by detect_tkinter_fromenv(), - # or detect_tkinter(). The former handles non-standard locations of - # Tcl/Tk, defined via the _TCLTK_INCLUDES and _TCLTK_LIBS environment - # variables. The latter handles any Tcl/Tk versions installed in - # standard Unix directories. - # - # It would be desirable to also 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 automatically here - # without bringing into play more tools and magic. That case - # can be handled using a recipe with the right arguments - # to detect_tkinter_fromenv(). - # - # 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 building Python without - # an explicit SDK or by configuring build arguments explicitly. - - from os.path import join, exists - - sysroot = macosx_sdk_root() # path to the SDK or '/' - - if macosx_sdk_specified(): - # Use case #2: an SDK other than '/' was specified. - # 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, otherwise 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: - # both Tcl.framework and Tk.framework should be present - for fw in 'Tcl', 'Tk': - if not exists(join(F, fw + '.framework')): - break - else: - # ok, F is now directory with both frameworks. Continue - # building - break - else: - # Tk and Tcl frameworks not found. Normal "unix" tkinter search - # will now resume. - return False - - include_dirs = [ - join(F, fw + '.framework', H) - for fw in ('Tcl', 'Tk') - for H in ('Headers',) - ] - - # Add the base framework directory as well - compile_args = ['-F', F] - - # Do not build tkinter for archs that this Tk was not built with. - cflags = sysconfig.get_config_vars('CFLAGS')[0] - archs = re.findall(r'-arch\s+(\w+)', cflags) - - tmpfile = os.path.join(self.build_temp, 'tk.arch') - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) - - run_command( - "file {}/Tk.framework/Tk | grep 'for architecture' > {}".format(F, tmpfile) - ) - with open(tmpfile) as fp: - detected_archs = [] - for ln in fp: - a = ln.split()[-1] - if a in archs: - detected_archs.append(ln.split()[-1]) - os.unlink(tmpfile) - - arch_args = [] - for a in detected_archs: - arch_args.append('-arch') - 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'], - define_macros=[('WITH_APPINIT', 1)], - include_dirs=include_dirs, - libraries=[], - extra_compile_args=compile_args, - extra_link_args=link_args)) - return True - def detect_tkinter(self): - # The _tkinter module. - # - # Detection of Tcl/Tk is attempted in the following order: - # - Through environment variables. - # - Platform specific detection of Tcl/Tk (currently only macOS). - # - Search of various standard Unix header/library paths. - # - # Detection stops at the first successful method. - - # Check for Tcl and Tk at the locations indicated by _TCLTK_INCLUDES - # and _TCLTK_LIBS environment variables. - if self.detect_tkinter_fromenv(): - return True - - # Rather than complicate the code below, detecting and building - # AquaTk is a separate method. Only one Tkinter will be built on - # Darwin - either AquaTk, if it is found, or X11 based Tk. - if (MACOS and self.detect_tkinter_darwin()): - return True - - # Assume we haven't found any of the libraries or include files - # The versions with dots are used on Unix, and the versions without - # dots on Windows, for detection by cygwin. - tcllib = tklib = tcl_includes = tk_includes = None - for version in ['8.6', '86', '8.5', '85', '8.4', '84', '8.3', '83', - '8.2', '82', '8.1', '81', '8.0', '80']: - tklib = self.compiler.find_library_file(self.lib_dirs, - 'tk' + version) - tcllib = self.compiler.find_library_file(self.lib_dirs, - 'tcl' + version) - if tklib and tcllib: - # Exit the loop when we've found the Tcl/Tk libraries - break - - # Now check for the header files - if tklib and tcllib: - # Check for the include files on Debian and {Free,Open}BSD, where - # they're put in /usr/include/{tcl,tk}X.Y - dotversion = version - if '.' not in dotversion and "bsd" in HOST_PLATFORM.lower(): - # OpenBSD and FreeBSD use Tcl/Tk library names like libtcl83.a, - # but the include subdirs are named like .../include/tcl8.3. - dotversion = dotversion[:-1] + '.' + dotversion[-1] - tcl_include_sub = [] - tk_include_sub = [] - for dir in self.inc_dirs: - tcl_include_sub += [dir + os.sep + "tcl" + dotversion] - tk_include_sub += [dir + os.sep + "tk" + dotversion] - tk_include_sub += tcl_include_sub - tcl_includes = find_file('tcl.h', self.inc_dirs, tcl_include_sub) - tk_includes = find_file('tk.h', self.inc_dirs, tk_include_sub) - - if (tcllib is None or tklib is None or - tcl_includes is None or tk_includes is None): - self.announce("INFO: Can't locate Tcl/Tk libs and/or headers", 2) - return False - - # OK... everything seems to be present for Tcl/Tk. - - include_dirs = [] - libs = [] - defs = [] - added_lib_dirs = [] - for dir in tcl_includes + tk_includes: - if dir not in include_dirs: - include_dirs.append(dir) - - # Check for various platform-specific directories - if HOST_PLATFORM == 'sunos5': - include_dirs.append('/usr/openwin/include') - added_lib_dirs.append('/usr/openwin/lib') - elif os.path.exists('/usr/X11R6/include'): - include_dirs.append('/usr/X11R6/include') - added_lib_dirs.append('/usr/X11R6/lib64') - added_lib_dirs.append('/usr/X11R6/lib') - elif os.path.exists('/usr/X11R5/include'): - include_dirs.append('/usr/X11R5/include') - added_lib_dirs.append('/usr/X11R5/lib') - else: - # Assume default location for X11 - include_dirs.append('/usr/X11/include') - added_lib_dirs.append('/usr/X11/lib') - - # If Cygwin, then verify that X is installed before proceeding - if CYGWIN: - x11_inc = find_file('X11/Xlib.h', [], include_dirs) - if x11_inc is None: - return False - - # Check for BLT extension - if self.compiler.find_library_file(self.lib_dirs + added_lib_dirs, - 'BLT8.0'): - defs.append( ('WITH_BLT', 1) ) - libs.append('BLT8.0') - elif self.compiler.find_library_file(self.lib_dirs + added_lib_dirs, - 'BLT'): - defs.append( ('WITH_BLT', 1) ) - libs.append('BLT') - - # Add the Tcl/Tk libraries - libs.append('tk'+ version) - libs.append('tcl'+ version) - - # Finally, link with the X11 libraries (not appropriate on cygwin) - if not CYGWIN: - libs.append('X11') - - # XXX handle these, but how to detect? - # *** Uncomment and edit for PIL (TkImaging) extension only: - # -DWITH_PIL -I../Extensions/Imaging/libImaging tkImaging.c \ - # *** Uncomment and edit for TOGL extension only: - # -DWITH_TOGL togl.c \ - # *** Uncomment these for TOGL extension only: - # -lGL -lGLU -lXext -lXmu \ - - self.add(Extension('_tkinter', ['_tkinter.c', 'tkappinit.c'], - define_macros=[('WITH_APPINIT', 1)] + defs, - include_dirs=include_dirs, - libraries=libs, - library_dirs=added_lib_dirs)) - return True + self.addext(Extension('_tkinter', ['_tkinter.c', 'tkappinit.c'])) def configure_ctypes(self, ext): return True