mirror of
https://git.savannah.gnu.org/git/guix.git
synced 2025-01-19 05:57:04 +01:00
guix/store/environment.scm: new module
* guix/store/environment.scm (<environment>, environment-{namespaces, variables, temp-dirs, filesystems, new-session?, new-pgroup?, setup-i/o-proc, preserved-fds, chroot, personality, user, group, hostname, domainname}, build-environment-vars, delete-environment, run-in-environment, bind-mount, standard-i/o-setup, %standard-preserved-fds, nonchroot-build-environment, chroot-build-environment, builtin-builder-environment, run-standard, run-standard-build, wait-for-build): new exported variables. * guix/store/database.scm (output-path-id-sql, outputs-exist?, references-sql, file-closure, all-input-output-paths, all-transitive-inputs): new variables. (outputs-exist?, file-closure, all-transitive-inputs): exported. * Makefile.am (STORE_MODULES): add guix/store/environment.scm.
This commit is contained in:
parent
46e2a65bbf
commit
d1832f8b5c
3 changed files with 572 additions and 2 deletions
|
@ -311,7 +311,8 @@ endif BUILD_DAEMON_OFFLOAD
|
||||||
STORE_MODULES = \
|
STORE_MODULES = \
|
||||||
guix/store/database.scm \
|
guix/store/database.scm \
|
||||||
guix/store/deduplication.scm \
|
guix/store/deduplication.scm \
|
||||||
guix/store/roots.scm
|
guix/store/roots.scm \
|
||||||
|
guix/store/environment.scm
|
||||||
|
|
||||||
MODULES += $(STORE_MODULES)
|
MODULES += $(STORE_MODULES)
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ (define-module (guix store database)
|
||||||
#:use-module (srfi srfi-26)
|
#:use-module (srfi srfi-26)
|
||||||
#:use-module (rnrs io ports)
|
#:use-module (rnrs io ports)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
|
#:use-module (ice-9 vlist)
|
||||||
#:use-module (system foreign)
|
#:use-module (system foreign)
|
||||||
#:export (sql-schema
|
#:export (sql-schema
|
||||||
%default-database-file
|
%default-database-file
|
||||||
|
@ -46,7 +47,10 @@ (define-module (guix store database)
|
||||||
register-items
|
register-items
|
||||||
registered-derivation-outputs
|
registered-derivation-outputs
|
||||||
%epoch
|
%epoch
|
||||||
reset-timestamps))
|
reset-timestamps
|
||||||
|
outputs-exist?
|
||||||
|
file-closure
|
||||||
|
all-transitive-inputs))
|
||||||
|
|
||||||
;;; Code for working with the store database directly.
|
;;; Code for working with the store database directly.
|
||||||
|
|
||||||
|
@ -403,3 +407,76 @@ (define (register-derivation-outputs drv)
|
||||||
(register db item)
|
(register db item)
|
||||||
(report))
|
(report))
|
||||||
items)))))))))
|
items)))))))))
|
||||||
|
|
||||||
|
(define output-path-id-sql
|
||||||
|
"SELECT id FROM ValidPaths WHERE path IN (SELECT path FROM DerivationOutputs
|
||||||
|
WHERE DerivationOutputs.id = :id AND drv IN (SELECT id FROM ValidPaths WHERE
|
||||||
|
path = :drvpath))")
|
||||||
|
|
||||||
|
(define* (outputs-exist? drv-path outputs
|
||||||
|
#:optional (database %default-database-file))
|
||||||
|
"Determine whether all output labels in OUTPUTS exist as built outputs of
|
||||||
|
DRV-PATH."
|
||||||
|
(with-database database db
|
||||||
|
(let ((stmt (sqlite-prepare db output-path-id-sql)))
|
||||||
|
(sqlite-bind-arguments stmt #:drvpath drv-path)
|
||||||
|
(let ((result (every (lambda (out-id)
|
||||||
|
(sqlite-reset stmt)
|
||||||
|
(sqlite-bind-arguments stmt #:id out-id)
|
||||||
|
(sqlite-step stmt))
|
||||||
|
outputs)))
|
||||||
|
(sqlite-finalize stmt)
|
||||||
|
result))))
|
||||||
|
|
||||||
|
(define references-sql
|
||||||
|
"SELECT path FROM ValidPaths WHERE id IN (SELECT reference FROM Refs WHERE
|
||||||
|
referrer IN (SELECT id FROM ValidPaths WHERE path = :path))")
|
||||||
|
|
||||||
|
(define* (file-closure path #:key
|
||||||
|
(database %default-database-file)
|
||||||
|
(list-so-far vlist-null))
|
||||||
|
"Return a vlist containing the store paths referenced by PATH, the store
|
||||||
|
paths referenced by those paths, and so on."
|
||||||
|
(with-database database db
|
||||||
|
(let ((get-references (sqlite-prepare db references-sql)))
|
||||||
|
;; to make it possible to go depth-first we need to get all the
|
||||||
|
;; references of an item first or we'll have re-entrancy issues with
|
||||||
|
;; the get-references statement.
|
||||||
|
(define (references-of path)
|
||||||
|
;; There are no problems with resetting an already-reset
|
||||||
|
;; statement.
|
||||||
|
(sqlite-reset get-references)
|
||||||
|
(sqlite-bind-arguments get-references #:path path)
|
||||||
|
(sqlite-fold (lambda (row prev)
|
||||||
|
(cons (vector-ref row 0) prev))
|
||||||
|
'()
|
||||||
|
get-references))
|
||||||
|
|
||||||
|
(let ((result
|
||||||
|
(let %file-closure ((path path)
|
||||||
|
(references-vlist list-so-far))
|
||||||
|
(if (vhash-assoc path references-vlist)
|
||||||
|
references-vlist
|
||||||
|
(fold %file-closure
|
||||||
|
(vhash-cons path #t references-vlist)
|
||||||
|
(references-of path))))))
|
||||||
|
(sqlite-finalize get-references)
|
||||||
|
result))))
|
||||||
|
|
||||||
|
(define (all-input-output-paths drv)
|
||||||
|
"Return a list containing the output paths this derivation's inputs need to
|
||||||
|
provide."
|
||||||
|
(apply append (map derivation-input-output-paths
|
||||||
|
(derivation-inputs drv))))
|
||||||
|
|
||||||
|
(define (all-transitive-inputs drv)
|
||||||
|
"Produce a list of all inputs and all of their references."
|
||||||
|
(let ((input-paths (all-input-output-paths drv)))
|
||||||
|
(vhash-fold (lambda (key val prev)
|
||||||
|
(cons key prev))
|
||||||
|
'()
|
||||||
|
(fold (lambda (input list-so-far)
|
||||||
|
(file-closure input #:list-so-far list-so-far))
|
||||||
|
vlist-null
|
||||||
|
`(,@(derivation-sources drv)
|
||||||
|
,@input-paths)))))
|
||||||
|
|
492
guix/store/environment.scm
Normal file
492
guix/store/environment.scm
Normal file
|
@ -0,0 +1,492 @@
|
||||||
|
;;; GNU Guix --- Functional package management for GNU
|
||||||
|
;;; Copyright © 2019 Caleb Ristvedt <caleb.ristvedt@cune.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/>.
|
||||||
|
|
||||||
|
;;; Code for setting up environments, especially build environments. Builds
|
||||||
|
;;; on top of (gnu build linux-container).
|
||||||
|
|
||||||
|
(define-module (guix store environment)
|
||||||
|
#:use-module (guix records)
|
||||||
|
#:use-module (guix config)
|
||||||
|
#:use-module (gnu build linux-container)
|
||||||
|
#:use-module (gnu system file-systems)
|
||||||
|
#:use-module ((guix build utils) #:select (delete-file-recursively
|
||||||
|
mkdir-p
|
||||||
|
copy-recursively))
|
||||||
|
#:use-module (guix derivations)
|
||||||
|
#:use-module (guix store)
|
||||||
|
#:use-module (guix build syscalls)
|
||||||
|
#:use-module (guix store database)
|
||||||
|
#:use-module (guix store files)
|
||||||
|
#:use-module (gcrypt hash)
|
||||||
|
#:use-module (guix base32)
|
||||||
|
#:use-module (ice-9 match)
|
||||||
|
#:use-module (rnrs bytevectors)
|
||||||
|
#:use-module (rnrs io ports)
|
||||||
|
#:use-module (srfi srfi-1)
|
||||||
|
#:use-module (srfi srfi-11)
|
||||||
|
#:use-module (srfi srfi-26)
|
||||||
|
#:use-module (srfi srfi-98)
|
||||||
|
|
||||||
|
#:export (<environment>
|
||||||
|
environment
|
||||||
|
environment-namespaces
|
||||||
|
environment-variables
|
||||||
|
environment-temp-dirs
|
||||||
|
environment-filesystems
|
||||||
|
environment-new-session?
|
||||||
|
environment-new-pgroup?
|
||||||
|
environment-setup-i/o-proc
|
||||||
|
environment-preserved-fds
|
||||||
|
environment-chroot
|
||||||
|
environment-personality
|
||||||
|
environment-user
|
||||||
|
environment-group
|
||||||
|
environment-hostname
|
||||||
|
environment-domainname
|
||||||
|
build-environment-vars
|
||||||
|
delete-environment
|
||||||
|
run-in-environment
|
||||||
|
bind-mount
|
||||||
|
standard-i/o-setup
|
||||||
|
%standard-preserved-fds
|
||||||
|
nonchroot-build-environment
|
||||||
|
chroot-build-environment
|
||||||
|
builtin-builder-environment
|
||||||
|
run-standard
|
||||||
|
run-standard-build
|
||||||
|
wait-for-build))
|
||||||
|
|
||||||
|
(define %standard-preserved-fds '(0 1 2))
|
||||||
|
|
||||||
|
(define-record-type* <environment> environment
|
||||||
|
;; The defaults are set to be as close to the "current environment" as
|
||||||
|
;; possible.
|
||||||
|
make-environment
|
||||||
|
environment?
|
||||||
|
(namespaces environment-namespaces (default '())) ; list of symbols
|
||||||
|
; list of (key . val) pairs
|
||||||
|
(variables environment-variables (default (get-environment-variables)))
|
||||||
|
; list of (symbol . filename) pairs.
|
||||||
|
(temp-dirs environment-temp-dirs (default '()))
|
||||||
|
;; list of <file-system> objects. Only used when MNT is in NAMESPACES.
|
||||||
|
(filesystems environment-filesystems (default '()))
|
||||||
|
; boolean (implies NEW-PGROUP?)
|
||||||
|
(new-session? environment-new-session? (default #f))
|
||||||
|
(new-pgroup? environment-new-pgroup? (default #f)) ; boolean
|
||||||
|
(setup-i/o environment-setup-i/o-proc) ; a thunk or #f
|
||||||
|
; #f or list of integers (in case of #f, all are preserved)
|
||||||
|
(preserved-fds environment-preserved-fds (default #f))
|
||||||
|
;; either the chroot directory or #f, must not be #f if MNT is in
|
||||||
|
;; NAMESPACES! Will be recursively deleted when the environment is
|
||||||
|
;; destroyed. Ignored if MNT is not in NAMESPACES.
|
||||||
|
(chroot environment-chroot (default #f))
|
||||||
|
(initial-directory environment-initial-directory (default #f)) ; string or #f
|
||||||
|
(personality environment-personality (default #f)) ; integer or #f
|
||||||
|
;; These are currently naively handled in the case of user namespaces.
|
||||||
|
(user environment-user (default #f)) ; integer or #f
|
||||||
|
(group environment-group (default #f)) ; integer or #f
|
||||||
|
(hostname environment-hostname (default #f)) ; string or #f
|
||||||
|
(domainname environment-domainname (default #f))) ; string or #f
|
||||||
|
|
||||||
|
(define (delete-environment env)
|
||||||
|
"Delete all temporary directories used in ENV."
|
||||||
|
(for-each (match-lambda
|
||||||
|
((id . filename)
|
||||||
|
(delete-file-recursively filename)))
|
||||||
|
(environment-temp-dirs env))
|
||||||
|
(when (environment-chroot env)
|
||||||
|
(delete-file-recursively (environment-chroot env))))
|
||||||
|
|
||||||
|
(define (format-file file-name . args)
|
||||||
|
(call-with-output-file file-name
|
||||||
|
(lambda (port)
|
||||||
|
(apply simple-format port args))))
|
||||||
|
|
||||||
|
(define* (mkdir-p* dir #:optional permissions)
|
||||||
|
(mkdir-p dir)
|
||||||
|
(when permissions
|
||||||
|
(chmod dir permissions)))
|
||||||
|
|
||||||
|
(define (add-core-files environment fixed-output?)
|
||||||
|
"Populate container with miscellaneous files and directories that shouldn't
|
||||||
|
be bind-mounted."
|
||||||
|
(let ((uid (environment-user environment))
|
||||||
|
(gid (environment-group environment)))
|
||||||
|
(mkdir-p* "/tmp" #o1777)
|
||||||
|
(mkdir-p* "/etc")
|
||||||
|
|
||||||
|
(unless (or (file-exists? "/etc/passwd")
|
||||||
|
(file-exists? "/etc/group"))
|
||||||
|
(format-file "/etc/passwd"
|
||||||
|
(string-append "nixbld:x:~a:~a:Nix build user:/:/noshell~%"
|
||||||
|
"nobody:x:65534:65534:Nobody:/:/noshell~%")
|
||||||
|
uid gid)
|
||||||
|
(format-file "/etc/group" "nixbld:!:~a:~%" gid))
|
||||||
|
|
||||||
|
(unless (or fixed-output? (file-exists? "/etc/hosts"))
|
||||||
|
(format-file "/etc/hosts" "127.0.0.1 localhost~%"))
|
||||||
|
(when (file-exists? "/dev/pts/ptmx")
|
||||||
|
(chmod "/dev/pts/ptmx" #o0666))))
|
||||||
|
|
||||||
|
(define (run-in-environment env thunk . i/o-args)
|
||||||
|
"Run THUNK in ENV with I/O-ARGS passed to the SETUP-I/O procedure of
|
||||||
|
ENV. Return the pid of the process THUNK is run in."
|
||||||
|
(match env
|
||||||
|
(($ <environment> namespaces variables temp-dirs
|
||||||
|
filesystems new-session? new-pgroup? setup-i/o
|
||||||
|
preserved-fds chroot current-directory new-personality
|
||||||
|
user group hostname domainname)
|
||||||
|
(when (and new-session? (not new-pgroup?))
|
||||||
|
(throw 'invalid-environment "NEW-SESSION? implies NEW-PGROUP?."))
|
||||||
|
(let ((fixed-output? (not (memq 'net namespaces))))
|
||||||
|
(run-container chroot filesystems namespaces (and user (1+ user))
|
||||||
|
(lambda ()
|
||||||
|
(when hostname (sethostname hostname))
|
||||||
|
(when domainname (setdomainname domainname))
|
||||||
|
;; setsid / setpgrp as necessary
|
||||||
|
(if new-session?
|
||||||
|
(setsid)
|
||||||
|
(when new-pgroup?
|
||||||
|
(setpgid 0 0)))
|
||||||
|
(when chroot
|
||||||
|
(add-core-files env fixed-output?))
|
||||||
|
;; set environment variables
|
||||||
|
(when variables
|
||||||
|
(environ (map (match-lambda
|
||||||
|
((key . val)
|
||||||
|
(string-append key "=" val)))
|
||||||
|
variables)))
|
||||||
|
(when setup-i/o (apply setup-i/o i/o-args))
|
||||||
|
;; set UID and GID
|
||||||
|
(when current-directory (chdir current-directory))
|
||||||
|
(when group (setgid group))
|
||||||
|
(when user (setuid user))
|
||||||
|
;; Close unpreserved fds
|
||||||
|
(when preserved-fds
|
||||||
|
(let close-next ((n 0))
|
||||||
|
(when (< n 20) ;; XXX: don't hardcode.
|
||||||
|
(unless (memq n preserved-fds)
|
||||||
|
(false-if-exception (close-fdes n)))
|
||||||
|
(close-next (1+ n)))))
|
||||||
|
|
||||||
|
;; enact personality
|
||||||
|
(when new-personality (personality new-personality))
|
||||||
|
(thunk)))))))
|
||||||
|
|
||||||
|
(define (bind-mount src dest)
|
||||||
|
"Return a <file-system> denoting the bind-mounting of SRC to DEST. Note that
|
||||||
|
if this is part of a chroot <environment>, DEST will be the name *inside of*
|
||||||
|
the chroot, i.e.
|
||||||
|
|
||||||
|
(bind-mount \"/foo/x\" \"/bar/x\")
|
||||||
|
|
||||||
|
in an environment with chroot \"/chrootdir\" will bind-mount \"/foo/x\" to
|
||||||
|
\"/chrootdir/bar/x\"."
|
||||||
|
(file-system
|
||||||
|
(device src)
|
||||||
|
(mount-point dest)
|
||||||
|
(type "none")
|
||||||
|
(flags '(bind-mount))
|
||||||
|
(check? #f)))
|
||||||
|
|
||||||
|
(define input->mount
|
||||||
|
(match-lambda
|
||||||
|
((source . dest)
|
||||||
|
(bind-mount source dest))
|
||||||
|
(source
|
||||||
|
(bind-mount source source))))
|
||||||
|
|
||||||
|
(define (default-files drv)
|
||||||
|
"Return a list of the files to be bind-mounted that aren't store items or
|
||||||
|
already added by call-with-container."
|
||||||
|
`(,@(if (file-exists? "/dev/kvm")
|
||||||
|
'("/dev/kvm")
|
||||||
|
'())
|
||||||
|
,@(if (fixed-output-derivation? drv)
|
||||||
|
'("/etc/resolv.conf"
|
||||||
|
"/etc/nsswitch.conf"
|
||||||
|
"/etc/services"
|
||||||
|
"/etc/hosts")
|
||||||
|
'())))
|
||||||
|
|
||||||
|
(define (build-environment-vars drv build-dir)
|
||||||
|
"Return an alist of environment variable / value pairs for every environment
|
||||||
|
variable that should be set during the build execution."
|
||||||
|
(let ((leaked-vars (and
|
||||||
|
(fixed-output-derivation? drv)
|
||||||
|
(let ((leak-string
|
||||||
|
(assoc-ref (derivation-builder-environment-vars drv)
|
||||||
|
"impureEnvVars")))
|
||||||
|
(and leak-string
|
||||||
|
(string-tokenize leak-string
|
||||||
|
(char-set-complement
|
||||||
|
(char-set #\space))))))))
|
||||||
|
(append `(("PATH" . "/path-not-set")
|
||||||
|
("HOME" . "/homeless-shelter")
|
||||||
|
("NIX_STORE" . ,%store-directory)
|
||||||
|
;; XXX: make this configurable
|
||||||
|
("NIX_BUILD_CORES" . "0")
|
||||||
|
("NIX_BUILD_TOP" . ,build-dir)
|
||||||
|
("TMPDIR" . ,build-dir)
|
||||||
|
("TEMPDIR" . ,build-dir)
|
||||||
|
("TMP" . ,build-dir)
|
||||||
|
("TEMP" . ,build-dir)
|
||||||
|
("PWD" . ,build-dir))
|
||||||
|
(if (fixed-output-derivation? drv)
|
||||||
|
(cons '("NIX_OUTPUT_CHECKED" . "1")
|
||||||
|
(if leaked-vars
|
||||||
|
;; leaked vars might be #f
|
||||||
|
(filter cdr
|
||||||
|
(map (lambda (leaked-var)
|
||||||
|
(cons leaked-var (getenv leaked-var)))
|
||||||
|
leaked-vars))
|
||||||
|
'()))
|
||||||
|
'())
|
||||||
|
(derivation-builder-environment-vars drv))))
|
||||||
|
|
||||||
|
(define* (temp-directory name #:optional permissions user group
|
||||||
|
#:key (tmpdir %temp-directory))
|
||||||
|
"Create a temporary directory under TMPDIR with permissions PERMISSIONS if
|
||||||
|
specified, otherwise default permissions as specified by umask, and belonging
|
||||||
|
to user USER and group GROUP (defaulting to current user if not specified or
|
||||||
|
#f). Return the full filename of the form <tmpdir>/<name>-<number>."
|
||||||
|
(let try-again ((attempt-number 0))
|
||||||
|
(catch 'system-error
|
||||||
|
(lambda ()
|
||||||
|
(let ((attempt-name (string-append tmpdir "/" name "-"
|
||||||
|
(number->string
|
||||||
|
attempt-number 10))))
|
||||||
|
(mkdir attempt-name permissions)
|
||||||
|
(when permissions
|
||||||
|
;; the only guarantee we get from mkdir is that the actual
|
||||||
|
;; permissions are no more permissive than what we specified. In
|
||||||
|
;; the event we want to be more permissive than the umask, though,
|
||||||
|
;; this is necessary.
|
||||||
|
(chmod attempt-name permissions))
|
||||||
|
;; -1 means "unchanged"
|
||||||
|
(chown attempt-name (or user -1) (or group -1))
|
||||||
|
attempt-name))
|
||||||
|
(lambda args
|
||||||
|
(if (= (system-error-errno args) EEXIST)
|
||||||
|
(try-again (+ attempt-number 1))
|
||||||
|
(apply throw args))))))
|
||||||
|
|
||||||
|
(define (special-filesystems input-paths)
|
||||||
|
"Return whatever new filesystems need to be created in the container, which
|
||||||
|
depends on whether they're already set to be bind-mounted. INPUT-PATHS must
|
||||||
|
be a list of paths or pairs of paths."
|
||||||
|
;; procfs and devpts are already taken care of by run-container
|
||||||
|
`(,@(if (file-exists? "/dev/shm")
|
||||||
|
(list (file-system
|
||||||
|
(device "none")
|
||||||
|
(mount-point "/dev/shm")
|
||||||
|
(type "tmpfs")
|
||||||
|
(check? #f)))
|
||||||
|
'())))
|
||||||
|
|
||||||
|
(define (standard-i/o-setup output-port)
|
||||||
|
"Redirect output and error streams to OUTPUT-FD, get input from /dev/null."
|
||||||
|
(define output-fd (port->fdes output-port))
|
||||||
|
(define stdout (fdopen 1 "w"))
|
||||||
|
;; Useful in case an error happens between here and an exec and it needs to
|
||||||
|
;; get reported.
|
||||||
|
(set-current-output-port stdout)
|
||||||
|
(set-current-error-port stdout)
|
||||||
|
(dup2 output-fd 1)
|
||||||
|
(dup2 output-fd 2)
|
||||||
|
(call-with-input-file "/dev/null"
|
||||||
|
(lambda (null-port)
|
||||||
|
(dup2 (port->fdes null-port) 0)))
|
||||||
|
(sigaction SIGPIPE SIG_DFL))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(define (derivation-tempname drv)
|
||||||
|
(string-append "guix-build-"
|
||||||
|
(store-path-package-name (derivation-file-name drv))))
|
||||||
|
|
||||||
|
;; We might want to add to this sometime.
|
||||||
|
(define %default-chroot-dirs
|
||||||
|
'())
|
||||||
|
|
||||||
|
(define (default-personality drv)
|
||||||
|
(let ((current-personality (personality #xffffffff)))
|
||||||
|
(logior current-personality ADDR_NO_RANDOMIZE
|
||||||
|
(match (cons %system (derivation-system drv))
|
||||||
|
((or ("x86_64-linux" . "i686-linux")
|
||||||
|
("aarch64-linux" . "armhf-linux"))
|
||||||
|
PER_LINUX32)
|
||||||
|
(_ 0))
|
||||||
|
(match (cons (derivation-system drv) (%impersonate-linux-2.6?))
|
||||||
|
(((or "x86_64-linux" "i686-linux") . #t)
|
||||||
|
UNAME26)
|
||||||
|
(_ 0)))))
|
||||||
|
|
||||||
|
(define* (make-build-directory drv #:optional uid gid)
|
||||||
|
(let ((build-directory (temp-directory (derivation-tempname drv) #o0700
|
||||||
|
uid gid)))
|
||||||
|
;; XXX: Honor exportReferencesGraph here...
|
||||||
|
build-directory))
|
||||||
|
|
||||||
|
(define* (nonchroot-build-environment drv #:key gid uid)
|
||||||
|
"Create and return an <environment> for building DRV outside of a chroot, as
|
||||||
|
well as the store inputs the build requires."
|
||||||
|
(let* ((fixed-output? (fixed-output-derivation? drv))
|
||||||
|
(build-directory (make-build-directory drv)))
|
||||||
|
(values
|
||||||
|
(environment
|
||||||
|
(temp-dirs `((build-directory . ,build-directory)))
|
||||||
|
(initial-directory build-directory)
|
||||||
|
(new-session? #t)
|
||||||
|
(new-pgroup? #t)
|
||||||
|
(variables (build-environment-vars drv build-directory))
|
||||||
|
(preserved-fds %standard-preserved-fds)
|
||||||
|
(setup-i/o standard-i/o-setup)
|
||||||
|
(personality (default-personality drv))
|
||||||
|
(user uid)
|
||||||
|
(group gid))
|
||||||
|
(all-transitive-inputs drv))))
|
||||||
|
|
||||||
|
|
||||||
|
(define* (builtin-builder-environment drv #:key gid uid)
|
||||||
|
"Create and return an <environment> for builtin builders, as well as the
|
||||||
|
store inputs the build requires."
|
||||||
|
;; It's just the same as non-chroot-build-environment, but without any
|
||||||
|
;; environment variables being changed.
|
||||||
|
(let*-values (((env inputs) (nonchroot-build-environment drv
|
||||||
|
#:gid gid
|
||||||
|
#:uid uid)))
|
||||||
|
(values
|
||||||
|
(environment (inherit env)
|
||||||
|
(variables (get-environment-variables)))
|
||||||
|
inputs)))
|
||||||
|
|
||||||
|
(define* (chroot-build-environment drv #:key gid uid
|
||||||
|
(extra-chroot-dirs '())
|
||||||
|
build-chroot-dirs )
|
||||||
|
"Create an <environment> for building DRV with standard in-chroot
|
||||||
|
settings (as used by nix daemon). Return said environment as well as the
|
||||||
|
store paths that are included in it (useful for reference scanning)."
|
||||||
|
(let* ((tempname (derivation-tempname drv))
|
||||||
|
(store-directory (temp-directory (string-append tempname ".store")
|
||||||
|
#o1775 0 gid))
|
||||||
|
(build-directory (make-build-directory drv uid gid))
|
||||||
|
(inside-build-dir (string-append %temp-directory "/" tempname "-0"))
|
||||||
|
(fixed-output? (fixed-output-derivation? drv))
|
||||||
|
(store-inputs (all-transitive-inputs drv))
|
||||||
|
(input-paths (append store-inputs
|
||||||
|
(default-files drv)
|
||||||
|
(or build-chroot-dirs
|
||||||
|
%default-chroot-dirs)
|
||||||
|
extra-chroot-dirs)))
|
||||||
|
(values
|
||||||
|
(environment
|
||||||
|
(namespaces `(mnt pid ipc uts ,@(if fixed-output? '() '(net))))
|
||||||
|
(filesystems
|
||||||
|
(cons* (bind-mount build-directory inside-build-dir)
|
||||||
|
(bind-mount store-directory %store-directory)
|
||||||
|
(append (special-filesystems input-paths)
|
||||||
|
(map input->mount input-paths))))
|
||||||
|
(temp-dirs `((store-directory . ,store-directory)
|
||||||
|
(build-directory . ,build-directory)))
|
||||||
|
(initial-directory inside-build-dir)
|
||||||
|
(new-session? #t)
|
||||||
|
(new-pgroup? #t)
|
||||||
|
(setup-i/o (lambda (output-fd)
|
||||||
|
(unless fixed-output?
|
||||||
|
(initialize-loopback))
|
||||||
|
(standard-i/o-setup output-fd)))
|
||||||
|
(variables (build-environment-vars drv inside-build-dir))
|
||||||
|
(preserved-fds %standard-preserved-fds)
|
||||||
|
(chroot (temp-directory (string-append tempname ".chroot") #o750 0 gid))
|
||||||
|
(user uid)
|
||||||
|
(group gid)
|
||||||
|
(personality (default-personality drv))
|
||||||
|
(hostname "localhost")
|
||||||
|
(domainname "(none)"))
|
||||||
|
store-inputs)))
|
||||||
|
|
||||||
|
(define (redirected-path drv output)
|
||||||
|
(let* ((original (derivation-output-path (assoc-ref (derivation-outputs drv)
|
||||||
|
output)))
|
||||||
|
(hash
|
||||||
|
(bytevector->nix-base32-string
|
||||||
|
(compressed-hash (sha256 (string-append "rewrite:"
|
||||||
|
(derivation-file-name drv)
|
||||||
|
":"
|
||||||
|
original))
|
||||||
|
20))))
|
||||||
|
(string-append (%store-prefix) "/" hash "-"
|
||||||
|
(store-path-package-name original))))
|
||||||
|
|
||||||
|
(define (redirect-outputs env drv output-names)
|
||||||
|
"Create a new <environment> based on ENV but modified so that for each
|
||||||
|
output-name in OUTPUT-NAMES, the environment variable corresponding to that
|
||||||
|
output is set to a newly-generated output path."
|
||||||
|
(environment (inherit env)
|
||||||
|
(variables (append (map (lambda (output)
|
||||||
|
(cons output (redirected-path drv output)))
|
||||||
|
output-names)
|
||||||
|
(remove (lambda (var)
|
||||||
|
(member (car var) output-names))
|
||||||
|
(environment-variables env))))))
|
||||||
|
|
||||||
|
(define (run-standard environment thunk)
|
||||||
|
"Run THUNK in ENVIRONMENT. Return the PID it is being run in and the read
|
||||||
|
end of the pipe its i/o has been set up with."
|
||||||
|
(match (pipe)
|
||||||
|
((read . write)
|
||||||
|
(let ((pid (run-in-environment environment
|
||||||
|
(lambda ()
|
||||||
|
(catch #t
|
||||||
|
(lambda ()
|
||||||
|
(thunk)
|
||||||
|
(primitive-exit 0))
|
||||||
|
(lambda args
|
||||||
|
(format #t "Error: ~A~%" args)
|
||||||
|
(primitive-exit 1))))
|
||||||
|
write)))
|
||||||
|
(close-fdes (port->fdes write))
|
||||||
|
(values pid read)))))
|
||||||
|
|
||||||
|
(define (run-standard-build drv environment)
|
||||||
|
"Run the builder of DRV in ENVIRONMENT. Return the PID it is being run in
|
||||||
|
and the read end of the pipe its i/o has been set up with."
|
||||||
|
(run-standard environment
|
||||||
|
(lambda ()
|
||||||
|
(let ((prog (derivation-builder drv))
|
||||||
|
(args (derivation-builder-arguments drv)))
|
||||||
|
(apply execl prog prog args)))))
|
||||||
|
|
||||||
|
(define* (dump-port port #:optional (target-port (current-output-port)))
|
||||||
|
(if (port-eof? port)
|
||||||
|
(force-output target-port)
|
||||||
|
(begin
|
||||||
|
(put-bytevector target-port (get-bytevector-some port))
|
||||||
|
(dump-port port target-port))))
|
||||||
|
|
||||||
|
(define (wait-for-build pid read-port)
|
||||||
|
"Dump all input from READ-PORT to (current-output-port), then wait for PID
|
||||||
|
to terminate."
|
||||||
|
(dump-port read-port)
|
||||||
|
(close-fdes (port->fdes read-port))
|
||||||
|
;; Should we wait specifically for PID to die, or just for any state change?
|
||||||
|
(cdr (waitpid pid)))
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue