diff options
| -rw-r--r-- | Makefile.am | 3 | ||||
| -rw-r--r-- | guix/build/store-copy.scm | 3 | ||||
| -rw-r--r-- | guix/store/build-derivations.scm | 474 |
3 files changed, 478 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am index 43a98ad906c..664e3da4a2b 100644 --- a/Makefile.am +++ b/Makefile.am | |||
| @@ -312,7 +312,8 @@ STORE_MODULES = \ | |||
| 312 | guix/store/database.scm \ | 312 | guix/store/database.scm \ |
| 313 | guix/store/deduplication.scm \ | 313 | guix/store/deduplication.scm \ |
| 314 | guix/store/roots.scm \ | 314 | guix/store/roots.scm \ |
| 315 | guix/store/environment.scm | 315 | guix/store/environment.scm \ |
| 316 | guix/store/build-derivations.scm | ||
| 316 | 317 | ||
| 317 | MODULES += $(STORE_MODULES) | 318 | MODULES += $(STORE_MODULES) |
| 318 | 319 | ||
diff --git a/guix/build/store-copy.scm b/guix/build/store-copy.scm index 549aa4f28be..73bfe694e08 100644 --- a/guix/build/store-copy.scm +++ b/guix/build/store-copy.scm | |||
| @@ -27,7 +27,8 @@ | |||
| 27 | #:use-module (ice-9 rdelim) | 27 | #:use-module (ice-9 rdelim) |
| 28 | #:use-module (ice-9 ftw) | 28 | #:use-module (ice-9 ftw) |
| 29 | #:use-module (ice-9 vlist) | 29 | #:use-module (ice-9 vlist) |
| 30 | #:export (store-info? | 30 | #:export (<store-info> |
| 31 | store-info? | ||
| 31 | store-info | 32 | store-info |
| 32 | store-info-item | 33 | store-info-item |
| 33 | store-info-deriver | 34 | store-info-deriver |
diff --git a/guix/store/build-derivations.scm b/guix/store/build-derivations.scm new file mode 100644 index 00000000000..506cd406722 --- /dev/null +++ b/guix/store/build-derivations.scm | |||
| @@ -0,0 +1,474 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | ||
| 2 | ;;; Copyright © 2017, 2019 Caleb Ristvedt <caleb.ristvedt@cune.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 | ;;; For building derivations. | ||
| 20 | |||
| 21 | (define-module (guix store build-derivations) | ||
| 22 | #:use-module (guix store derivations) | ||
| 23 | #:use-module (guix store files) | ||
| 24 | #:use-module (guix store database) | ||
| 25 | #:use-module (guix config) | ||
| 26 | #:use-module (guix build syscalls) | ||
| 27 | #:use-module (ice-9 vlist) | ||
| 28 | #:use-module (ice-9 popen) | ||
| 29 | #:use-module (srfi srfi-1) | ||
| 30 | #:use-module (srfi srfi-9) | ||
| 31 | #:use-module (srfi srfi-26) | ||
| 32 | #:use-module (srfi srfi-11) | ||
| 33 | #:use-module (gcrypt hash) | ||
| 34 | #:use-module (guix serialization) | ||
| 35 | #:use-module (guix base16) | ||
| 36 | #:use-module (guix sets) | ||
| 37 | #:use-module ((guix build utils) #:select (delete-file-recursively | ||
| 38 | mkdir-p | ||
| 39 | copy-recursively)) | ||
| 40 | #:use-module (guix build store-copy) | ||
| 41 | #:use-module (gnu system file-systems) | ||
| 42 | #:use-module (ice-9 textual-ports) | ||
| 43 | #:use-module (ice-9 match) | ||
| 44 | #:use-module (rnrs io ports) | ||
| 45 | #:use-module (ice-9 rdelim) | ||
| 46 | #:use-module (ice-9 ftw) | ||
| 47 | #:use-module (ice-9 q) | ||
| 48 | #:use-module (srfi srfi-43) | ||
| 49 | #:use-module (rnrs bytevectors) | ||
| 50 | #:use-module (guix store environment) | ||
| 51 | #:export (builder+environment+inputs | ||
| 52 | build-derivation)) | ||
| 53 | |||
| 54 | (define (output-paths drv) | ||
| 55 | "Return all store output paths produced by DRV." | ||
| 56 | (match (derivation-outputs drv) | ||
| 57 | (((outid . ($ <derivation-output> output-path)) ...) | ||
| 58 | output-path))) | ||
| 59 | |||
| 60 | (define (get-output-specs drv possible-references) | ||
| 61 | "Return a list of <store-info> objects, one for each output of DRV." | ||
| 62 | (map (match-lambda | ||
| 63 | ((outid . ($ <derivation-output> output-path)) | ||
| 64 | (let ((references | ||
| 65 | (scan-for-references output-path | ||
| 66 | ;; outputs can reference | ||
| 67 | ;; themselves or other outputs of | ||
| 68 | ;; the same derivation. | ||
| 69 | (append (output-paths drv) | ||
| 70 | possible-references)))) | ||
| 71 | (store-info output-path (derivation-file-name drv) references)))) | ||
| 72 | (derivation-outputs drv))) | ||
| 73 | |||
| 74 | (define (builtin-download drv outputs) | ||
| 75 | "Download DRV outputs OUTPUTS into the store." | ||
| 76 | (setenv "NIX_STORE" %store-directory) | ||
| 77 | ;; XXX: Set _NIX_OPTIONS once client settings are known | ||
| 78 | (execl (string-append %libexecdir "/download") | ||
| 79 | "download" | ||
| 80 | (derivation-file-name drv) | ||
| 81 | ;; We assume this has only a single output | ||
| 82 | (derivation-output-path (cdr (first outputs))))) | ||
| 83 | |||
| 84 | ;; if a derivation builder name is in here, it is a builtin. For normal | ||
| 85 | ;; behavior, make sure everything starts with "builtin:". Also, the procedures | ||
| 86 | ;; stored in here should take two arguments, the derivation and the list of | ||
| 87 | ;; (output-name . <derivation-output>)s to be built. | ||
| 88 | |||
| 89 | (define builtins | ||
| 90 | (let ((builtins-table (make-hash-table 10))) | ||
| 91 | (hash-set! builtins-table | ||
| 92 | "builtin:download" | ||
| 93 | builtin-download) | ||
| 94 | builtins-table)) | ||
| 95 | |||
| 96 | (define %keep-build-dir? #t) | ||
| 97 | |||
| 98 | ;; XXX: make this configurable. | ||
| 99 | (define %build-group | ||
| 100 | (make-parameter (false-if-exception (getgrnam "guixbuild")))) | ||
| 101 | |||
| 102 | (define (get-build-user) | ||
| 103 | ;; XXX: user namespace to make build-user work instead of having to be root? | ||
| 104 | (or (and=> (%build-group) | ||
| 105 | ;; XXX: Acquire a user via lock files once those are properly | ||
| 106 | ;; implemented. For now, avoid conflict with the existing daemon | ||
| 107 | ;; where possible by picking a build user from the end (last) | ||
| 108 | ;; instead of the front. | ||
| 109 | ;; So in the future, replace LAST with ACQUIRE-BUILD-USER | ||
| 110 | (compose passwd:uid getpwnam last group:mem)) | ||
| 111 | (getuid))) | ||
| 112 | |||
| 113 | (define (get-build-group) | ||
| 114 | (or (and (zero? (getuid)) | ||
| 115 | (group:gid %build-group)) | ||
| 116 | (getgid))) | ||
| 117 | |||
| 118 | (define-record-type <trie-node> | ||
| 119 | (make-trie-node table string-exists?) | ||
| 120 | trie-node? | ||
| 121 | ;; TODO implement skip values. Probably not as big a speed gain as you think | ||
| 122 | ;; it is, since this is I/O-bound. | ||
| 123 | ;; (skip-value node-skip-value set-skip-value!) | ||
| 124 | (table node-table set-node-table!) | ||
| 125 | ;; Technically speaking, it's possible for both CAT and CATTLE to be in a | ||
| 126 | ;; trie at once. Of course, for our purposes, this is | ||
| 127 | (string-exists? node-string-exists? set-string-exists?!)) | ||
| 128 | |||
| 129 | (define* (add-to-trie trie string #:optional (new-tables-size 2)) | ||
| 130 | "Adds STR to TRIE." | ||
| 131 | (let ((str (string->utf8 string))) | ||
| 132 | (let next-node ((position 0) | ||
| 133 | (current-node trie)) | ||
| 134 | (if (= position (bytevector-length str)) | ||
| 135 | ;; this is it. This is where we need to register that this string is | ||
| 136 | ;; present. | ||
| 137 | (set-string-exists?! current-node #t) | ||
| 138 | (let* ((current-table (node-table current-node)) | ||
| 139 | (node (hash-ref current-table | ||
| 140 | (bytevector-u8-ref str position)))) | ||
| 141 | (if node | ||
| 142 | (next-node (1+ position) | ||
| 143 | node) | ||
| 144 | (let ((new-node (make-trie-node (make-hash-table new-tables-size) | ||
| 145 | #f))) | ||
| 146 | (hash-set! current-table | ||
| 147 | (bytevector-u8-ref str position) | ||
| 148 | new-node) | ||
| 149 | (next-node (1+ position) | ||
| 150 | new-node)))))))) | ||
| 151 | |||
| 152 | (define (make-search-trie strings) | ||
| 153 | ;; TODO: make the first few trie levels non-sparse tables to avoid hashing | ||
| 154 | ;; overhead. | ||
| 155 | (let ((root (make-trie-node (make-hash-table) #f))) | ||
| 156 | (for-each (cut add-to-trie root <>) | ||
| 157 | strings) | ||
| 158 | root)) | ||
| 159 | |||
| 160 | |||
| 161 | (define (remove-from-trie! trie sequence) | ||
| 162 | "Removes SEQUENCE from TRIE. This means that any nodes that are only in the | ||
| 163 | path of SEQUENCE are removed. It's an error to use this with a sequence not | ||
| 164 | already in TRIE." | ||
| 165 | ;; Hm. Looks like we'll have to recurse all the way down, find where it | ||
| 166 | ;; ends, then stop at the first thing on the way back up that has anything | ||
| 167 | ;; with the same prefix. Or I could do this the right way with an explicit | ||
| 168 | ;; stack. Hm... | ||
| 169 | |||
| 170 | (define (node-stack) | ||
| 171 | (let next ((nodes '()) | ||
| 172 | (i 0) | ||
| 173 | (current-node trie)) | ||
| 174 | (if (= (bytevector-length sequence) i) | ||
| 175 | (begin | ||
| 176 | ;; it's possible that even though this is the last node of this | ||
| 177 | ;; sequence it can't be deleted. So mark it as not denoting a | ||
| 178 | ;; string. | ||
| 179 | (set-string-exists?! current-node #f) | ||
| 180 | (cons current-node nodes)) | ||
| 181 | (let ((next-node (hash-ref (node-table current-node) | ||
| 182 | (bytevector-u8-ref sequence i)))) | ||
| 183 | (next (cons current-node nodes) | ||
| 184 | (1+ i) | ||
| 185 | next-node))))) | ||
| 186 | |||
| 187 | (let maybe-delete ((visited-nodes (node-stack)) | ||
| 188 | (i (1- (bytevector-length sequence)))) | ||
| 189 | (match visited-nodes | ||
| 190 | ((current parent others ...) | ||
| 191 | (when (zero? (hash-count (const #t) | ||
| 192 | (node-table current))) | ||
| 193 | |||
| 194 | (hash-remove! (node-table parent) | ||
| 195 | (bytevector-u8-ref sequence i)) | ||
| 196 | (maybe-delete (cdr visited-nodes) | ||
| 197 | (1- i)))) | ||
| 198 | ((current) | ||
| 199 | #f)))) | ||
| 200 | |||
| 201 | (define (scanning-wrapper-port output-port paths) | ||
| 202 | "Creates a wrapper port which passes through bytes to OUTPUT-PORT and | ||
| 203 | returns it as well as a procedure which, when called, returns a list of all | ||
| 204 | references out of the possibilities enumerated in PATHS that were | ||
| 205 | detected. PATHS must not be empty." | ||
| 206 | ;; Not sure if I should be using custom ports or soft ports... | ||
| 207 | (let* ((strings (map store-path-hash-part paths)) | ||
| 208 | (string->path (fold (lambda (current prev) | ||
| 209 | (vhash-cons (store-path-hash-part current) | ||
| 210 | current | ||
| 211 | prev)) | ||
| 212 | vlist-null | ||
| 213 | paths)) | ||
| 214 | (lookback-size (apply max (map (compose bytevector-length string->utf8) | ||
| 215 | strings))) | ||
| 216 | (smallest-length (apply min (map (compose bytevector-length | ||
| 217 | string->utf8) | ||
| 218 | strings))) | ||
| 219 | (lookback-buffer (make-bytevector lookback-size)) | ||
| 220 | (search-trie (make-search-trie strings)) | ||
| 221 | (buffer-pos 0) | ||
| 222 | (references '())) | ||
| 223 | |||
| 224 | (values | ||
| 225 | (make-custom-binary-output-port | ||
| 226 | "scanning-wrapper" | ||
| 227 | ;; write | ||
| 228 | (lambda (bytes offset count) | ||
| 229 | (define (in-lookback? n) | ||
| 230 | (< n buffer-pos)) | ||
| 231 | ;; the "virtual" stuff provides a convenient interface that makes it | ||
| 232 | ;; look like we magically remember the end of the previous buffer. | ||
| 233 | (define (virtual-ref n) | ||
| 234 | (if (in-lookback? n) | ||
| 235 | (bytevector-u8-ref lookback-buffer n) | ||
| 236 | (bytevector-u8-ref bytes (+ (- n buffer-pos) | ||
| 237 | offset)))) | ||
| 238 | |||
| 239 | |||
| 240 | (let ((total-length (+ buffer-pos count))) | ||
| 241 | |||
| 242 | (define (virtual-copy! start end target) | ||
| 243 | (let* ((copy-size (- end start))) | ||
| 244 | (let copy-next ((i 0)) | ||
| 245 | (unless (= i copy-size) | ||
| 246 | (bytevector-u8-set! target | ||
| 247 | i | ||
| 248 | (virtual-ref (+ start i))) | ||
| 249 | (copy-next (1+ i)))) | ||
| 250 | target)) | ||
| 251 | |||
| 252 | ;; the gritty reality of that magic | ||
| 253 | (define (remember-end) | ||
| 254 | (let* ((copy-amount (min total-length | ||
| 255 | lookback-size)) | ||
| 256 | (start (- total-length copy-amount)) | ||
| 257 | (end total-length)) | ||
| 258 | (virtual-copy! start end lookback-buffer) | ||
| 259 | (set! buffer-pos copy-amount))) | ||
| 260 | |||
| 261 | (define (attempt-match n trie) | ||
| 262 | (let test-position ((i n) | ||
| 263 | (current-node trie)) | ||
| 264 | (if (node-string-exists? current-node) | ||
| 265 | ;; MATCH | ||
| 266 | (virtual-copy! n i (make-bytevector (- i n))) | ||
| 267 | (if (>= i total-length) | ||
| 268 | #f | ||
| 269 | (let ((next-node (hash-ref (node-table current-node) | ||
| 270 | (virtual-ref i)))) | ||
| 271 | (if next-node | ||
| 272 | (test-position (1+ i) | ||
| 273 | next-node) | ||
| 274 | #f)))))) | ||
| 275 | |||
| 276 | |||
| 277 | |||
| 278 | (define (scan) | ||
| 279 | (let next-char ((i 0)) | ||
| 280 | (when (< i (- total-length smallest-length)) | ||
| 281 | (let ((match-result (attempt-match i search-trie))) | ||
| 282 | (if match-result | ||
| 283 | (begin | ||
| 284 | (set! references | ||
| 285 | (let ((str-result | ||
| 286 | (cdr (vhash-assoc (utf8->string match-result) | ||
| 287 | string->path)))) | ||
| 288 | (format #t "Found reference to: ~a~%" str-result) | ||
| 289 | (cons str-result | ||
| 290 | references))) | ||
| 291 | ;; We're not interested in multiple references, it'd | ||
| 292 | ;; just slow us down. | ||
| 293 | (remove-from-trie! search-trie match-result) | ||
| 294 | (next-char (+ i (bytevector-length match-result)))) | ||
| 295 | (next-char (1+ i))))))) | ||
| 296 | (format #t "Scanning chunk of ~a bytes~%" count) | ||
| 297 | (scan) | ||
| 298 | (remember-end) | ||
| 299 | (put-bytevector output-port bytes offset count) | ||
| 300 | count)) | ||
| 301 | #f ;; get-position | ||
| 302 | #f ;; set-position | ||
| 303 | (lambda () | ||
| 304 | (close-port output-port))) | ||
| 305 | (lambda () | ||
| 306 | references)))) | ||
| 307 | |||
| 308 | |||
| 309 | ;; There are two main approaches we can use here: we can look for the entire | ||
| 310 | ;; store path of the form "/gnu/store/hashpart-name", which will yield no | ||
| 311 | ;; false positives and likely be faster due to being more quickly able to rule | ||
| 312 | ;; out sequences, and we can look for just hashpart, which will be faster to | ||
| 313 | ;; lookup and may both increase false positives and decrease false negatives | ||
| 314 | ;; as stuff that gets split up will likely still have the hash part all | ||
| 315 | ;; together, but adds a chance that 32 random base-32 characters could cause a | ||
| 316 | ;; false positive, but the chances of that are extremely slim, and an | ||
| 317 | ;; adversary couldn't really use that. | ||
| 318 | (define (scan-for-references file possibilities) | ||
| 319 | "Scans for literal references in FILE as long as they happen to be in | ||
| 320 | POSSIBILITIES. Returns the list of references found, the sha256 hash of the | ||
| 321 | nar, and the length of the nar." | ||
| 322 | (let*-values (((scanning-port get-references) | ||
| 323 | (scanning-wrapper-port (%make-void-port "w") possibilities))) | ||
| 324 | (write-file file scanning-port) | ||
| 325 | (force-output scanning-port) | ||
| 326 | (get-references))) | ||
| 327 | |||
| 328 | (define (copy-outputs drv environment) | ||
| 329 | "Copy output paths produced in ENVIRONMENT from building DRV to the store if | ||
| 330 | a fake store was used." | ||
| 331 | (let ((store-dir (assoc-ref (environment-temp-dirs environment) | ||
| 332 | 'store-directory))) | ||
| 333 | (when store-dir | ||
| 334 | (for-each | ||
| 335 | (match-lambda | ||
| 336 | ((outid . ($ <derivation-output> output-path)) | ||
| 337 | (copy-recursively | ||
| 338 | (string-append store-dir "/" (basename output-path)) output-path))) | ||
| 339 | (derivation-outputs drv))))) | ||
| 340 | |||
| 341 | (define (topologically-sorted store-infos) | ||
| 342 | "Returns STORE-INFOS in topological order or throws CYCLE-DETECTED if no | ||
| 343 | such order exists." | ||
| 344 | (define path->store-info | ||
| 345 | (let loop ((infos store-infos) | ||
| 346 | (mapping vlist-null)) | ||
| 347 | (match infos | ||
| 348 | ((($ <store-info> item deriver references) . tail) | ||
| 349 | (loop tail (vhash-cons item (car infos) mapping))) | ||
| 350 | (() | ||
| 351 | (lambda (path) | ||
| 352 | (let ((pair (vhash-assoc path mapping))) | ||
| 353 | (and pair | ||
| 354 | (cdr pair)))))))) | ||
| 355 | |||
| 356 | (define (references-of store-info) | ||
| 357 | ;; We need to pretend that self-references don't exist... | ||
| 358 | (fold (lambda (current prev) | ||
| 359 | (let ((info (path->store-info current))) | ||
| 360 | (or (and (not (equal? info store-info)) | ||
| 361 | info | ||
| 362 | (cons info prev)) | ||
| 363 | prev))) | ||
| 364 | '() | ||
| 365 | (store-info-references store-info))) | ||
| 366 | |||
| 367 | (reverse | ||
| 368 | (let visit ((infos store-infos) | ||
| 369 | (visited (set)) | ||
| 370 | (dependents (set)) | ||
| 371 | (result '())) | ||
| 372 | (match infos | ||
| 373 | ((head . tail) | ||
| 374 | (if (set-contains? visited head) | ||
| 375 | (if (set-contains? dependents head) | ||
| 376 | (throw 'cycle-detected head) | ||
| 377 | (visit tail visited dependents result)) | ||
| 378 | (call-with-values | ||
| 379 | (lambda () | ||
| 380 | (visit (references-of head) | ||
| 381 | (set-insert head visited) | ||
| 382 | (set-insert head dependents) | ||
| 383 | result)) | ||
| 384 | (lambda (result visited) | ||
| 385 | (visit tail | ||
| 386 | visited | ||
| 387 | dependents | ||
| 388 | (cons head result)))))) | ||
| 389 | (() | ||
| 390 | (values result visited)))))) | ||
| 391 | |||
| 392 | (define (run-builder builder drv environment store-inputs) | ||
| 393 | "Run the builder BUILDER for DRV in ENVIRONMENT, wait for it to finish, and | ||
| 394 | return the list of <store-info>s corresponding to its outputs." | ||
| 395 | (match (status:exit-val (call-with-values | ||
| 396 | (lambda () | ||
| 397 | (run-standard environment builder)) | ||
| 398 | wait-for-build)) | ||
| 399 | (0 | ||
| 400 | ;; XXX: check that the output paths were produced. | ||
| 401 | (copy-outputs drv environment) | ||
| 402 | (delete-environment environment) | ||
| 403 | (get-output-specs drv store-inputs)) | ||
| 404 | (exit-value | ||
| 405 | (format #t "Builder exited with status ~A~%" exit-value) | ||
| 406 | (if %keep-build-dir? | ||
| 407 | (format #t "Note: keeping build directories: ~A~%" | ||
| 408 | (match (environment-temp-dirs environment) | ||
| 409 | (((sym . dir) ...) | ||
| 410 | dir))) | ||
| 411 | (delete-environment environment)) | ||
| 412 | #f))) | ||
| 413 | |||
| 414 | (define* (builder+environment+inputs drv #:optional (chroot? #t)) | ||
| 415 | "Return a thunk that performs the build action, the environment it should be | ||
| 416 | run in, and the store inputs of that environment." | ||
| 417 | (let*-values (((builtin) (hash-ref builtins (derivation-builder drv))) | ||
| 418 | ((environment store-inputs) | ||
| 419 | ((if builtin | ||
| 420 | builtin-builder-environment | ||
| 421 | (if chroot? | ||
| 422 | chroot-build-environment | ||
| 423 | nonchroot-build-environment)) | ||
| 424 | drv #:gid (get-build-group) #:uid (get-build-user))) | ||
| 425 | ((builder) (or | ||
| 426 | (and builtin (lambda () | ||
| 427 | (builtin drv (derivation-outputs | ||
| 428 | drv)))) | ||
| 429 | (lambda () | ||
| 430 | (let ((prog (derivation-builder drv)) | ||
| 431 | (args (derivation-builder-arguments drv))) | ||
| 432 | (apply execl prog prog args)))))) | ||
| 433 | (values builder environment store-inputs))) | ||
| 434 | |||
| 435 | ;; Note: used for testing mostly, daemon should be starting builds directly | ||
| 436 | ;; and not just waiting for them to finish sequentially... | ||
| 437 | (define (%build-derivation drv) | ||
| 438 | "Given a <derivation> DRV, build the derivation unconditionally even if its | ||
| 439 | outputs already exist." | ||
| 440 | ;; Make sure store permissions and ownership are intact (test-env creates a | ||
| 441 | ;; store with wrong permissions, for example). | ||
| 442 | (when (and (zero? (getuid)) %build-group) | ||
| 443 | (chown %store-directory 0 %build-group)) | ||
| 444 | (chmod %store-directory #o1775) | ||
| 445 | ;; Inputs need to exist regardless of how we're getting the outputs of this | ||
| 446 | ;; derivation. | ||
| 447 | (ensure-input-outputs-exist (derivation-inputs drv)) | ||
| 448 | (format #t "Starting build of derivation ~a~%~%" drv) | ||
| 449 | (let*-values (((builder environment store-inputs) | ||
| 450 | (builder+environment+inputs drv (zero? (getuid)))) | ||
| 451 | ((output-specs) | ||
| 452 | (run-builder builder drv environment store-inputs))) | ||
| 453 | (if output-specs | ||
| 454 | (register-items (topologically-sorted output-specs)) | ||
| 455 | (throw 'derivation-build-failed drv)))) | ||
| 456 | |||
| 457 | (define (ensure-input-outputs-exist inputs) | ||
| 458 | "Call %build-derivation as necessary, recursively, to make the necessary | ||
| 459 | outputs of INPUTS exist." | ||
| 460 | (for-each | ||
| 461 | (lambda (input) | ||
| 462 | (let ((input-drv-path (derivation-input-path input))) | ||
| 463 | (unless (outputs-exist? input-drv-path | ||
| 464 | (derivation-input-sub-derivations input)) | ||
| 465 | (%build-derivation (read-derivation-from-file input-drv-path))))) | ||
| 466 | inputs)) | ||
| 467 | |||
| 468 | (define* (build-derivation drv | ||
| 469 | #:optional (outputs (derivation-output-names drv))) | ||
| 470 | "Given a <derivation> DRV with desired outputs OUTPUTS, build DRV if the | ||
| 471 | outputs don't already exist." | ||
| 472 | (unless (outputs-exist? (derivation-file-name drv) | ||
| 473 | outputs) | ||
| 474 | (%build-derivation drv))) | ||
