Repository: spack/spack

Issue:
Run-time issue: python+tkinter
The tkagg backend does not work as expected, see below with a fresh Spack at 319f20d94ff68f781b4913d236e0587dfa0195ba.

```console
$ spack install py-matplotlib backend=tkagg
$ spack load -r py-matplotlib
$ python -c "import matplotlib.pyplot as plt; plt.plot([0],[1]); plt.show()"
...
_tkinter.TclError: Can't find a usable tk.tcl in the following directories: 
    /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tk-8.6.10-egp4nzel66d2273p3olurpjif6bhhkmj/lib /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tcl-8.6.11-uoh3u6zmjwbky6rhfojdihz6blnk5sy5/lib/tcl8.6/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tcl-8.6.11-uoh3u6zmjwbky6rhfojdihz6blnk5sy5/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/library
```

The variants `tk` and `tcl` itself were installed:

```console
$ spack find --loaded -v | grep -e tcl -e tk   
py-matplotlib@3.4.2~animation~fonts+image~latex~movies backend=tkagg
python@3.8.10+bz2+ctypes+dbm~debug+libxml2+lzma~nis~optimizations+pic+pyexpat+pythoncmd+readline+shared+sqlite3+ssl~tix+tkinter~ucs4+uuid+zlib patches=0d98e93189bc278fbc37a50ed7f183bd8aaf249a8e1670a465f0db6bb4f8cf87
tcl@8.6.11
tk@8.6.10+xft+xss
``` 

### General information

* **Spack:** 0.16.1-2700-319f20d94f
* **Python:** 3.8.5
* **Platform:** linux-ubuntu20.04-haswell
* **Concretizer:** original


Discussion:
I'm unable to reproduce this on macOS, tkagg seems to work just fine.

Can you try the following test to see if this actually involves matplotlib?
```console
$ python -c 'import tkinter; import _tkinter'
```
Can you also try:
```console
$ cd /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tk-8.6.10-egp4nzel66d2273p3olurpjif6bhhkmj
$ find . -name tk.tcl
```
For me, it's in `lib/tk8.6/tk.tcl`.
Can you follow the debugging steps listed at https://wiki.python.org/moin/TkInter? One possibility is that you are using SSH and `DISPLAY` is not set (requires `ssh -Y`).
I'm not using SSH and `DISPLAY` is set:

```
echo $DISPLAY
:0
```

Imports work but the test fails:

```
Python 3.8.10 (default, May 18 2021, 20:19:06) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import _tkinter
>>> import tkinter
>>> tkinter._test() 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/python3.8/tkinter/__init__.py", line 4557, in _test
    root = Tk()
  File "/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/python3.8/tkinter/__init__.py", line 2270, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: Can't find a usable tk.tcl in the following directories: 
    /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tk-8.6.10-egp4nzel66d2273p3olurpjif6bhhkmj/lib /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tcl-8.6.11-uoh3u6zmjwbky6rhfojdihz6blnk5sy5/lib/tcl8.6/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tcl-8.6.11-uoh3u6zmjwbky6rhfojdihz6blnk5sy5/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/library



This probably means that tk wasn't installed properly.
```

```
for d in /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tk-8.6.10-egp4nzel66d2273p3olurpjif6bhhkmj/lib /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tcl-8.6.11-uoh3u6zmjwbky6rhfojdihz6blnk5sy5/lib/tcl8.6/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tcl-8.6.11-uoh3u6zmjwbky6rhfojdihz6blnk5sy5/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/lib/tk8.6 /home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/library; do ls $d/tk.tcl; done
ls: cannot access '/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tk-8.6.10-egp4nzel66d2273p3olurpjif6bhhkmj/lib/tk.tcl': No such file or directory
ls: cannot access '/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tcl-8.6.11-uoh3u6zmjwbky6rhfojdihz6blnk5sy5/lib/tcl8.6/tk8.6/tk.tcl': No such file or directory
ls: cannot access '/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tcl-8.6.11-uoh3u6zmjwbky6rhfojdihz6blnk5sy5/lib/tk8.6/tk.tcl': No such file or directory
ls: cannot access '/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/tk8.6/tk.tcl': No such file or directory
ls: cannot access '/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/lib/tk8.6/tk.tcl': No such file or directory
ls: cannot access '/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/lib/tk8.6/tk.tcl': No such file or directory
ls: cannot access '/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/python-3.8.10-efzidqekqhq4y4raa2q4iabetuoqdnse/library/tk.tcl': No such file or directory
```

