gnu: Add r-pore.
[jackhill/guix/guix.git] / guix / inferior.scm
CommitLineData
2ca299ca
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2018 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix inferior)
20 #:use-module (srfi srfi-9)
21 #:use-module (srfi srfi-9 gnu)
7e1d2290 22 #:use-module ((guix utils) #:select (source-properties->location))
2ca299ca
LC
23 #:use-module (ice-9 match)
24 #:use-module (ice-9 popen)
25 #:export (inferior?
26 open-inferior
27 close-inferior
28 inferior-eval
29 inferior-object?
30
31 inferior-package?
32 inferior-package-name
33 inferior-package-version
34
35 inferior-packages
36 inferior-package-synopsis
7e1d2290
LC
37 inferior-package-description
38 inferior-package-home-page
39 inferior-package-location))
2ca299ca
LC
40
41;;; Commentary:
42;;;
43;;; This module provides a way to spawn Guix "inferior" processes and to talk
44;;; to them. It allows us, from one instance of Guix, to interact with
45;;; another instance of Guix coming from a different commit.
46;;;
47;;; Code:
48
49;; Inferior Guix process.
50(define-record-type <inferior>
51 (inferior pid socket version)
52 inferior?
53 (pid inferior-pid)
54 (socket inferior-socket)
55 (version inferior-version)) ;REPL protocol version
56
57(define (inferior-pipe directory command)
58 "Return an input/output pipe on the Guix instance in DIRECTORY. This runs
59'DIRECTORY/COMMAND repl' if it exists, or falls back to some other method if
60it's an old Guix."
61 (let ((pipe (with-error-to-port (%make-void-port "w")
62 (lambda ()
63 (open-pipe* OPEN_BOTH
64 (string-append directory "/" command)
65 "repl" "-t" "machine")))))
66 (if (eof-object? (peek-char pipe))
67 (begin
68 (close-pipe pipe)
69
70 ;; Older versions of Guix didn't have a 'guix repl' command, so
71 ;; emulate it.
72 (open-pipe* OPEN_BOTH "guile"
73 "-L" (string-append directory "/share/guile/site/"
74 (effective-version))
75 "-C" (string-append directory "/share/guile/site/"
76 (effective-version))
77 "-C" (string-append directory "/lib/guile/"
78 (effective-version) "/site-ccache")
79 "-c"
80 (object->string
81 `(begin
82 (primitive-load ,(search-path %load-path
83 "guix/scripts/repl.scm"))
84 ((@ (guix scripts repl) machine-repl))))))
85 pipe)))
86
87(define* (open-inferior directory #:key (command "bin/guix"))
88 "Open the inferior Guix in DIRECTORY, running 'DIRECTORY/COMMAND repl' or
89equivalent. Return #f if the inferior could not be launched."
90 (define pipe
91 (inferior-pipe directory command))
92
a81b59b1
LC
93 (cond-expand
94 ((and guile-2 (not guile-2.2)) #t)
95 (else (setvbuf pipe 'line)))
96
2ca299ca
LC
97 (match (read pipe)
98 (('repl-version 0 rest ...)
99 (let ((result (inferior 'pipe pipe (cons 0 rest))))
100 (inferior-eval '(use-modules (guix)) result)
101 (inferior-eval '(use-modules (gnu)) result)
102 (inferior-eval '(define %package-table (make-hash-table))
103 result)
104 result))
105 (_
106 #f)))
107
108(define (close-inferior inferior)
109 "Close INFERIOR."
110 (close-pipe (inferior-socket inferior)))
111
112;; Non-self-quoting object of the inferior.
113(define-record-type <inferior-object>
114 (inferior-object address appearance)
115 inferior-object?
116 (address inferior-object-address)
117 (appearance inferior-object-appearance))
118
119(define (write-inferior-object object port)
120 (match object
121 (($ <inferior-object> _ appearance)
122 (format port "#<inferior-object ~a>" appearance))))
123
124(set-record-type-printer! <inferior-object> write-inferior-object)
125
126(define (inferior-eval exp inferior)
127 "Evaluate EXP in INFERIOR."
128 (define sexp->object
129 (match-lambda
130 (('value value)
131 value)
132 (('non-self-quoting address string)
133 (inferior-object address string))))
134
135 (write exp (inferior-socket inferior))
136 (newline (inferior-socket inferior))
137 (match (read (inferior-socket inferior))
138 (('values objects ...)
139 (apply values (map sexp->object objects)))
140 (('exception key objects ...)
141 (apply throw key (map sexp->object objects)))))
142
143\f
144;;;
145;;; Inferior packages.
146;;;
147
148(define-record-type <inferior-package>
149 (inferior-package inferior name version id)
150 inferior-package?
151 (inferior inferior-package-inferior)
152 (name inferior-package-name)
153 (version inferior-package-version)
154 (id inferior-package-id))
155
156(define (write-inferior-package package port)
157 (match package
158 (($ <inferior-package> _ name version)
159 (format port "#<inferior-package ~a@~a ~a>"
160 name version
161 (number->string (object-address package) 16)))))
162
163(set-record-type-printer! <inferior-package> write-inferior-package)
164
165(define (inferior-packages inferior)
166 "Return the list of packages known to INFERIOR."
167 (let ((result (inferior-eval
168 '(fold-packages (lambda (package result)
169 (let ((id (object-address package)))
170 (hashv-set! %package-table id package)
171 (cons (list (package-name package)
172 (package-version package)
173 id)
174 result)))
175 '())
176 inferior)))
177 (map (match-lambda
178 ((name version id)
179 (inferior-package inferior name version id)))
180 result)))
181
182(define (inferior-package-field package getter)
183 "Return the field of PACKAGE, an inferior package, accessed with GETTER."
184 (let ((inferior (inferior-package-inferior package))
185 (id (inferior-package-id package)))
186 (inferior-eval `(,getter (hashv-ref %package-table ,id))
187 inferior)))
188
189(define* (inferior-package-synopsis package #:key (translate? #t))
190 "Return the Texinfo synopsis of PACKAGE, an inferior package. When
191TRANSLATE? is true, translate it to the current locale's language."
192 (inferior-package-field package
193 (if translate?
194 '(compose (@ (guix ui) P_) package-synopsis)
195 'package-synopsis)))
196
197(define* (inferior-package-description package #:key (translate? #t))
198 "Return the Texinfo description of PACKAGE, an inferior package. When
199TRANSLATE? is true, translate it to the current locale's language."
200 (inferior-package-field package
201 (if translate?
202 '(compose (@ (guix ui) P_) package-description)
203 'package-description)))
7e1d2290
LC
204
205(define (inferior-package-home-page package)
206 "Return the home page of PACKAGE."
207 (inferior-package-field package 'package-home-page))
208
209(define (inferior-package-location package)
210 "Return the source code location of PACKAGE, either #f or a <location>
211record."
212 (source-properties->location
213 (inferior-package-field package
214 '(compose (lambda (loc)
215 (and loc
216 (location->source-properties
217 loc)))
218 package-location))))