diff options
| -rw-r--r-- | guix/inferior.scm | 125 | ||||
| -rw-r--r-- | tests/inferior.scm | 22 |
2 files changed, 141 insertions, 6 deletions
diff --git a/guix/inferior.scm b/guix/inferior.scm index af37233a037..5bef9648871 100644 --- a/guix/inferior.scm +++ b/guix/inferior.scm | |||
| @@ -19,9 +19,21 @@ | |||
| 19 | (define-module (guix inferior) | 19 | (define-module (guix inferior) |
| 20 | #:use-module (srfi srfi-9) | 20 | #:use-module (srfi srfi-9) |
| 21 | #:use-module (srfi srfi-9 gnu) | 21 | #:use-module (srfi srfi-9 gnu) |
| 22 | #:use-module ((guix utils) #:select (source-properties->location)) | 22 | #:use-module ((guix utils) |
| 23 | #:select (%current-system | ||
| 24 | source-properties->location | ||
| 25 | call-with-temporary-directory)) | ||
| 26 | #:use-module ((guix store) | ||
| 27 | #:select (nix-server-socket | ||
| 28 | nix-server-major-version | ||
| 29 | nix-server-minor-version | ||
| 30 | store-lift)) | ||
| 31 | #:use-module ((guix derivations) | ||
| 32 | #:select (read-derivation-from-file)) | ||
| 33 | #:use-module (guix gexp) | ||
| 23 | #:use-module (ice-9 match) | 34 | #:use-module (ice-9 match) |
| 24 | #:use-module (ice-9 popen) | 35 | #:use-module (ice-9 popen) |
| 36 | #:use-module (ice-9 binary-ports) | ||
| 25 | #:export (inferior? | 37 | #:export (inferior? |
| 26 | open-inferior | 38 | open-inferior |
| 27 | close-inferior | 39 | close-inferior |
| @@ -36,7 +48,8 @@ | |||
| 36 | inferior-package-synopsis | 48 | inferior-package-synopsis |
| 37 | inferior-package-description | 49 | inferior-package-description |
| 38 | inferior-package-home-page | 50 | inferior-package-home-page |
| 39 | inferior-package-location)) | 51 | inferior-package-location |
| 52 | inferior-package-derivation)) | ||
| 40 | 53 | ||
| 41 | ;;; Commentary: | 54 | ;;; Commentary: |
| 42 | ;;; | 55 | ;;; |
| @@ -123,8 +136,7 @@ equivalent. Return #f if the inferior could not be launched." | |||
| 123 | 136 | ||
| 124 | (set-record-type-printer! <inferior-object> write-inferior-object) | 137 | (set-record-type-printer! <inferior-object> write-inferior-object) |
| 125 | 138 | ||
| 126 | (define (inferior-eval exp inferior) | 139 | (define (read-inferior-response inferior) |
| 127 | "Evaluate EXP in INFERIOR." | ||
| 128 | (define sexp->object | 140 | (define sexp->object |
| 129 | (match-lambda | 141 | (match-lambda |
| 130 | (('value value) | 142 | (('value value) |
| @@ -132,14 +144,21 @@ equivalent. Return #f if the inferior could not be launched." | |||
| 132 | (('non-self-quoting address string) | 144 | (('non-self-quoting address string) |
| 133 | (inferior-object address string)))) | 145 | (inferior-object address string)))) |
| 134 | 146 | ||
| 135 | (write exp (inferior-socket inferior)) | ||
| 136 | (newline (inferior-socket inferior)) | ||
| 137 | (match (read (inferior-socket inferior)) | 147 | (match (read (inferior-socket inferior)) |
| 138 | (('values objects ...) | 148 | (('values objects ...) |
| 139 | (apply values (map sexp->object objects))) | 149 | (apply values (map sexp->object objects))) |
| 140 | (('exception key objects ...) | 150 | (('exception key objects ...) |
| 141 | (apply throw key (map sexp->object objects))))) | 151 | (apply throw key (map sexp->object objects))))) |
| 142 | 152 | ||
| 153 | (define (send-inferior-request exp inferior) | ||
| 154 | (write exp (inferior-socket inferior)) | ||
| 155 | (newline (inferior-socket inferior))) | ||
| 156 | |||
| 157 | (define (inferior-eval exp inferior) | ||
| 158 | "Evaluate EXP in INFERIOR." | ||
| 159 | (send-inferior-request exp inferior) | ||
| 160 | (read-inferior-response inferior)) | ||
| 161 | |||
| 143 | 162 | ||
| 144 | ;;; | 163 | ;;; |
| 145 | ;;; Inferior packages. | 164 | ;;; Inferior packages. |
| @@ -216,3 +235,97 @@ record." | |||
| 216 | (location->source-properties | 235 | (location->source-properties |
| 217 | loc))) | 236 | loc))) |
| 218 | package-location)))) | 237 | package-location)))) |
| 238 | |||
| 239 | (define (proxy client backend) ;adapted from (guix ssh) | ||
| 240 | "Proxy communication between CLIENT and BACKEND until CLIENT closes the | ||
| 241 | connection, at which point CLIENT is closed (both CLIENT and BACKEND must be | ||
| 242 | input/output ports.)" | ||
| 243 | (define (select* read write except) | ||
| 244 | ;; This is a workaround for <https://bugs.gnu.org/30365> in Guile < 2.2.4: | ||
| 245 | ;; since 'select' sometimes returns non-empty sets for no good reason, | ||
| 246 | ;; call 'select' a second time with a zero timeout to filter out incorrect | ||
| 247 | ;; replies. | ||
| 248 | (match (select read write except) | ||
| 249 | ((read write except) | ||
| 250 | (select read write except 0)))) | ||
| 251 | |||
| 252 | ;; Use buffered ports so that 'get-bytevector-some' returns up to the | ||
| 253 | ;; whole buffer like read(2) would--see <https://bugs.gnu.org/30066>. | ||
| 254 | (setvbuf client _IOFBF 65536) | ||
| 255 | (setvbuf backend _IOFBF 65536) | ||
| 256 | |||
| 257 | (let loop () | ||
| 258 | (match (select* (list client backend) '() '()) | ||
| 259 | ((reads () ()) | ||
| 260 | (when (memq client reads) | ||
| 261 | (match (get-bytevector-some client) | ||
| 262 | ((? eof-object?) | ||
| 263 | (close-port client)) | ||
| 264 | (bv | ||
| 265 | (put-bytevector backend bv) | ||
| 266 | (force-output backend)))) | ||
| 267 | (when (memq backend reads) | ||
| 268 | (match (get-bytevector-some backend) | ||
| 269 | (bv | ||
| 270 | (put-bytevector client bv) | ||
| 271 | (force-output client)))) | ||
| 272 | (unless (port-closed? client) | ||
| 273 | (loop)))))) | ||
| 274 | |||
| 275 | (define* (inferior-package-derivation store package | ||
| 276 | #:optional | ||
| 277 | (system (%current-system)) | ||
| 278 | #:key target) | ||
| 279 | "Return the derivation for PACKAGE, an inferior package, built for SYSTEM | ||
| 280 | and cross-built for TARGET if TARGET is true. The inferior corresponding to | ||
| 281 | PACKAGE must be live." | ||
| 282 | ;; Create a named socket in /tmp and let the inferior of PACKAGE connect to | ||
| 283 | ;; it and use it as its store. This ensures the inferior uses the same | ||
| 284 | ;; store, with the same options, the same per-session GC roots, etc. | ||
| 285 | (call-with-temporary-directory | ||
| 286 | (lambda (directory) | ||
| 287 | (chmod directory #o700) | ||
| 288 | (let* ((name (string-append directory "/inferior")) | ||
| 289 | (socket (socket AF_UNIX SOCK_STREAM 0)) | ||
| 290 | (inferior (inferior-package-inferior package)) | ||
| 291 | (major (nix-server-major-version store)) | ||
| 292 | (minor (nix-server-minor-version store)) | ||
| 293 | (proto (logior major minor))) | ||
| 294 | (bind socket AF_UNIX name) | ||
| 295 | (listen socket 1024) | ||
| 296 | (send-inferior-request | ||
| 297 | `(let ((socket (socket AF_UNIX SOCK_STREAM 0))) | ||
| 298 | (connect socket AF_UNIX ,name) | ||
| 299 | |||
| 300 | ;; 'port->connection' appeared in June 2018 and we can hardly | ||
| 301 | ;; emulate it on older versions. Thus fall back to | ||
| 302 | ;; 'open-connection', at the risk of talking to the wrong daemon or | ||
| 303 | ;; having our build result reclaimed (XXX). | ||
| 304 | (let* ((store (if (defined? 'port->connection) | ||
| 305 | (port->connection socket #:version ,proto) | ||
| 306 | (open-connection))) | ||
| 307 | (package (hashv-ref %package-table | ||
| 308 | ,(inferior-package-id package))) | ||
| 309 | (drv ,(if target | ||
| 310 | `(package-cross-derivation store package | ||
| 311 | ,target | ||
| 312 | ,system) | ||
| 313 | `(package-derivation store package | ||
| 314 | ,system)))) | ||
| 315 | (close-connection store) | ||
| 316 | (close-port socket) | ||
| 317 | (derivation-file-name drv))) | ||
| 318 | inferior) | ||
| 319 | (match (accept socket) | ||
| 320 | ((client . address) | ||
| 321 | (proxy client (nix-server-socket store)))) | ||
| 322 | (close-port socket) | ||
| 323 | (read-derivation-from-file (read-inferior-response inferior)))))) | ||
| 324 | |||
| 325 | (define inferior-package->derivation | ||
| 326 | (store-lift inferior-package-derivation)) | ||
| 327 | |||
| 328 | (define-gexp-compiler (package-compiler (package <inferior-package>) system | ||
| 329 | target) | ||
| 330 | ;; Compile PACKAGE for SYSTEM, optionally cross-building for TARGET. | ||
| 331 | (inferior-package->derivation package system #:target target)) | ||
diff --git a/tests/inferior.scm b/tests/inferior.scm index ff5cad4210a..817fcb6c6b5 100644 --- a/tests/inferior.scm +++ b/tests/inferior.scm | |||
| @@ -17,9 +17,13 @@ | |||
| 17 | ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | 17 | ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. |
| 18 | 18 | ||
| 19 | (define-module (test-inferior) | 19 | (define-module (test-inferior) |
| 20 | #:use-module (guix tests) | ||
| 20 | #:use-module (guix inferior) | 21 | #:use-module (guix inferior) |
| 21 | #:use-module (guix packages) | 22 | #:use-module (guix packages) |
| 23 | #:use-module (guix store) | ||
| 24 | #:use-module (guix derivations) | ||
| 22 | #:use-module (gnu packages) | 25 | #:use-module (gnu packages) |
| 26 | #:use-module (gnu packages bootstrap) | ||
| 23 | #:use-module (srfi srfi-1) | 27 | #:use-module (srfi srfi-1) |
| 24 | #:use-module (srfi srfi-64)) | 28 | #:use-module (srfi srfi-64)) |
| 25 | 29 | ||
| @@ -29,6 +33,9 @@ | |||
| 29 | (define %top-builddir | 33 | (define %top-builddir |
| 30 | (dirname (search-path %load-compiled-path "guix.go"))) | 34 | (dirname (search-path %load-compiled-path "guix.go"))) |
| 31 | 35 | ||
| 36 | (define %store | ||
| 37 | (open-connection-for-tests)) | ||
| 38 | |||
| 32 | 39 | ||
| 33 | (test-begin "inferior") | 40 | (test-begin "inferior") |
| 34 | 41 | ||
| @@ -72,4 +79,19 @@ | |||
| 72 | (close-inferior inferior) | 79 | (close-inferior inferior) |
| 73 | result)))) | 80 | result)))) |
| 74 | 81 | ||
| 82 | (test-equal "inferior-package-derivation" | ||
| 83 | (map derivation-file-name | ||
| 84 | (list (package-derivation %store %bootstrap-guile "x86_64-linux") | ||
| 85 | (package-derivation %store %bootstrap-guile "armhf-linux"))) | ||
| 86 | (let* ((inferior (open-inferior %top-builddir | ||
| 87 | #:command "scripts/guix")) | ||
| 88 | (packages (inferior-packages inferior)) | ||
| 89 | (guile (find (lambda (package) | ||
| 90 | (string=? (package-name %bootstrap-guile) | ||
| 91 | (inferior-package-name package))) | ||
| 92 | packages))) | ||
| 93 | (map derivation-file-name | ||
| 94 | (list (inferior-package-derivation %store guile "x86_64-linux") | ||
| 95 | (inferior-package-derivation %store guile "armhf-linux"))))) | ||
| 96 | |||
| 75 | (test-end "inferior") | 97 | (test-end "inferior") |
