Python: fix +tkinter+tix support (#23980)

* Tcl: fix TCLLIBPATH

* Fix TCL|TK|TIX_LIBRARY paths

* Fix TCL_LIBRARY, no tcl8.6 subdir

* Don't rely on os.listdirs sorting

For tcl and tk, we also install the source directory, so there are
two init.tcl and tk.tcl locations. We want the one in lib/lib64,
which should come before the one in share.

* Add more patches

* Fix dylib on macOS

* Tk: add smoke tests

* Tix: add smoke test
This commit is contained in:
Adam J. Stewart 2021-06-08 12:45:04 -05:00 committed by GitHub
parent f33c4e7280
commit 92be358582
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 202 additions and 78 deletions

View File

@ -9,14 +9,13 @@
class Tcl(AutotoolsPackage, SourceforgePackage): class Tcl(AutotoolsPackage, SourceforgePackage):
"""Tcl (Tool Command Language) is a very powerful but easy to """Tcl (Tool Command Language) is a very powerful but easy to learn dynamic
learn dynamic programming language, suitable for a very wide programming language, suitable for a very wide range of uses, including web and
range of uses, including web and desktop applications, desktop applications, networking, administration, testing and many more. Open source
networking, administration, testing and many more. Open source and business-friendly, Tcl is a mature yet evolving language that is truly cross
and business-friendly, Tcl is a mature yet evolving language platform, easily deployed and highly extensible."""
that is truly cross platform, easily deployed and highly
extensible.""" homepage = "https://www.tcl.tk/"
homepage = "http://www.tcl.tk"
sourceforge_mirror_path = "tcl/tcl8.6.11-src.tar.gz" sourceforge_mirror_path = "tcl/tcl8.6.11-src.tar.gz"
version('8.6.11', sha256='8c0486668586672c5693d7d95817cb05a18c5ecca2f40e2836b9578064088258') version('8.6.11', sha256='8c0486668586672c5693d7d95817cb05a18c5ecca2f40e2836b9578064088258')
@ -38,7 +37,7 @@ def install(self, spec, prefix):
with working_dir(self.build_directory): with working_dir(self.build_directory):
make('install') make('install')
# http://wiki.tcl.tk/17463 # https://wiki.tcl-lang.org/page/kitgen
if self.spec.satisfies('@8.6:'): if self.spec.satisfies('@8.6:'):
make('install-headers') make('install-headers')
@ -81,22 +80,39 @@ def libs(self):
def command(self): def command(self):
"""Returns the tclsh command. """Returns the tclsh command.
:returns: The tclsh command Returns:
:rtype: Executable 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): def setup_run_environment(self, env):
# When using Tkinter from within spack provided python+tkinter, python """Set TCL_LIBRARY to the directory containing init.tcl.
# will not be able to find Tcl/Tk unless TCL_LIBRARY is set.
env.set('TCL_LIBRARY', self.spec['tcl'].libs.directories[0]) 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): 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. 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 # If we set TCLLIBPATH, we must also ensure that the corresponding
# tcl is found in the build environment. This to prevent cases # 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): if not is_system_path(self.prefix.bin):
env.prepend_path('PATH', self.prefix.bin) env.prepend_path('PATH', self.prefix.bin)
tcl_paths = [join_path(self.spec['tcl'].libs.directories[0], # WARNING: paths in $TCLLIBPATH must be *space* separated,
'tcl{0}'.format(self.version.up_to(2)))] # 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')): for d in dependent_spec.traverse(deptype=('build', 'run', 'test')):
if d.package.extends(self.spec): if d.package.extends(self.spec):
# Tcl libraries may be installed in lib or lib64, see #19546 # Tcl libraries may be installed in lib or lib64, see #19546
for lib in ['lib', 'lib64']: for lib in ['lib', 'lib64']:
tcl_paths.append(join_path( tcllibpath = join_path(d.prefix, lib)
d.prefix, lib, 'tcl{0}'.format(self.version.up_to(2)))) if os.path.exists(tcllibpath):
env.prepend_path('TCLLIBPATH', tcllibpath, separator=' ')
# 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)
def setup_dependent_run_environment(self, env, dependent_spec): def setup_dependent_run_environment(self, env, dependent_spec):
"""Set TCLLIBPATH to include the tcl-shipped directory for """Set TCLLIBPATH to include the tcl-shipped directory for
extensions and any other tcl extension it depends on. 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 For further info see:
# dependent_spec and prepend it to TCLLIBPATH
if dependent_spec.package.extends(self.spec): * https://wiki.tcl-lang.org/page/TCLLIBPATH
# Tcl libraries may be installed in lib or lib64, see #19546 """
for lib in ['lib', 'lib64']: for d in dependent_spec.traverse(deptype=('build', 'run', 'test')):
tcllibpath = join_path( if d.package.extends(self.spec):
self.prefix, lib, 'tcl{0}'.format(self.version.up_to(2))) # Tcl libraries may be installed in lib or lib64, see #19546
if os.path.exists(tcllibpath): for lib in ['lib', 'lib64']:
env.prepend_path('TCLLIBPATH', tcllibpath, separator=' ') tcllibpath = join_path(d.prefix, lib)
if os.path.exists(tcllibpath):
env.prepend_path('TCLLIBPATH', tcllibpath, separator=' ')

View File

