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: Tcl: add nmake system for Windows (#41759)

---
 var/spack/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.package import *
 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 @@ class Tcl(AutotoolsPackage, SourceforgePackage):
             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 @@ class Tcl(AutotoolsPackage, SourceforgePackage):
         # 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 @@ class Tcl(AutotoolsPackage, SourceforgePackage):
                 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)]
-- 
cgit v1.2.3-70-g09d2