From ecdf3ff297eda60180253c43a19a303a8b5a8bfd Mon Sep 17 00:00:00 2001 From: "John W. Parent" <45471568+johnwparent@users.noreply.github.com> Date: Thu, 21 Dec 2023 16:15:49 -0500 Subject: [PATCH] Tcl: add nmake system for Windows (#41759) --- .../repos/builtin/packages/tcl/package.py | 150 ++++++++++-------- 1 file changed, 88 insertions(+), 62 deletions(-) diff --git a/var/spack/repos/builtin/packages/tcl/package.py b/var/spack/repos/builtin/packages/tcl/package.py index dee78161bb..816b8f649a 100644 --- a/var/spack/repos/builtin/packages/tcl/package.py +++ b/var/spack/repos/builtin/packages/tcl/package.py @@ -11,7 +11,21 @@ from spack.util.environment import is_system_path -class Tcl(AutotoolsPackage, SourceforgePackage): +class TclMixin: + def _find_script_dir(self): + # Put more-specific prefixes first + check_prefixes = [ + join_path(self.prefix, "share", "tcl{0}".format(self.version.up_to(2))), + self.prefix, + ] + for prefix in check_prefixes: + result = find_first(prefix, "init.tcl") + if result: + return os.path.dirname(result) + raise RuntimeError("Cannot locate init.tcl") + + +class Tcl(AutotoolsPackage, NMakePackage, SourceforgePackage, TclMixin): """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 @@ -20,6 +34,7 @@ class Tcl(AutotoolsPackage, SourceforgePackage): homepage = "https://www.tcl.tk/" sourceforge_mirror_path = "tcl/tcl8.6.11-src.tar.gz" + tags = ["windows"] version("8.6.12", sha256="26c995dd0f167e48b11961d891ee555f680c175f7173ff8cb829f4ebcde4c1a6") version("8.6.11", sha256="8c0486668586672c5693d7d95817cb05a18c5ecca2f40e2836b9578064088258") @@ -35,44 +50,11 @@ class Tcl(AutotoolsPackage, SourceforgePackage): depends_on("zlib-api") - configure_directory = "unix" - - filter_compiler_wrappers("tclConfig.sh", relative_root="lib") - - def install(self, spec, prefix): - with working_dir(self.build_directory): - make("install") - - # https://wiki.tcl-lang.org/page/kitgen - if self.spec.satisfies("@8.6:"): - make("install-headers") - - # Some applications like Expect require private Tcl headers. - make("install-private-headers") - - # Copy source to install tree - # A user-provided install option might re-do this - # https://github.com/spack/spack/pull/4102/files - installed_src = join_path(self.spec.prefix, "share", self.name, "src") - stage_src = os.path.realpath(self.stage.source_path) - install_tree(stage_src, installed_src) - - # Replace stage dir -> installed src dir in tclConfig - filter_file( - stage_src, - installed_src, - join_path(self.spec["tcl"].libs.directories[0], "tclConfig.sh"), - ) - - # Don't install binaries in src/ tree - with working_dir(join_path(installed_src, self.configure_directory)): - make("clean") - - @run_after("install") - def symlink_tclsh(self): - with working_dir(self.prefix.bin): - symlink("tclsh{0}".format(self.version.up_to(2)), "tclsh") + # No compiler wrappers on Windows + for plat in ["linux", "darwin", "cray"]: + filter_compiler_wrappers("tclConfig.sh", relative_root="lib", when=f"platform={plat}") + build_system("autotools", "nmake") # ======================================================================== # Set up environment to make install easy for tcl extensions. # ======================================================================== @@ -97,18 +79,6 @@ def command(self): os.path.realpath(self.prefix.bin.join("tclsh{0}".format(self.version.up_to(2)))) ) - def _find_script_dir(self): - # Put more-specific prefixes first - check_prefixes = [ - join_path(self.prefix, "share", "tcl{0}".format(self.version.up_to(2))), - self.prefix, - ] - for prefix in check_prefixes: - result = find_first(prefix, "init.tcl") - if result: - return os.path.dirname(result) - raise RuntimeError("Cannot locate init.tcl") - def setup_run_environment(self, env): """Set TCL_LIBRARY to the directory containing init.tcl. @@ -120,6 +90,28 @@ def setup_run_environment(self, env): # python will not be able to find Tcl unless TCL_LIBRARY is set. env.set("TCL_LIBRARY", self._find_script_dir()) + 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-lang.org/page/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(dependent_spec.prefix, lib) + if os.path.exists(tcllibpath): + env.prepend_path("TCLLIBPATH", tcllibpath, separator=" ") + + +class BaseBuilder(TclMixin, metaclass=spack.builder.PhaseCallbacksMeta): + @run_after("install") + def symlink_tclsh(self): + with working_dir(self.prefix.bin): + symlink("tclsh{0}".format(self.version.up_to(2)), "tclsh") + def setup_dependent_build_environment(self, env, dependent_spec): """Set TCL_LIBRARY to the directory containing init.tcl. Set TCLLIBPATH to include the tcl-shipped directory for @@ -160,17 +152,51 @@ def setup_dependent_build_environment(self, env, dependent_spec): 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: +class AutotoolsBuilder(BaseBuilder, spack.build_systems.autotools.AutotoolsBuilder): + configure_directory = "unix" - * https://wiki.tcl-lang.org/page/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(dependent_spec.prefix, lib) - if os.path.exists(tcllibpath): - env.prepend_path("TCLLIBPATH", tcllibpath, separator=" ") + def install(self, spec, prefix): + with working_dir(self.build_directory): + make("install") + + # https://wiki.tcl-lang.org/page/kitgen + if self.spec.satisfies("@8.6:"): + make("install-headers") + + # Some applications like Expect require private Tcl headers. + make("install-private-headers") + + # Copy source to install tree + # A user-provided install option might re-do this + # https://github.com/spack/spack/pull/4102/files + installed_src = join_path(self.spec.prefix, "share", self.name, "src") + stage_src = os.path.realpath(self.stage.source_path) + install_tree(stage_src, installed_src) + + # Replace stage dir -> installed src dir in tclConfig + filter_file( + stage_src, + installed_src, + join_path(self.spec["tcl"].libs.directories[0], "tclConfig.sh"), + ) + + # Don't install binaries in src/ tree + with working_dir(join_path(installed_src, self.configure_directory)): + make("clean") + + +class NMakeBuilder(BaseBuilder, spack.build_systems.nmake.NMakeBuilder): + build_targets = ["all"] + install_targets = ["install"] + + @property + def makefile_root(self): + return f"{self.stage.source_path}\\win" + + @property + def makefile_name(self): + return "makefile.vc" + + def nmake_install_args(self): + return [self.define("INSTALLDIR", self.spec.prefix)]