@ -3,20 +3,46 @@
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
from spack import * from spack import *
class Tix(AutotoolsPackage): class Tix(AutotoolsPackage):
"""Tix is a powerful high-level widget set that expands the capabilities """Tix, the Tk Interface eXtension, is a powerful set of user interface components
of your Tk/Tcl and Python applications.""" 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/" homepage = "https://sourceforge.net/projects/tix/"
url = "https://sourceforge.net/projects/tix/files/tix/8.4.3/Tix8.4.3-src.tar.gz/download" url = "https://sourceforge.net/projects/tix/files/tix/8.4.3/Tix8.4.3-src.tar.gz/download"
version('8.4.3', sha256='562f040ff7657e10b5cffc2c41935f1a53c6402eb3d5f3189113d734fd6c03cb') version('8.4.3', sha256='562f040ff7657e10b5cffc2c41935f1a53c6402eb3d5f3189113d734fd6c03cb')
extends('tcl') extends('tcl', type=('build', 'link', 'run'))
depends_on('tk@:8.5.99') depends_on('tk', type=('build', 'link', 'run'))
depends_on('tcl@:8.5.99')
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): def configure_args(self):
spec = self.spec spec = self.spec
@ -27,7 +53,39 @@ def configure_args(self):
] ]
return args 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 @property
def libs(self): def libs(self):
return find_libraries(['libTix{0}'.format(self.version)], return find_libraries(['libTix{0}'.format(self.version)],
root=self.prefix, recursive=True) 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]))

View File

@ -0,0 +1,5 @@
#!/usr/bin/env tclsh
package require Tix
exit

View File

@ -3,18 +3,18 @@
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
from spack import *
import os import os
from spack import *
class Tk(AutotoolsPackage, SourceforgePackage): class Tk(AutotoolsPackage, SourceforgePackage):
"""Tk is a graphical user interface toolkit that takes developing """Tk is a graphical user interface toolkit that takes developing desktop
desktop applications to a higher level than conventional applications to a higher level than conventional approaches. Tk is the standard GUI
approaches. Tk is the standard GUI not only for Tcl, but for not only for Tcl, but for many other dynamic languages, and can produce rich, native
many other dynamic languages, and can produce rich, native applications that run unchanged across Windows, Mac OS X, Linux and more."""
applications that run unchanged across Windows, Mac OS X, Linux
and more.""" homepage = "https://www.tcl.tk"
homepage = "http://www.tcl.tk"
sourceforge_mirror_path = "tcl/tk8.6.5-src.tar.gz" sourceforge_mirror_path = "tcl/tk8.6.5-src.tar.gz"
version('8.6.11', sha256='5228a8187a7f70fa0791ef0f975270f068ba9557f57456f51eb02d9d4ea31282') version('8.6.11', sha256='5228a8187a7f70fa0791ef0f975270f068ba9557f57456f51eb02d9d4ea31282')
@ -25,14 +25,12 @@ class Tk(AutotoolsPackage, SourceforgePackage):
version('8.6.3', sha256='ba15d56ac27d8c0a7b1a983915a47e0f635199b9473cf6e10fbce1fc73fd8333') version('8.6.3', sha256='ba15d56ac27d8c0a7b1a983915a47e0f635199b9473cf6e10fbce1fc73fd8333')
version('8.5.19', sha256='407af1de167477d598bd6166d84459a3bdccc2fb349360706154e646a9620ffa') version('8.5.19', sha256='407af1de167477d598bd6166d84459a3bdccc2fb349360706154e646a9620ffa')
variant('xft', default=True, variant('xft', default=True, description='Enable X FreeType')
description='Enable X FreeType') variant('xss', default=True, description='Enable X Screen Saver')
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('libx11')
depends_on('libxft', when='+xft') depends_on('libxft', when='+xft')
depends_on('libxscrnsaver', when='+xss') depends_on('libxscrnsaver', when='+xss')
@ -45,6 +43,21 @@ class Tk(AutotoolsPackage, SourceforgePackage):
patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tk/files/patch-unix-Makefile.in.diff', patch('https://raw.githubusercontent.com/macports/macports-ports/master/x11/tk/files/patch-unix-Makefile.in.diff',
sha256='54bba3d2b3550b7e2c636881c1a3acaf6e1eb743f314449a132864ff47fd0010', sha256='54bba3d2b3550b7e2c636881c1a3acaf6e1eb743f314449a132864ff47fd0010',
level=0, when='@:8.6.11 platform=darwin') 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): def install(self, spec, prefix):
with working_dir(self.build_directory): with working_dir(self.build_directory):
@ -64,32 +77,54 @@ def install(self, spec, prefix):
stage_src, installed_src, stage_src, installed_src,
join_path(self.spec['tk'].libs.directories[0], 'tkConfig.sh')) 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 @property
def libs(self): def libs(self):
return find_libraries(['libtk{0}'.format(self.version.up_to(2))], return find_libraries(['libtk{0}'.format(self.version.up_to(2))],
root=self.prefix, recursive=True) root=self.prefix, recursive=True)
def setup_run_environment(self, env): def setup_run_environment(self, env):
# When using Tkinter from within spack provided python+tkinter, python """Set TK_LIBRARY to the directory containing tk.tcl.
# will not be able to find Tcl/Tk unless TK_LIBRARY is set.
env.set('TK_LIBRARY', self.spec['tk'].libs.directories[0]) For further info, see:
* 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]))
def setup_dependent_build_environment(self, env, dependent_spec): def setup_dependent_build_environment(self, env, dependent_spec):
env.set('TK_LIBRARY', self.spec['tk'].libs.directories[0]) """Set TK_LIBRARY to the directory containing tk.tcl.
def configure_args(self): For further info, see:
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 * https://www.tcl-lang.org/man/tcl/TkCmd/tkvars.htm
"""
@run_after('install') env.set('TK_LIBRARY', os.path.dirname(sorted(find(self.prefix, 'tk.tcl'))[0]))
def symlink_wish(self):
with working_dir(self.prefix.bin):
symlink('wish{0}'.format(self.version.up_to(2)), 'wish')

View File

@ -0,0 +1,5 @@
#!/usr/bin/env tclsh
package require Tk
exit