aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--doc/guix.texi18
-rw-r--r--guix/build-system/go.scm132
-rw-r--r--guix/build/go-build-system.scm217
4 files changed, 369 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index d054f78791..efbd07a351 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,6 +80,7 @@ MODULES = \
guix/build-system/dub.scm \
guix/build-system/emacs.scm \
guix/build-system/font.scm \
+ guix/build-system/go.scm \
guix/build-system/meson.scm \
guix/build-system/minify.scm \
guix/build-system/asdf.scm \
@@ -111,6 +112,7 @@ MODULES = \
guix/build/meson-build-system.scm \
guix/build/minify-build-system.scm \
guix/build/font-build-system.scm \
+ guix/build/go-build-system.scm \
guix/build/asdf-build-system.scm \
guix/build/git.scm \
guix/build/hg.scm \
diff --git a/doc/guix.texi b/doc/guix.texi
index 0940ea4e71..6018198567 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -3576,6 +3576,24 @@ debugging information''), which roughly means that code is compiled with
@code{-O2 -g}, as is the case for Autoconf-based packages by default.
@end defvr
+@defvr {Scheme Variable} go-build-system
+This variable is exported by @code{(guix build-system go)}. It
+implements a build procedure for Go packages using the standard
+@url{https://golang.org/cmd/go/#hdr-Compile_packages_and_dependencies,
+Go build mechanisms}.
+
+The user is expected to provide a value for the key @code{#:import-path}
+and, in some cases, @code{#:unpack-path}. The
+@url{https://golang.org/doc/code.html#ImportPaths, import path}
+corresponds to the filesystem path expected by the package's build
+scripts and any referring packages, and provides a unique way to
+refer to a Go package. It is typically based on a combination of the
+package source code's remote URI and filesystem hierarchy structure. In
+some cases, you will need to unpack the package's source code to a
+different directory structure than the one indicated by the import path,
+and @code{#:unpack-path} should be used in such cases.
+@end defvr
+
@defvr {Scheme Variable} glib-or-gtk-build-system
This variable is exported by @code{(guix build-system glib-or-gtk)}. It
is intended for use with packages making use of GLib or GTK+.
diff --git a/guix/build-system/go.scm b/guix/build-system/go.scm
new file mode 100644
index 0000000000..43599df6f4
--- /dev/null
+++ b/guix/build-system/go.scm
@@ -0,0 +1,132 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2016 Petter <petter@mykolab.ch>
+;;; Copyright © 2017 Leo Famulari <leo@famulari.name>
+;;;
+;;; 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 <http://www.gnu.org/licenses/>.
+
+(define-module (guix build-system go)
+ #:use-module (guix utils)
+ #:use-module (guix derivations)
+ #:use-module (guix search-paths)
+ #:use-module (guix build-system)
+ #:use-module (guix build-system gnu)
+ #:use-module (guix packages)
+ #:use-module (ice-9 match)
+ #:export (%go-build-system-modules
+ go-build
+ go-build-system))
+
+;; Commentary:
+;;
+;; Standard build procedure for packages using the Go build system. It is
+;; implemented as an extension of 'gnu-build-system'.
+;;
+;; Code:
+
+(define %go-build-system-modules
+ ;; Build-side modules imported and used by default.
+ `((guix build go-build-system)
+ ,@%gnu-build-system-modules))
+
+(define (default-go)
+ ;; Lazily resolve the binding to avoid a circular dependency.
+ (let ((go (resolve-interface '(gnu packages golang))))
+ (module-ref go 'go)))
+
+(define* (lower name
+ #:key source inputs native-inputs outputs system target
+ (go (default-go))
+ #:allow-other-keys
+ #:rest arguments)
+ "Return a bag for NAME."
+ (define private-keywords
+ '(#:source #:target #:go #:inputs #:native-inputs))
+
+ (and (not target) ;XXX: no cross-compilation
+ (bag
+ (name name)
+ (system system)
+ (host-inputs `(,@(if source
+ `(("source" ,source))
+ '())
+ ,@inputs
+
+ ;; Keep the standard inputs of 'gnu-build-system'.
+ ,@(standard-packages)))
+ (build-inputs `(("go" ,go)
+ ,@native-inputs))
+ (outputs outputs)
+ (build go-build)
+ (arguments (strip-keyword-arguments private-keywords arguments)))))
+
+(define* (go-build store name inputs
+ #:key
+ (phases '(@ (guix build go-build-system)
+ %standard-phases))
+ (outputs '("out"))
+ (search-paths '())
+ (import-path "")
+ (unpack-path "")
+ (tests? #t)
+ (system (%current-system))
+ (guile #f)
+ (imported-modules %go-build-system-modules)
+ (modules '((guix build go-build-system)
+ (guix build utils))))
+ (define builder
+ `(begin
+ (use-modules ,@modules)
+ (go-build #:name ,name
+ #:source ,(match (assoc-ref inputs "source")
+ (((? derivation? source))
+ (derivation->output-path source))
+ ((source)
+ source)
+ (source
+ source))
+ #:system ,system
+ #:phases ,phases
+ #:outputs %outputs
+ #:search-paths ',(map search-path-specification->sexp
+ search-paths)
+ #:import-path ,import-path
+ #:unpack-path ,unpack-path
+ #:tests? ,tests?
+ #:inputs %build-inputs)))
+
+ (define guile-for-build
+ (match guile
+ ((? package?)
+ (package-derivation store guile system #:graft? #f))
+ (#f ; the default
+ (let* ((distro (resolve-interface '(gnu packages commencement)))
+ (guile (module-ref distro 'guile-final)))
+ (package-derivation store guile system
+ #:graft? #f)))))
+
+ (build-expression->derivation store name builder
+ #:inputs inputs
+ #:system system
+ #:modules imported-modules
+ #:outputs outputs
+ #:guile-for-build guile-for-build))
+
+(define go-build-system
+ (build-system
+ (name 'go)
+ (description
+ "Build system for Go programs")
+ (lower lower)))
diff --git a/guix/build/go-build-system.scm b/guix/build/go-build-system.scm
new file mode 100644
index 0000000000..7f04e3db8c
--- /dev/null
+++ b/guix/build/go-build-system.scm
@@ -0,0 +1,217 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2016 Petter <petter@mykolab.ch>
+;;; Copyright © 2017 Leo Famulari <leo@famulari.name>
+;;;
+;;; 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 <http://www.gnu.org/licenses/>.
+
+(define-module (guix build go-build-system)
+ #:use-module ((guix build gnu-build-system) #:prefix gnu:)
+ #:use-module (guix build utils)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:export (%standard-phases
+ go-build))
+
+;; Commentary:
+;;
+;; Build procedures for Go packages. This is the builder-side code.
+;;
+;; Software written in Go is either a 'package' (i.e. library) or 'command'
+;; (i.e. executable). Both types can be built with either the `go build` or `go
+;; install` commands. However, `go build` discards the result of the build
+;; process for Go libraries, so we use `go install`, which preserves the
+;; results. [0]
+
+;; Go software is developed and built within a particular filesystem hierarchy
+;; structure called a 'workspace' [1]. This workspace is found by Go
+;; via the GOPATH environment variable. Typically, all Go source code
+;; and compiled objects are kept in a single workspace, but it is
+;; possible for GOPATH to contain a list of directories, and that is
+;; what we do in this go-build-system. [2]
+;;
+;; Go software, whether a package or a command, is uniquely named using
+;; an 'import path'. The import path is based on the URL of the
+;; software's source. Since most source code is provided over the
+;; internet, the import path is typically a combination of the remote
+;; URL and the source repository's filesystem structure. For example,
+;; the Go port of the common `du` command is hosted on github.com, at
+;; <https://github.com/calmh/du>. Thus, the import path is
+;; <github.com/calmh/du>. [3]
+;;
+;; It may be possible to programatically guess a package's import path
+;; based on the source URL, but we don't try that in this revision of
+;; the go-build-system.
+;;
+;; Modules of modular Go libraries are named uniquely with their
+;; filesystem paths. For example, the supplemental but "standardized"
+;; libraries developed by the Go upstream developers are available at
+;; <https://golang.org/x/{net,text,crypto, et cetera}>. The Go IPv4
+;; library's import path is <golang.org/x/net/ipv4>. The source of
+;; such modular libraries must be unpacked at the top-level of the
+;; filesystem structure of the library. So the IPv4 library should be
+;; unpacked to <golang.org/x/net>. This is handled in the
+;; go-build-system with the optional #:unpack-path key.
+;;
+;; In general, Go software is built using a standardized build mechanism
+;; that does not require any build scripts like Makefiles. This means
+;; that all modules of modular libraries cannot be built with a single
+;; command. Each module must be built individually. This complicates
+;; certain cases, and these issues are currently resolved by creating a
+;; filesystem union of the required modules of such libraries. I think
+;; this could be improved in future revisions of the go-build-system.
+;;
+;; [0] `go build`:
+;; https://golang.org/cmd/go/#hdr-Compile_packages_and_dependencies
+;; `go install`:
+;; https://golang.org/cmd/go/#hdr-Compile_and_install_packages_and_dependencies
+;; [1] Go workspace example, from <https://golang.org/doc/code.html#Workspaces>:
+;; bin/
+;; hello # command executable
+;; outyet # command executable
+;; pkg/
+;; linux_amd64/
+;; github.com/golang/example/
+;; stringutil.a # package object
+;; src/
+;; github.com/golang/example/
+;; .git/ # Git repository metadata
+;; hello/
+;; hello.go # command source
+;; outyet/
+;; main.go # command source
+;; main_test.go # test source
+;; stringutil/
+;; reverse.go # package source
+;; reverse_test.go # test source
+;; golang.org/x/image/
+;; .git/ # Git repository metadata
+;; bmp/
+;; reader.go # package source
+;; writer.go # package source
+;; ... (many more repositories and packages omitted) ...
+;;
+;; [2] https://golang.org/doc/code.html#GOPATH
+;; [3] https://golang.org/doc/code.html#ImportPaths
+;;
+;; Code:
+
+(define* (unpack #:key source import-path unpack-path #:allow-other-keys)
+ "Unpack SOURCE in the UNPACK-PATH, or the IMPORT-PATH is the UNPACK-PATH is
+unset. When SOURCE is a directory, copy it instead of unpacking."
+ (if (string-null? import-path)
+ ((display "WARNING: The Go import path is unset.\n")))
+ (if (string-null? unpack-path)
+ (set! unpack-path import-path))
+ (mkdir "src")
+ (let ((dest (string-append "src/" unpack-path)))
+ (mkdir-p dest)
+ (if (file-is-directory? source)
+ (begin
+ (copy-recursively source dest #:keep-mtime? #t)
+ #t)
+ (if (string-suffix? ".zip" source)
+ (zero? (system* "unzip" "-d" dest source))
+ (zero? (system* "tar" "-C" dest "-xvf" source))))))
+
+(define* (install-source #:key outputs #:allow-other-keys)
+ "Install the source code to the output directory."
+ (let* ((out (assoc-ref outputs "out"))
+ (source "src")
+ (dest (string-append out "/" source)))
+ (copy-recursively source dest #:keep-mtime? #t)
+ #t))
+
+(define (go-package? name)
+ (string-prefix? "go-" name))
+
+(define (go-inputs inputs)
+ "Return the alist of INPUTS that are Go software."
+ ;; XXX This should not check the file name of the store item. Instead we
+ ;; should pass, from the host side, the list of inputs that are packages using
+ ;; the go-build-system.
+ (alist-delete "go" ; Exclude the Go compiler
+ (alist-delete "source" ; Exclude the source code of the package being built
+ (filter (match-lambda
+ ((label . directory)
+ (go-package? ((compose package-name->name+version
+ strip-store-file-name)
+ directory)))
+ (_ #f))
+ inputs))))
+
+(define* (setup-environment #:key inputs outputs #:allow-other-keys)
+ "Export the variables GOPATH and GOBIN, which are based on INPUTS and OUTPUTS,
+respectively."
+ (let ((out (assoc-ref outputs "out")))
+ ;; GOPATH is where Go looks for the source code of the build's dependencies.
+ (set-path-environment-variable "GOPATH"
+ ;; XXX Matching "." hints that we could do
+ ;; something simpler here...
+ (list ".")
+ (match (go-inputs inputs)
+ (((_ . dir) ...)
+ dir)))
+
+ ;; Add the source code of the package being built to GOPATH.
+ (if (getenv "GOPATH")
+ (setenv "GOPATH" (string-append (getcwd) ":" (getenv "GOPATH")))
+ (setenv "GOPATH" (getcwd)))
+ ;; Where to install compiled executable files ('commands' in Go parlance').
+ (setenv "GOBIN" out)
+ #t))
+
+(define* (build #:key import-path #:allow-other-keys)
+ "Build the package named by IMPORT-PATH."
+ (or
+ (zero? (system* "go" "install"
+ "-v" ; print the name of packages as they are compiled
+ "-x" ; print each command as it is invoked
+ import-path))
+ (begin
+ (display (string-append "Building '" import-path "' failed.\n"
+ "Here are the results of `go env`:\n"))
+ (system* "go" "env")
+ #f)))
+
+(define* (check #:key tests? import-path #:allow-other-keys)
+ "Run the tests for the package named by IMPORT-PATH."
+ (if tests?
+ (zero? (system* "go" "test" import-path))))
+
+(define* (install #:key outputs #:allow-other-keys)
+ "Install the compiled libraries. `go install` installs these files to
+$GOPATH/pkg, so we have to copy them into the output direcotry manually.
+Compiled executable files should have already been installed to the store based
+on $GOBIN in the build phase."
+ (when (file-exists? "pkg")
+ (copy-recursively "pkg" (string-append (assoc-ref outputs "out") "/pkg")))
+ #t)
+
+(define %standard-phases
+ (modify-phases gnu:%standard-phases
+ (delete 'configure)
+ (delete 'patch-generated-file-shebangs)
+ (replace 'unpack unpack)
+ (add-after 'unpack 'install-source install-source)
+ (add-before 'build 'setup-environment setup-environment)
+ (replace 'build build)
+ (replace 'check check)
+ (replace 'install install)))
+
+(define* (go-build #:key inputs (phases %standard-phases)
+ #:allow-other-keys #:rest args)
+ "Build the given Go package, applying all of PHASES in order."
+ (apply gnu:gnu-build #:inputs inputs #:phases phases args))