;;; GNU Guix --- Functional package management for GNU
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix. If not, see .
;;; TODOs:
;;; 1. Support non-git root repositories
;;; 2. Store/cache individual module downloads
(define-module (guix go-mod-download)
#:use-module (guix build utils)
#:use-module (guix derivations)
#:use-module (guix packages)
#:use-module (guix gexp)
#:use-module (guix modules)
#:use-module (guix monads)
#:use-module (guix records)
#:use-module (guix store)
#:use-module (guix git-download)
#:export (go-mod-reference
go-mod-reference?
go-mod-source
go-mod-go
go-mod-fetch))
(define (go-package)
"Return the default Go package."
(let ((distro (resolve-interface '(gnu packages golang))))
(module-ref distro 'go)))
(define (nss-certs-package)
"Return the default nss-certs package."
(let ((distro (resolve-interface '(gnu packages certs))))
(module-ref distro 'nss-certs)))
;; The source key accepts the same kinds as the package record, but mostly go
;; use git. The go key specifies which go version to use, which might be
;; necessary if any module sets a newer toolchain in go.mod
(define-record-type*
go-mod-reference make-go-mod-reference
go-mod-reference?
(source go-mod-source)
(go go-mod-go (default (go-package)) (thunked)))
;; Fetch all direct and indirect dependencies of the go modules in the source
;; tree (usually a git repo) using go mod download.
(define* (go-mod-fetch source hash-algo hash
#:optional name
#:key (system (%current-system))
(guile (default-guile))
(go (go-package))
(nss-certs (nss-certs-package)))
(define guile-json
(module-ref (resolve-interface '(gnu packages guile)) 'guile-json-4))
(define* (build source go)
(with-imported-modules (source-module-closure
'((guix build utils)
(guix build download)
(srfi srfi-34)))
(with-extensions (list guile-json)
#~(begin
(use-modules
(guix build download)
(guix build utils)
(srfi srfi-34))
(let* ((cert-dir (string-append #$nss-certs "/etc/ssl/certs"))
(src-dir (string-append #$output "/source"))
(mod-cache (string-append #$output "/go/pkg"))
(xgo (string-append #$go "/bin/go")))
;; go.mod files can specify a minimum required toolchain which could
;; cause go mod download to fetch and install a newer compiler
;; if invoked with an older one.
(setenv "GOTOOLCHAIN" (string-append "go" (getenv "go toolchain")))
(setenv "SSL_CERT_DIR" cert-dir)
(setenv "GOCACHE" "/homeless-shelter")
(setenv "GOPATH" "/homeless-shelter")
(setenv "GOMODCACHE" mod-cache)
(mkdir-p src-dir)
(mkdir-p mod-cache)
(copy-recursively #$source src-dir)
(let* ((go-mods (find-files src-dir "go.mod")))
;; go mod will update the go.mod with transitive dependencies if
;; they are not set, and fail with an error if the file is not
;; writable.
(for-each make-file-writable go-mods)
(with-throw-handler
#t
(lambda _
(for-each
(lambda (go-mod)
(with-directory-excursion (dirname go-mod)
;; go mod download must be run twice - the first
;; fetches direct dependencies and *records*
;; transitive dependencies, the second run fetches
;; the transitive dependencies.
(and
(invoke xgo "mod" "download")
(invoke xgo "mod" "download"))))
go-mods)
#t)
(lambda (key . args)
(display (string-append "Fetching modules failed.\n"
"Here are the results of `go env`:\n"))
(invoke xgo "env")))))))))
(let* ((mod-source (go-mod-source source))
(mod-ref (origin-uri mod-source))
(mod-go (go-mod-go source))
(mod-hash (origin-hash mod-source))
(mod-hash-value (content-hash-value mod-hash))
(mod-hash-algo (content-hash-algorithm mod-hash)))
(mlet* %store-monad ((guile-for-build (package->derivation guile system))
(git-source (git-fetch mod-ref mod-hash-algo
mod-hash-value
#:system system
#:guile guile-for-build)))
(gexp->derivation (or name "go-mod-fetch") (build git-source mod-go)
#:script-name "go-mod-fetch"
#:env-vars
`(("go toolchain" . ,(package-version mod-go)))
#:leaked-env-vars '("http_proxy" "https_proxy"
"LC_ALL" "LC_MESSAGES" "LANG"
"COLUMNS")
#:system system
#:local-build? #t ;don't offload repo cloning
#:recursive? #t
#:hash-algo hash-algo
#:hash hash
#:guile-for-build guile-for-build))))