summaryrefslogtreecommitdiff
path: root/guix/inferior.scm
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2018-07-10 14:18:36 +0200
committerLudovic Courtès <ludo@gnu.org>2018-07-13 17:28:39 +0200
commit2ca299caf64489f4e1e665ec1158fb0309b0b565 (patch)
tree29a657a53161916cc64b6d0365c712501d666919 /guix/inferior.scm
parentbc6e291ef0b3c71c07e50d88d7764e5dd334e8b1 (diff)
downloadgnu-guix-2ca299caf64489f4e1e665ec1158fb0309b0b565.tar
gnu-guix-2ca299caf64489f4e1e665ec1158fb0309b0b565.tar.gz
Add (guix inferior) and (guix scripts repl).
* guix/inferior.scm, guix/scripts/repl.scm, tests/inferior.scm: New files. * Makefile.am (MODULES): Add 'guix/scripts/repl.scm' and 'guix/inferior.scm'. (SCM_TESTS): Add 'tests/inferior.scm'. * doc/guix.texi (Invoking guix repl): New node.
Diffstat (limited to 'guix/inferior.scm')
-rw-r--r--guix/inferior.scm197
1 files changed, 197 insertions, 0 deletions
diff --git a/guix/inferior.scm b/guix/inferior.scm
new file mode 100644
index 0000000000..629c2c4313
--- /dev/null
+++ b/guix/inferior.scm
@@ -0,0 +1,197 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2018 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; 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 inferior)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-9 gnu)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 popen)
+ #:export (inferior?
+ open-inferior
+ close-inferior
+ inferior-eval
+ inferior-object?
+
+ inferior-package?
+ inferior-package-name
+ inferior-package-version
+
+ inferior-packages
+ inferior-package-synopsis
+ inferior-package-description))
+
+;;; Commentary:
+;;;
+;;; This module provides a way to spawn Guix "inferior" processes and to talk
+;;; to them. It allows us, from one instance of Guix, to interact with
+;;; another instance of Guix coming from a different commit.
+;;;
+;;; Code:
+
+;; Inferior Guix process.
+(define-record-type <inferior>
+ (inferior pid socket version)
+ inferior?
+ (pid inferior-pid)
+ (socket inferior-socket)
+ (version inferior-version)) ;REPL protocol version
+
+(define (inferior-pipe directory command)
+ "Return an input/output pipe on the Guix instance in DIRECTORY. This runs
+'DIRECTORY/COMMAND repl' if it exists, or falls back to some other method if
+it's an old Guix."
+ (let ((pipe (with-error-to-port (%make-void-port "w")
+ (lambda ()
+ (open-pipe* OPEN_BOTH
+ (string-append directory "/" command)
+ "repl" "-t" "machine")))))
+ (if (eof-object? (peek-char pipe))
+ (begin
+ (close-pipe pipe)
+
+ ;; Older versions of Guix didn't have a 'guix repl' command, so
+ ;; emulate it.
+ (open-pipe* OPEN_BOTH "guile"
+ "-L" (string-append directory "/share/guile/site/"
+ (effective-version))
+ "-C" (string-append directory "/share/guile/site/"
+ (effective-version))
+ "-C" (string-append directory "/lib/guile/"
+ (effective-version) "/site-ccache")
+ "-c"
+ (object->string
+ `(begin
+ (primitive-load ,(search-path %load-path
+ "guix/scripts/repl.scm"))
+ ((@ (guix scripts repl) machine-repl))))))
+ pipe)))
+
+(define* (open-inferior directory #:key (command "bin/guix"))
+ "Open the inferior Guix in DIRECTORY, running 'DIRECTORY/COMMAND repl' or
+equivalent. Return #f if the inferior could not be launched."
+ (define pipe
+ (inferior-pipe directory command))
+
+ (setvbuf pipe _IOLBF)
+ (match (read pipe)
+ (('repl-version 0 rest ...)
+ (let ((result (inferior 'pipe pipe (cons 0 rest))))
+ (inferior-eval '(use-modules (guix)) result)
+ (inferior-eval '(use-modules (gnu)) result)
+ (inferior-eval '(define %package-table (make-hash-table))
+ result)
+ result))
+ (_
+ #f)))
+
+(define (close-inferior inferior)
+ "Close INFERIOR."
+ (close-pipe (inferior-socket inferior)))
+
+;; Non-self-quoting object of the inferior.
+(define-record-type <inferior-object>
+ (inferior-object address appearance)
+ inferior-object?
+ (address inferior-object-address)
+ (appearance inferior-object-appearance))
+
+(define (write-inferior-object object port)
+ (match object
+ (($ <inferior-object> _ appearance)
+ (format port "#<inferior-object ~a>" appearance))))
+
+(set-record-type-printer! <inferior-object> write-inferior-object)
+
+(define (inferior-eval exp inferior)
+ "Evaluate EXP in INFERIOR."
+ (define sexp->object
+ (match-lambda
+ (('value value)
+ value)
+ (('non-self-quoting address string)
+ (inferior-object address string))))
+
+ (write exp (inferior-socket inferior))
+ (newline (inferior-socket inferior))
+ (match (read (inferior-socket inferior))
+ (('values objects ...)
+ (apply values (map sexp->object objects)))
+ (('exception key objects ...)
+ (apply throw key (map sexp->object objects)))))
+
+
+;;;
+;;; Inferior packages.
+;;;
+
+(define-record-type <inferior-package>
+ (inferior-package inferior name version id)
+ inferior-package?
+ (inferior inferior-package-inferior)
+ (name inferior-package-name)
+ (version inferior-package-version)
+ (id inferior-package-id))
+
+(define (write-inferior-package package port)
+ (match package
+ (($ <inferior-package> _ name version)
+ (format port "#<inferior-package ~a@~a ~a>"
+ name version
+ (number->string (object-address package) 16)))))
+
+(set-record-type-printer! <inferior-package> write-inferior-package)
+
+(define (inferior-packages inferior)
+ "Return the list of packages known to INFERIOR."
+ (let ((result (inferior-eval
+ '(fold-packages (lambda (package result)
+ (let ((id (object-address package)))
+ (hashv-set! %package-table id package)
+ (cons (list (package-name package)
+ (package-version package)
+ id)
+ result)))
+ '())
+ inferior)))
+ (map (match-lambda
+ ((name version id)
+ (inferior-package inferior name version id)))
+ result)))
+
+(define (inferior-package-field package getter)
+ "Return the field of PACKAGE, an inferior package, accessed with GETTER."
+ (let ((inferior (inferior-package-inferior package))
+ (id (inferior-package-id package)))
+ (inferior-eval `(,getter (hashv-ref %package-table ,id))
+ inferior)))
+
+(define* (inferior-package-synopsis package #:key (translate? #t))
+ "Return the Texinfo synopsis of PACKAGE, an inferior package. When
+TRANSLATE? is true, translate it to the current locale's language."
+ (inferior-package-field package
+ (if translate?
+ '(compose (@ (guix ui) P_) package-synopsis)
+ 'package-synopsis)))
+
+(define* (inferior-package-description package #:key (translate? #t))
+ "Return the Texinfo description of PACKAGE, an inferior package. When
+TRANSLATE? is true, translate it to the current locale's language."
+ (inferior-package-field package
+ (if translate?
+ '(compose (@ (guix ui) P_) package-description)
+ 'package-description)))