guix-play/gnu/system/vm.scm
Ludovic Courtès 4c0f0673b2 gnu: `expression->derivation-in-linux-vm' accepts files as inputs.
* gnu/system/vm.scm (expression->derivation-in-linux-vm): Accept items
  in INPUTS that refer to a file instead of a package.
2013-08-31 23:02:18 +02:00

294 lines
12 KiB
Scheme
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013 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 (gnu system vm)
#:use-module (guix store)
#:use-module (guix derivations)
#:use-module (guix packages)
#:use-module ((gnu packages base) #:select (%final-inputs guile-final))
#:use-module (gnu packages qemu)
#:use-module (gnu packages parted)
#:use-module (gnu packages grub)
#:use-module (gnu packages linux)
#:use-module (gnu packages linux-initrd)
#:use-module ((gnu packages make-bootstrap)
#:select (%guile-static-stripped))
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:use-module (ice-9 match)
#:export (expression->derivation-in-linux-vm
qemu-image))
;;; Commentary:
;;;
;;; Tools to evaluate build expressions within virtual machines.
;;;
;;; Code:
(define* (expression->derivation-in-linux-vm store name exp
#:key
(system (%current-system))
(inputs '())
(linux linux-libre)
(initrd qemu-initrd)
(qemu qemu/smb-shares)
(env-vars '())
(modules '())
(guile-for-build
(%guile-for-build))
(make-disk-image? #f)
(references-graphs #f)
(disk-image-size
(* 100 (expt 2 20))))
"Evaluate EXP in a QEMU virtual machine running LINUX with INITRD. In the
virtual machine, EXP has access to all of INPUTS from the store; it should put
its output files in the `/xchg' directory, which is copied to the derivation's
output when the VM terminates.
When MAKE-DISK-IMAGE? is true, then create a QEMU disk image of
DISK-IMAGE-SIZE bytes and return it.
When REFERENCES-GRAPHS is true, it must be a list of file name/store path
pairs, as for `derivation'. The files containing the reference graphs are
made available under the /xchg CIFS share."
(define input-alist
(map (match-lambda
((input (? package? package))
`(,input . ,(package-output store package "out" system)))
((input (? package? package) sub-drv)
`(,input . ,(package-output store package sub-drv system)))
((input (and (? string?) (? store-path?) file))
`(,input . ,file)))
inputs))
(define exp*
;; EXP, but with INPUTS available.
`(let ((%build-inputs ',input-alist))
,exp))
(define builder
;; Code that launches the VM that evaluates EXP.
`(let ()
(use-modules (guix build utils)
(srfi srfi-1)
(ice-9 rdelim))
(let ((out (assoc-ref %outputs "out"))
(cu (string-append (assoc-ref %build-inputs "coreutils")
"/bin"))
(qemu (string-append (assoc-ref %build-inputs "qemu")
"/bin/qemu-system-"
(car (string-split ,system #\-))))
(img (string-append (assoc-ref %build-inputs "qemu")
"/bin/qemu-img"))
(linux (string-append (assoc-ref %build-inputs "linux")
"/bzImage"))
(initrd (string-append (assoc-ref %build-inputs "initrd")
"/initrd"))
(builder (assoc-ref %build-inputs "builder")))
;; XXX: QEMU uses "rm -rf" when it's done to remove the temporary SMB
;; directory, so it really needs `rm' in $PATH.
(setenv "PATH" cu)
,(if make-disk-image?
`(zero? (system* img "create" "image.qcow2"
,(number->string disk-image-size)))
'(begin))
(mkdir "xchg")
;; Copy the reference-graph files under xchg/ so EXP can access it.
(begin
,@(match references-graphs
(((graph-files . _) ...)
(map (lambda (file)
`(copy-file ,file
,(string-append "xchg/" file)))
graph-files))
(#f '())))
(and (zero?
(system* qemu "-nographic" "-no-reboot"
"-net" "nic,model=e1000"
"-net" (string-append "user,smb=" (getcwd))
"-kernel" linux
"-initrd" initrd
"-append" (string-append "console=ttyS0 --load="
builder)
,@(if make-disk-image?
'("-hda" "image.qcow2")
'())))
,(if make-disk-image?
'(copy-file "image.qcow2" ; XXX: who mkdir'd OUT?
out)
'(begin
(mkdir out)
(copy-recursively "xchg" out)))))))
(let ((user-builder (add-text-to-store store "builder-in-linux-vm"
(object->string exp*)
'()))
(->drv (cut package-derivation store <> system))
(coreutils (car (assoc-ref %final-inputs "coreutils"))))
(build-expression->derivation store name system builder
`(("qemu" ,(->drv qemu))
("linux" ,(->drv linux))
("initrd" ,(->drv initrd))
("coreutils" ,(->drv coreutils))
("builder" ,user-builder)
,@(map (match-lambda
((name (? package? package)
sub-drv ...)
`(,name ,(->drv package)
,@sub-drv))
((name (? string? file))
`(,name ,file)))
inputs))
#:env-vars env-vars
#:modules (delete-duplicates
`((guix build utils)
,@modules))
#:guile-for-build guile-for-build
#:references-graphs references-graphs)))
(define* (qemu-image store #:key
(name "qemu-image")
(system (%current-system))
(disk-image-size (* 100 (expt 2 20)))
(linux linux-libre)
(initrd qemu-initrd)
(inputs '()))
"Return a bootable, stand-alone QEMU image."
(expression->derivation-in-linux-vm
store "qemu-image"
`(let ((parted (string-append (assoc-ref %build-inputs "parted")
"/sbin/parted"))
(mkfs (string-append (assoc-ref %build-inputs "e2fsprogs")
"/sbin/mkfs.ext3"))
(grub (string-append (assoc-ref %build-inputs "grub")
"/sbin/grub-install"))
(umount (string-append (assoc-ref %build-inputs "util-linux")
"/bin/umount")) ; XXX: add to Guile
(initrd (string-append (assoc-ref %build-inputs "initrd")
"/initrd"))
(linux (string-append (assoc-ref %build-inputs "linux")
"/bzImage"))
(makedev (lambda (major minor)
(+ (* major 256) minor))))
;; GRUB is full of shell scripts.
(setenv "PATH"
(string-append (dirname grub) ":"
(assoc-ref %build-inputs "coreutils") "/bin:"
(assoc-ref %build-inputs "findutils") "/bin:"
(assoc-ref %build-inputs "sed") "/bin:"
(assoc-ref %build-inputs "grep") "/bin:"
(assoc-ref %build-inputs "gawk") "/bin"))
(display "creating partition table...\n")
(mknod "/dev/vda" 'block-special #o644 (makedev 8 0))
(and (zero? (system* parted "/dev/vda" "mklabel" "msdos"
"mkpart" "primary" "ext2" "1MiB"
,(format #f "~aB"
(- disk-image-size
(* 5 (expt 2 20))))))
(begin
(display "creating ext3 partition...\n")
(mknod "/dev/vda1" 'block-special #o644 (makedev 8 1))
(and (zero? (system* mkfs "-F" "/dev/vda1"))
(begin
(display "mounting partition...\n")
(mkdir "/fs")
(mount "/dev/vda1" "/fs" "ext3")
(mkdir "/fs/boot")
(mkdir "/fs/boot/grub")
(copy-file linux "/fs/boot/bzImage")
(copy-file initrd "/fs/boot/initrd")
(call-with-output-file "/fs/boot/grub/grub.cfg"
(lambda (p)
(display "
set default=1
set timeout=5
search.file /boot/bzImage
menuentry \"Boot-to-Guile! (GNU System technology preview)\" {
linux /boot/bzImage --repl
initrd /boot/initrd
}" p)))
(and (zero?
(system* grub "--no-floppy"
"--boot-directory" "/fs/boot"
"/dev/vda"))
(zero?
(system* umount "/fs"))
(reboot)))))))
#:system system
#:inputs `(("parted" ,parted)
("grub" ,grub)
("e2fsprogs" ,e2fsprogs)
("linux" ,linux-libre)
("initrd" ,qemu-initrd)
;; For shell scripts.
("sed" ,(car (assoc-ref %final-inputs "sed")))
("grep" ,(car (assoc-ref %final-inputs "grep")))
("coreutils" ,(car (assoc-ref %final-inputs "coreutils")))
("findutils" ,(car (assoc-ref %final-inputs "findutils")))
("gawk" ,(car (assoc-ref %final-inputs "gawk")))
("util-linux" ,util-linux))
#:make-disk-image? #t
#:disk-image-size disk-image-size))
;;;
;;; Guile 2.0 potluck examples.
;;;
(define (example1)
(let ((store #f))
(dynamic-wind
(lambda ()
(set! store (open-connection)))
(lambda ()
(parameterize ((%guile-for-build (package-derivation store guile-final)))
(expression->derivation-in-linux-vm
store "vm-test"
'(begin
(display "hello from boot!\n")
(call-with-output-file "/xchg/hello"
(lambda (p)
(display "world" p)))))))
(lambda ()
(close-connection store)))))
(define (example2)
(let ((store #f))
(dynamic-wind
(lambda ()
(set! store (open-connection)))
(lambda ()
(parameterize ((%guile-for-build (package-derivation store guile-final)))
(qemu-image store #:disk-image-size (* 30 (expt 2 20)))))
(lambda ()
(close-connection store)))))
;;; vm.scm ends here