However, I do have a `tk.tcl` in:

```
/home/sschmitt/spack-tcl/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tk-8.6.10-egp4nzel66d2273p3olurpjif6bhhkmj/lib/tk8.6/tk.tcl
```
Which has `tk8.6` at the end more compared to the search path reported above. When I symlink the contents of `tk-8.6.10-egp4nzel66d2273p3olurpjif6bhhkmj/lib/tk8.6` one level down (to `tk-8.6.10-egp4nzel66d2273p3olurpjif6bhhkmj/lib`), things seems to work.

Alright, I am actually seeing a possibly related bug on macOS:
```python
>>> tkinter._test()
DEPRECATION WARNING: The system version of Tk is deprecated and may be removed in a future release. Please don't rely on it. Set TK_SILENCE_DEPRECATION=1 to suppress this warning.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-oteamoaswrgdgdw6jhdrn4n7acir5c73/lib/python3.8/tkinter/__init__.py", line 4557, in _test
    root = Tk()
  File "/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-oteamoaswrgdgdw6jhdrn4n7acir5c73/lib/python3.8/tkinter/__init__.py", line 2272, in __init__
    self._loadtk()
  File "/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-oteamoaswrgdgdw6jhdrn4n7acir5c73/lib/python3.8/tkinter/__init__.py", line 2288, in _loadtk
    raise RuntimeError("tk.h version (%s) doesn't match libtk.a version (%s)"
RuntimeError: tk.h version (8.6) doesn't match libtk.a version (8.5)
```
So it's picking up something, just the wrong one. I'll keep investigating.
The Python build log actually points out this issue:
```
*** WARNING: renaming "_tkinter" since importing it failed: dlopen(/private/var/folders/21/hwq39zyj4g36x6zjfyl5l8080000gn/T/Adam/spack-stage/spack-stage-python-3.8.10-oteamoaswrgdgdw6jhdrn4n7acir5c73/spack-src/spack-build/build/lib.macosx-10.15.7-x86_64-3.8/_tkinter.cpython-38-darwin.so, 2): Library not loaded: /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.10-sn76ddqkq3sircg4zaiphqc6j2sthcvj/lib:/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib:/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/libx11-1.7.0-vcsch4cw5xcbjepmtfhkto24me356zj5/lib/libtk8.6.dylib
  Referenced from: /private/var/folders/21/hwq39zyj4g36x6zjfyl5l8080000gn/T/Adam/spack-stage/spack-stage-python-3.8.10-oteamoaswrgdgdw6jhdrn4n7acir5c73/spack-src/spack-build/build/lib.macosx-10.15.7-x86_64-3.8/_tkinter.cpython-38-darwin.so
  Reason: image not found

Python build finished successfully!
The necessary bits to build these optional modules were not found:
ossaudiodev           spwd
To find the necessary bits, look in setup.py in detect_modules() for the module's name.


The following modules found by detect_modules() in setup.py, have been
built by the Makefile instead, as configured by the Setup files:
_abc                  atexit                pwd
time


Following modules built successfully but were removed because they could not be imported:
_tkinter
```
This can be seen on the command line too:
```console
$ otool -L lib/python3.8/lib-dynload/_tkinter.cpython-38-darwin.so
lib/python3.8/lib-dynload/_tkinter.cpython-38-darwin.so:
	/System/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl (compatibility version 8.5.0, current version 8.5.9)
	/System/Library/Frameworks/Tk.framework/Versions/8.5/Tk (compatibility version 8.5.0, current version 8.5.9)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
$ otool -L lib/python3.8/lib-dynload/_tkinter.cpython-38-darwin_failed.so 
lib/python3.8/lib-dynload/_tkinter.cpython-38-darwin_failed.so:
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib/libtcl8.6.dylib (compatibility version 8.6.0, current version 8.6.11)
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.10-sn76ddqkq3sircg4zaiphqc6j2sthcvj/lib:/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib:/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/libx11-1.7.0-vcsch4cw5xcbjepmtfhkto24me356zj5/lib/libtk8.6.dylib (compatibility version 8.6.0, current version 8.6.10)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
```
Possibly a bug in the tk build?
```console
$ otool -L /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.10-sn76ddqkq3sircg4zaiphqc6j2sthcvj/lib/libtk8.6.dylib 
/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.10-sn76ddqkq3sircg4zaiphqc6j2sthcvj/lib/libtk8.6.dylib:
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.10-sn76ddqkq3sircg4zaiphqc6j2sthcvj/lib:/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib:/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/libx11-1.7.0-vcsch4cw5xcbjepmtfhkto24me356zj5/lib/libtk8.6.dylib (compatibility version 8.6.0, current version 8.6.10)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1677.104.0)
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/libx11-1.7.0-vcsch4cw5xcbjepmtfhkto24me356zj5/lib/libX11.6.dylib (compatibility version 11.0.0, current version 11.0.0)
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/libxscrnsaver-1.2.2-pmikcsrah6qmsqa5ydwqse245byho3rb/lib/libXss.1.dylib (compatibility version 2.0.0, current version 2.0.0, weak)
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/libxext-1.3.3-dpw3fi3s423crfns3ss37cbv66ledu2g/lib/libXext.6.dylib (compatibility version 11.0.0, current version 11.0.0)
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/zlib-1.2.11-x2anksgssxsxa7pcnhzg5k3dhgacglze/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
```
I wonder if this is because `tk` and `tcl` aren't designed to be installed to different directories. During installation, the `tk` configure script issues the following warning:
```
configure: WARNING:
        Different --prefix selected for Tk and Tcl!
        [package require Tk] may not work correctly in tclsh.
```
Most package managers seem to have a single package that combines both `tcl` and `tk` and installs them to the same prefix:

* [Homebrew](https://github.com/Homebrew/homebrew-core/blob/master/Formula/tcl-tk.rb)
* [Macports](https://github.com/macports/macports-ports/blob/master/x11/tk/Portfile)
* [Conda](https://github.com/conda-forge/tk-feedstock/blob/master/recipe/meta.yaml)
Okay, thanks to a patch from Macports, I've fixed the bug I encountered on macOS. Now I'm getting the same bug as you were getting. Still digging to solve that one, but I might end up symlinking things like you suggest.
Okay, I think I'm starting to lose my mind. If I install `tk` multiple times, sometimes it installs `lib/tk8.6/tk.tcl`, sometimes it installs `lib/tk.tcl`, and sometimes it doesn't install `tk.tcl` at all. Maybe it's a parallel build bug. Testing now.
@schmitts I'm not sure how else to debug this problem. Can you try uninstalling and reinstalling `tk` a few times on your system to see where it installs `tk.tcl`? With #23825 my installation now consistently succeeds, but that is only a patch for macOS so won't help you.
I've done

```
spack uninstall  --dependents --all tcl tk
spack install tk
```

a couple of times. `tk.tcl` always ends like below:

```
sschmitt@sierra ➜  ~/spack-tcl git:(develop) find -name tk.tcl
./opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tk-8.6.10-hioelpdlpihvlnph5nzdml3whkivz3co/share/tk/src/library/tk.tcl
./opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/tk-8.6.10-hioelpdlpihvlnph5nzdml3whkivz3co/lib/tk8.6/tk.tcl
```
Opened up an issue with the `tk` developers, let's see what they say: https://core.tcl-lang.org/tk/tktview/447bd3e4abe17452d19a80e6840dcc8a2603fcbc

Alternatively, we can open an issue with the Python developers and get tkinter to search in `<prefix>/lib/tk8.6/tk.tcl`.
According to the tk developers, `<prefix>/lib/tk8.6/tk.tcl` is indeed the correct installation location. I opened an issue with the Python developers to see if we can get Python to look in this directory: https://bugs.python.org/issue44253

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/tcl/package.py b/var/spack/repos/builtin/packages/tcl/package.py
--- a/var/spack/repos/builtin/packages/tcl/package.py
+++ b/var/spack/repos/builtin/packages/tcl/package.py
@@ -9,14 +9,13 @@
 
 
 class Tcl(AutotoolsPackage, SourceforgePackage):
-    """Tcl (Tool Command Language) is a very powerful but easy to
-       learn dynamic programming language, suitable for a very wide
-       range of uses, including web and desktop applications,
-       networking, administration, testing and many more. Open source
-       and business-friendly, Tcl is a mature yet evolving language
-       that is truly cross platform, easily deployed and highly
-       extensible."""
-    homepage = "http://www.tcl.tk"
+    """Tcl (Tool Command Language) is a very powerful but easy to learn dynamic
+    programming language, suitable for a very wide range of uses, including web and
+    desktop applications, networking, administration, testing and many more. Open source
+    and business-friendly, Tcl is a mature yet evolving language that is truly cross
+    platform, easily deployed and highly extensible."""
+
+    homepage = "https://www.tcl.tk/"
     sourceforge_mirror_path = "tcl/tcl8.6.11-src.tar.gz"
 
     version('8.6.11', sha256='8c0486668586672c5693d7d95817cb05a18c5ecca2f40e2836b9578064088258')
@@ -38,7 +37,7 @@ def install(self, spec, prefix):
         with working_dir(self.build_directory):
             make('install')
 
-            # http://wiki.tcl.tk/17463
+            # https://wiki.tcl-lang.org/page/kitgen
             if self.spec.satisfies('@8.6:'):
                 make('install-headers')
 
@@ -81,22 +80,39 @@ def libs(self):
     def command(self):
         """Returns the tclsh command.
 
-        :returns: The tclsh command
-        :rtype: Executable
+        Returns:
+            Executable: the tclsh command
         """
-        return Executable(os.path.realpath(self.prefix.bin.tclsh))
+        # Although we symlink tclshX.Y to tclsh, we also need to support external
+        # installations that may not have this symlink, or may have multiple versions
+        # of Tcl installed in the same directory.
+        return Executable(os.path.realpath(self.prefix.bin.join(
+            'tclsh{0}'.format(self.version.up_to(2)))))
 
     def setup_run_environment(self, env):
-        # When using Tkinter from within spack provided python+tkinter, python
-        # will not be able to find Tcl/Tk unless TCL_LIBRARY is set.
-        env.set('TCL_LIBRARY', self.spec['tcl'].libs.directories[0])
+        """Set TCL_LIBRARY to the directory containing init.tcl.
+
+        For further info see:
+
+        * https://wiki.tcl-lang.org/page/TCL_LIBRARY
+        """
+        # When using tkinter from within spack provided python+tkinter,
+        # python will not be able to find Tcl unless TCL_LIBRARY is set.
+        env.set('TCL_LIBRARY', os.path.dirname(
+            sorted(find(self.prefix, 'init.tcl'))[0]))
 
     def setup_dependent_build_environment(self, env, dependent_spec):
-        """Set TCLLIBPATH to include the tcl-shipped directory for
+        """Set TCL_LIBRARY to the directory containing init.tcl.
+        Set TCLLIBPATH to include the tcl-shipped directory for
         extensions and any other tcl extension it depends on.
-        For further info see: https://wiki.tcl.tk/1787"""
 
-        env.set('TCL_LIBRARY', self.spec['tcl'].libs.directories[0])
+        For further info see:
+
+        * https://wiki.tcl-lang.org/page/TCL_LIBRARY
+        * https://wiki.tcl-lang.org/page/TCLLIBPATH
+        """
+        env.set('TCL_LIBRARY', os.path.dirname(
+            sorted(find(self.prefix, 'init.tcl'))[0]))
 
         # If we set TCLLIBPATH, we must also ensure that the corresponding
         # tcl is found in the build environment. This to prevent cases
@@ -106,35 +122,40 @@ def setup_dependent_build_environment(self, env, dependent_spec):
         if not is_system_path(self.prefix.bin):
             env.prepend_path('PATH', self.prefix.bin)
 
-        tcl_paths = [join_path(self.spec['tcl'].libs.directories[0],
-                               'tcl{0}'.format(self.version.up_to(2)))]
+        # WARNING: paths in $TCLLIBPATH must be *space* separated,
+        # its value is meant to be a Tcl list, *not* an env list
+        # as explained here: https://wiki.tcl-lang.org/page/TCLLIBPATH:
+        # "TCLLIBPATH is a Tcl list, not some platform-specific
+        # colon-separated or semi-colon separated format"
+
+        # WARNING: Tcl and Tcl extensions like Tk install their configuration files
+        # in subdirectories like `<prefix>/lib/tcl8.6`. However, Tcl is aware of this,
+        # and $TCLLIBPATH should only contain `<prefix>/lib`. $TCLLIBPATH is only needed
+        # because we install Tcl extensions to different directories than Tcl. See:
+        # https://core.tcl-lang.org/tk/tktview/447bd3e4abe17452d19a80e6840dcc8a2603fcbc
+        env.prepend_path(
+            'TCLLIBPATH', self.spec['tcl'].libs.directories[0], separator=' ')
 
         for d in dependent_spec.traverse(deptype=('build', 'run', 'test')):
             if d.package.extends(self.spec):
                 # Tcl libraries may be installed in lib or lib64, see #19546
                 for lib in ['lib', 'lib64']:
-                    tcl_paths.append(join_path(
-                        d.prefix, lib, 'tcl{0}'.format(self.version.up_to(2))))
-
-        # WARNING: paths in $TCLLIBPATH must be *space* separated,
-        # its value is meant to be a Tcl list, *not* an env list
-        # as explained here: https://wiki.tcl.tk/1787:
-        # "TCLLIBPATH is a Tcl list, not some platform-specific
-        # colon-separated or semi-colon separated format"
-        tcllibpath = ' '.join(tcl_paths)
-        env.set('TCLLIBPATH', tcllibpath)
+                    tcllibpath = join_path(d.prefix, lib)
+                    if os.path.exists(tcllibpath):
+                        env.prepend_path('TCLLIBPATH', tcllibpath, separator=' ')
 
     def setup_dependent_run_environment(self, env, dependent_spec):
         """Set TCLLIBPATH to include the tcl-shipped directory for
         extensions and any other tcl extension it depends on.
-        For further info see: https://wiki.tcl.tk/1787"""
-
-        # For run time environment set only the path for
-        # dependent_spec and prepend it to TCLLIBPATH
-        if dependent_spec.package.extends(self.spec):
-            # Tcl libraries may be installed in lib or lib64, see #19546
-            for lib in ['lib', 'lib64']:
-                tcllibpath = join_path(
-                    self.prefix, lib, 'tcl{0}'.format(self.version.up_to(2)))
-                if os.path.exists(tcllibpath):
-                    env.prepend_path('TCLLIBPATH', tcllibpath, separator=' ')
+
+        For further info see:
+
+        * https://wiki.tcl-lang.org/page/TCLLIBPATH
+        """
+        for d in dependent_spec.traverse(deptype=('build', 'run', 'test')):
+            if d.package.extends(self.spec):
+                # Tcl libraries may be installed in lib or lib64, see #19546
+                for lib in ['lib', 'lib64']:
+                    tcllibpath = join_path(d.prefix, lib)
+                    if os.path.exists(tcllibpath):
+                        env.prepend_path('TCLLIBPATH', tcllibpath, separator=' ')
diff --git a/var/spack/repos/builtin/packages/tix/package.py b/var/spack/repos/builtin/packages/tix/package.py
--- a/var/spack/repos/builtin/packages/tix/package.py
+++ b/var/spack/repos/builtin/packages/tix/package.py
@@ -3,20 +3,46 @@
 #
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 
+import os
+
 from spack import *
 
 
 class Tix(AutotoolsPackage):
-    """Tix is a powerful high-level widget set that expands the capabilities
-       of your Tk/Tcl and Python applications."""
+    """Tix, the Tk Interface eXtension, is a powerful set of user interface components
+    that expands the capabilities of your Tcl/Tk and Python applications. Using Tix
+    together with Tk will greatly enhance the appearance and functionality of your
+    application."""
 
     homepage = "https://sourceforge.net/projects/tix/"
     url      = "https://sourceforge.net/projects/tix/files/tix/8.4.3/Tix8.4.3-src.tar.gz/download"
+
     version('8.4.3', sha256='562f040ff7657e10b5cffc2c41935f1a53c6402eb3d5f3189113d734fd6c03cb')
 
-    extends('tcl')
-    depends_on('tk@:8.5.99')
-    depends_on('tcl@:8.5.99')
+    extends('tcl', type=('build', 'link', 'run'))
+    depends_on('tk', type=('build', 'link', 'run'))
+
+    patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tix/files/panic.patch',
+          sha256='1be1a1c7453f6ab8771f90d7e7c0f8959490104752a16a8755bbb7287a841a96',
+          level=0)
+    patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tix/files/implicit.patch',
+          sha256='8a2720368c7757896814684147029d8318b9aa3b0914b3f37dd5e8a8603a61d3',
+          level=0)
+    patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tix/files/patch-generic-tixGrSort.c.diff',
+          sha256='99b33cc307f71bcf9cc6f5a44b588f22956884ce3f1e4c716ad64c79cf9c5f41',
+          level=0)
+    patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tix/files/patch-missing-headers.diff',
+          sha256='d9f789dcfe5f4c5ee4589a18f9f410cdf162e41d35d00648c1ef37831f4a2b2b',
+          level=0)
+    patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tix/files/patch-tk_x11.diff',
+          sha256='1e28d8eee1aaa956a00571cf495a4775e72a993958dff1cabfbc5f102e327a6f',
+          level=0)
+    patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tix/files/patch-tk_aqua.diff',
+          sha256='41a717f5d95f61b4b8196ca6f14ece8f4764d4ba58fb2e1ae15e3240ee5ac534',
+          level=0, when='platform=darwin')
+    patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tix/files/patch-dyld_variable.diff',
+          sha256='719eb2e4d8c5d6aae897e5f676cf5ed1a0005c1bd07fd9b18705d81a005f592b',
+          level=0, when='platform=darwin')
 
     def configure_args(self):
         spec = self.spec
@@ -27,7 +53,39 @@ def configure_args(self):
         ]
         return args
 
+    @run_after('install')
+    def darwin_fix(self):
+        # The shared library is not installed correctly on Darwin; fix this
+        if 'platform=darwin' in self.spec:
+            fix_darwin_install_name(self.prefix.lib.Tix + str(self.version))
+
+    def test(self):
+        test_data_dir = self.test_suite.current_test_data_dir
+        test_file = test_data_dir.join('test.tcl')
+        self.run_test(self.spec['tcl'].command.path, test_file,
+                      purpose='test that tix can be loaded')
+
     @property
     def libs(self):
         return find_libraries(['libTix{0}'.format(self.version)],
                               root=self.prefix, recursive=True)
+
+    def setup_run_environment(self, env):
+        """Set TIX_LIBRARY to the directory containing Tix.tcl.
+
+        For further info, see:
+
+        * http://tix.sourceforge.net/docs/pdf/TixUser.pdf
+        """
+        # When using tkinter.tix from within spack provided python+tkinter+tix,
+        # python will not be able to find Tix unless TIX_LIBRARY is set.
+        env.set('TIX_LIBRARY', os.path.dirname(find(self.prefix, 'Tix.tcl')[0]))
+
+    def setup_dependent_build_environment(self, env, dependent_spec):
+        """Set TIX_LIBRARY to the directory containing Tix.tcl.
+
+        For further info, see:
+
+        * http://tix.sourceforge.net/docs/pdf/TixUser.pdf
+        """
+        env.set('TIX_LIBRARY', os.path.dirname(find(self.prefix, 'Tix.tcl')[0]))
diff --git a/var/spack/repos/builtin/packages/tk/package.py b/var/spack/repos/builtin/packages/tk/package.py
--- a/var/spack/repos/builtin/packages/tk/package.py
+++ b/var/spack/repos/builtin/packages/tk/package.py
@@ -3,18 +3,18 @@
 #
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 
-from spack import *
 import os
 
+from spack import *
+
 
 class Tk(AutotoolsPackage, SourceforgePackage):
-    """Tk is a graphical user interface toolkit that takes developing
-       desktop applications to a higher level than conventional
-       approaches. Tk is the standard GUI not only for Tcl, but for
-       many other dynamic languages, and can produce rich, native
-       applications that run unchanged across Windows, Mac OS X, Linux
-       and more."""
-    homepage = "http://www.tcl.tk"
+    """Tk is a graphical user interface toolkit that takes developing desktop
+    applications to a higher level than conventional approaches. Tk is the standard GUI
+    not only for Tcl, but for many other dynamic languages, and can produce rich, native
+    applications that run unchanged across Windows, Mac OS X, Linux and more."""
+
+    homepage = "https://www.tcl.tk"
     sourceforge_mirror_path = "tcl/tk8.6.5-src.tar.gz"
 
     version('8.6.11', sha256='5228a8187a7f70fa0791ef0f975270f068ba9557f57456f51eb02d9d4ea31282')
@@ -25,14 +25,12 @@ class Tk(AutotoolsPackage, SourceforgePackage):
     version('8.6.3',  sha256='ba15d56ac27d8c0a7b1a983915a47e0f635199b9473cf6e10fbce1fc73fd8333')
     version('8.5.19', sha256='407af1de167477d598bd6166d84459a3bdccc2fb349360706154e646a9620ffa')
 
-    variant('xft', default=True,
-            description='Enable X FreeType')
-    variant('xss', default=True,
-            description='Enable X Screen Saver')
+    variant('xft', default=True, description='Enable X FreeType')
+    variant('xss', default=True, description='Enable X Screen Saver')
 
-    extends('tcl')
+    extends('tcl', type=('build', 'link', 'run'))
 
-    depends_on('tcl@8.6:', when='@8.6:')
+    depends_on('tcl@8.6:', type=('build', 'link', 'run'), when='@8.6:')
     depends_on('libx11')
     depends_on('libxft', when='+xft')
     depends_on('libxscrnsaver', when='+xss')
@@ -45,6 +43,21 @@ class Tk(AutotoolsPackage, SourceforgePackage):
     patch('https://github.com/macports/macports-ports/blob/master/x11/tk/files/patch-unix-Makefile.in.diff',
           sha256='54bba3d2b3550b7e2c636881c1a3acaf6e1eb743f314449a132864ff47fd0010',
           level=0, when='@:8.6.11 platform=darwin')
+    patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tk/files/patch-dyld_fallback_library_path.diff',
+          sha256='9ce6512f1928db9987986f4d3540207c39429395d5234bd6489ba9d86a6d9c31',
+          level=0, when='platform=darwin')
+
+    def configure_args(self):
+        spec = self.spec
+        config_args = [
+            '--with-tcl={0}'.format(spec['tcl'].libs.directories[0]),
+            '--x-includes={0}'.format(spec['libx11'].headers.directories[0]),
+            '--x-libraries={0}'.format(spec['libx11'].libs.directories[0])
+        ]
+        config_args += self.enable_or_disable('xft')
+        config_args += self.enable_or_disable('xss')
+
+        return config_args
 
     def install(self, spec, prefix):
         with working_dir(self.build_directory):
@@ -64,32 +77,54 @@ def install(self, spec, prefix):
                 stage_src, installed_src,
                 join_path(self.spec['tk'].libs.directories[0], 'tkConfig.sh'))
 
+    @run_after('install')
+    def symlink_wish(self):
+        with working_dir(self.prefix.bin):
+            symlink('wish{0}'.format(self.version.up_to(2)), 'wish')
+
+    def test(self):
+        self.run_test(self.spec['tk'].command.path, ['-h'],
+                      purpose='test wish command')
+
+        test_data_dir = self.test_suite.current_test_data_dir
+        test_file = test_data_dir.join('test.tcl')
+        self.run_test(self.spec['tcl'].command.path, test_file,
+                      purpose='test that tk can be loaded')
+
+    @property
+    def command(self):
+        """Returns the wish command.
+
+        Returns:
+            Executable: the wish command
+        """
+        # Although we symlink wishX.Y to wish, we also need to support external
+        # installations that may not have this symlink, or may have multiple versions
+        # of Tk installed in the same directory.
+        return Executable(os.path.realpath(self.prefix.bin.join(
+            'wish{0}'.format(self.version.up_to(2)))))
+
     @property
     def libs(self):
         return find_libraries(['libtk{0}'.format(self.version.up_to(2))],
                               root=self.prefix, recursive=True)
 
     def setup_run_environment(self, env):
-        # When using Tkinter from within spack provided python+tkinter, python
-        # will not be able to find Tcl/Tk unless TK_LIBRARY is set.
-        env.set('TK_LIBRARY', self.spec['tk'].libs.directories[0])
+        """Set TK_LIBRARY to the directory containing tk.tcl.
 
-    def setup_dependent_build_environment(self, env, dependent_spec):
-        env.set('TK_LIBRARY', self.spec['tk'].libs.directories[0])
+        For further info, see:
 
-    def configure_args(self):
-        spec = self.spec
-        config_args = [
-            '--with-tcl={0}'.format(spec['tcl'].libs.directories[0]),
-            '--x-includes={0}'.format(spec['libx11'].headers.directories[0]),
-            '--x-libraries={0}'.format(spec['libx11'].libs.directories[0])
-        ]
-        config_args += self.enable_or_disable('xft')
-        config_args += self.enable_or_disable('xss')
+        * https://www.tcl-lang.org/man/tcl/TkCmd/tkvars.htm
+        """
+        # When using tkinter from within spack provided python+tkinter,
+        # python will not be able to find Tk unless TK_LIBRARY is set.
+        env.set('TK_LIBRARY', os.path.dirname(sorted(find(self.prefix, 'tk.tcl'))[0]))
 
-        return config_args
+    def setup_dependent_build_environment(self, env, dependent_spec):
+        """Set TK_LIBRARY to the directory containing tk.tcl.
 
-    @run_after('install')
-    def symlink_wish(self):
-        with working_dir(self.prefix.bin):
-            symlink('wish{0}'.format(self.version.up_to(2)), 'wish')
+        For further info, see:
+
+        * https://www.tcl-lang.org/man/tcl/TkCmd/tkvars.htm
+        """
+        env.set('TK_LIBRARY', os.path.dirname(sorted(find(self.prefix, 'tk.tcl'))[0]))


Test patch:
diff --git a/var/spack/repos/builtin/packages/tix/test/test.tcl b/var/spack/repos/builtin/packages/tix/test/test.tcl
new file mode 100644
--- /dev/null
+++ b/var/spack/repos/builtin/packages/tix/test/test.tcl
@@ -0,0 +1,5 @@
+#!/usr/bin/env tclsh
+
+package require Tix
+
+exit
diff --git a/var/spack/repos/builtin/packages/tk/test/test.tcl b/var/spack/repos/builtin/packages/tk/test/test.tcl
new file mode 100644
--- /dev/null
+++ b/var/spack/repos/builtin/packages/tk/test/test.tcl
@@ -0,0 +1,5 @@
+#!/usr/bin/env tclsh
+
+package require Tk
+
+exit