From 18efd808da878a137a05bb1e2633b67458577b06 Mon Sep 17 00:00:00 2001
From: Alec Scott <hi@alecbcs.com>
Date: Tue, 28 Nov 2023 01:33:46 -0800
Subject: GoPackage: add new build system for Go packages (#41164)

Co-authored-by: Tom Scogland <scogland1@llnl.gov>
Co-authored-by: Wouter Deconinck <wdconinc@gmail.com>
Co-authored-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
---
 lib/spack/spack/build_systems/go.py                | 98 ++++++++++++++++++++++
 lib/spack/spack/cmd/create.py                      | 10 +++
 lib/spack/spack/package.py                         |  1 +
 share/spack/spack-completion.fish                  |  2 +-
 .../repos/builtin/packages/lazygit/package.py      | 20 +++++
 var/spack/repos/builtin/packages/scc/package.py    | 22 +++++
 6 files changed, 152 insertions(+), 1 deletion(-)
 create mode 100644 lib/spack/spack/build_systems/go.py
 create mode 100644 var/spack/repos/builtin/packages/lazygit/package.py
 create mode 100644 var/spack/repos/builtin/packages/scc/package.py

diff --git a/lib/spack/spack/build_systems/go.py b/lib/spack/spack/build_systems/go.py
new file mode 100644
index 0000000000..a7dd04fcf6
--- /dev/null
+++ b/lib/spack/spack/build_systems/go.py
@@ -0,0 +1,98 @@
+# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+import inspect
+
+import llnl.util.filesystem as fs
+
+import spack.builder
+import spack.package_base
+from spack.directives import build_system, extends
+from spack.multimethod import when
+
+from ._checks import BaseBuilder, execute_install_time_tests
+
+
+class GoPackage(spack.package_base.PackageBase):
+    """Specialized class for packages built using the Go toolchain."""
+
+    #: This attribute is used in UI queries that need to know the build
+    #: system base class
+    build_system_class = "GoPackage"
+
+    #: Legacy buildsystem attribute used to deserialize and install old specs
+    legacy_buildsystem = "go"
+
+    build_system("go")
+
+    with when("build_system=go"):
+        # TODO: this seems like it should be depends_on, see
+        # setup_dependent_build_environment in go for why I kept it like this
+        extends("go@1.14:", type="build")
+
+
+@spack.builder.builder("go")
+class GoBuilder(BaseBuilder):
+    """The Go builder encodes the most common way of building software with
+    a golang go.mod file. It has two phases that can be overridden, if need be:
+
+            1. :py:meth:`~.GoBuilder.build`
+            2. :py:meth:`~.GoBuilder.install`
+
+    For a finer tuning you may override:
+
+        +-----------------------------------------------+--------------------+
+        | **Method**                                    | **Purpose**        |
+        +===============================================+====================+
+        | :py:meth:`~.GoBuilder.build_args`             | Specify arguments  |
+        |                                               | to ``go build``    |
+        +-----------------------------------------------+--------------------+
+        | :py:meth:`~.GoBuilder.check_args`             | Specify arguments  |
+        |                                               | to ``go test``     |
+        +-----------------------------------------------+--------------------+
+    """
+
+    phases = ("build", "install")
+
+    #: Callback names for install-time test
+    install_time_test_callbacks = ["check"]
+
+    def setup_build_environment(self, env):
+        env.set("GO111MODULE", "on")
+        env.set("GOTOOLCHAIN", "local")
+
+    @property
+    def build_directory(self):
+        """Return the directory containing the main go.mod."""
+        return self.pkg.stage.source_path
+
+    @property
+    def build_args(self):
+        """Arguments for ``go build``."""
+        # Pass ldflags -s = --strip-all and -w = --no-warnings by default
+        return ["-ldflags", "-s -w", "-o", f"{self.pkg.name}"]
+
+    @property
+    def check_args(self):
+        """Argument for ``go test`` during check phase"""
+        return []
+
+    def build(self, pkg, spec, prefix):
+        """Runs ``go build`` in the source directory"""
+        with fs.working_dir(self.build_directory):
+            inspect.getmodule(pkg).go("build", *self.build_args)
+
+    def install(self, pkg, spec, prefix):
+        """Install built binaries into prefix bin."""
+        with fs.working_dir(self.build_directory):
+            fs.mkdirp(prefix.bin)
+            fs.install(pkg.name, prefix.bin)
+
+    spack.builder.run_after("install")(execute_install_time_tests)
+
+    def check(self):
+        """Run ``go test .`` in the source directory"""
+        with fs.working_dir(self.build_directory):
+            inspect.getmodule(self.pkg).go("test", *self.check_args)
diff --git a/lib/spack/spack/cmd/create.py b/lib/spack/spack/cmd/create.py
index 130242a8b1..f9d4c30a3d 100644
--- a/lib/spack/spack/cmd/create.py
+++ b/lib/spack/spack/cmd/create.py
@@ -194,6 +194,14 @@ class CMakePackageTemplate(PackageTemplate):
         return args"""
 
 
+class GoPackageTemplate(PackageTemplate):
+    """Provides appropriate overrides for Go-module-based packages"""
+
+    base_class_name = "GoPackage"
+
+    body_def = ""
+
+
 class LuaPackageTemplate(PackageTemplate):
     """Provides appropriate overrides for LuaRocks-based packages"""
 
@@ -590,6 +598,7 @@ templates = {
     "cargo": CargoPackageTemplate,
     "cmake": CMakePackageTemplate,
     "generic": PackageTemplate,
+    "go": GoPackageTemplate,
     "intel": IntelPackageTemplate,
     "lua": LuaPackageTemplate,
     "makefile": MakefilePackageTemplate,
@@ -689,6 +698,7 @@ class BuildSystemGuesser:
             (r"/CMakeLists\.txt$", "cmake"),
             (r"/NAMESPACE$", "r"),
             (r"/Cargo\.toml$", "cargo"),
+            (r"/go\.mod$", "go"),
             (r"/configure$", "autotools"),
             (r"/configure\.(in|ac)$", "autoreconf"),
             (r"/Makefile\.am$", "autoreconf"),
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 0fdfffc177..8113d363dd 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -42,6 +42,7 @@ from spack.build_systems.cmake import CMakePackage, generator
 from spack.build_systems.cuda import CudaPackage
 from spack.build_systems.generic import Package
 from spack.build_systems.gnu import GNUMirrorPackage
+from spack.build_systems.go import GoPackage
 from spack.build_systems.intel import IntelPackage
 from spack.build_systems.lua import LuaPackage
 from spack.build_systems.makefile import MakefilePackage
diff --git a/share/spack/spack-completion.fish b/share/spack/spack-completion.fish
index 08df9825b6..1d32fc1d37 100755
--- a/share/spack/spack-completion.fish
+++ b/share/spack/spack-completion.fish
@@ -1261,7 +1261,7 @@ complete -c spack -n '__fish_spack_using_command create' -l keep-stage -f -a kee
 complete -c spack -n '__fish_spack_using_command create' -l keep-stage -d 'don\'t clean up staging area when command completes'
 complete -c spack -n '__fish_spack_using_command create' -s n -l name -r -f -a name
 complete -c spack -n '__fish_spack_using_command create' -s n -l name -r -d 'name of the package to create'
-complete -c spack -n '__fish_spack_using_command create' -s t -l template -r -f -a 'autoreconf autotools bazel bundle cargo cmake generic intel lua makefile maven meson octave perlbuild perlmake python qmake r racket ruby scons sip waf'
+complete -c spack -n '__fish_spack_using_command create' -s t -l template -r -f -a 'autoreconf autotools bazel bundle cargo cmake generic go intel lua makefile maven meson octave perlbuild perlmake python qmake r racket ruby scons sip waf'
 complete -c spack -n '__fish_spack_using_command create' -s t -l template -r -d 'build system template to use'
 complete -c spack -n '__fish_spack_using_command create' -s r -l repo -r -f -a repo
 complete -c spack -n '__fish_spack_using_command create' -s r -l repo -r -d 'path to a repository where the package should be created'
diff --git a/var/spack/repos/builtin/packages/lazygit/package.py b/var/spack/repos/builtin/packages/lazygit/package.py
new file mode 100644
index 0000000000..81395262bf
--- /dev/null
+++ b/var/spack/repos/builtin/packages/lazygit/package.py
@@ -0,0 +1,20 @@
+# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+
+from spack.package import *
+
+
+class Lazygit(GoPackage):
+    """A simple terminal UI for git commands"""
+
+    homepage = "https://github.com/jesseduffield/lazygit"
+    url = "https://github.com/jesseduffield/lazygit/archive/refs/tags/v0.40.2.tar.gz"
+
+    maintainers("twrs")
+
+    license("MIT")
+
+    version("0.40.2", sha256="146bd63995fcf2f2373bbc2143b3565b7a2be49a1d4e385496265ac0f69e4128")
diff --git a/var/spack/repos/builtin/packages/scc/package.py b/var/spack/repos/builtin/packages/scc/package.py
new file mode 100644
index 0000000000..b0eae1b3c8
--- /dev/null
+++ b/var/spack/repos/builtin/packages/scc/package.py
@@ -0,0 +1,22 @@
+# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+
+from spack.package import *
+
+
+class Scc(GoPackage):
+    """
+    Sloc, Cloc and Code: scc is a very fast accurate code counter with
+    complexity calculations and COCOMO estimates written in pure Go.
+    """
+
+    homepage = "https://github.com/boyter/scc"
+    url = "https://github.com/boyter/scc/archive/refs/tags/v3.1.0.tar.gz"
+    git = "https://github.com/boyter/scc.git"
+
+    license("MIT")
+
+    version("3.1.0", sha256="bffea99c7f178bc48bfba3c64397d53a20a751dfc78221d347aabdce3422fd20")
-- 
cgit v1.2.3-70-g09d2