diff --git a/guix/grafts.scm b/guix/grafts.scm index f93da32981..48f4c212f7 100644 --- a/guix/grafts.scm +++ b/guix/grafts.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2014-2022 Ludovic Courtès +;;; Copyright © 2014-2023 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -176,11 +176,8 @@ (define (references* items) (append-map (cut references/cached store <>) items)))) (append-map (cut references/cached store <>) items))) - (let ((refs (references* (map (cut derivation->output-path drv <>) - outputs))) - (self (match (derivation->output-paths drv) - (((names . items) ...) - items)))) + (let* ((self (map (cut derivation->output-path drv <>) outputs)) + (refs (references* self))) (remove (cut member <> self) refs))) (define %graft-cache @@ -207,7 +204,7 @@ (define-syntax-rule (with-cache key exp ...) (return result))))))) (define (reference-origins drv items) - "Return the derivation/output pairs among the inputs of DRV, recursively, + "Return the derivation/output pairs among DRV and its inputs, recursively, that produce ITEMS. Elements of ITEMS not produced by a derivation (i.e., it's a content-addressed \"source\"), or not produced by a dependency of DRV, have no corresponding element in the resulting list." @@ -238,13 +235,10 @@ (define (lookup-derivers drv result items) ((set-contains? visited drv) (loop rest items result visited)) (else - (let* ((inputs - (map derivation-input-derivation - (derivation-inputs drv))) - (result items - (fold2 lookup-derivers - result items inputs))) - (loop (append rest inputs) + (let ((result items (lookup-derivers drv result items))) + (loop (append rest + (map derivation-input-derivation + (derivation-inputs drv))) items result (set-insert drv visited))))))))) @@ -258,16 +252,17 @@ (define* (cumulative-grafts store drv grafts This is a monadic procedure in %STATE-MONAD where the state is a vhash mapping derivations to the corresponding set of grafts." - (define (graft-origin? drv graft) - ;; Return true if DRV corresponds to the origin of GRAFT. + (define (graft-origin? drv output graft) + ;; Return true if DRV and OUTPUT correspond to the origin of GRAFT. (match graft - (($ (? derivation? origin) output) - (match (assoc-ref (derivation->output-paths drv) output) - ((? string? result) - (string=? result - (derivation->output-path origin output))) - (_ - #f))) + (($ (? derivation? origin) origin-output) + (and (string=? origin-output output) + (match (assoc-ref (derivation->output-paths drv) output) + ((? string? result) + (string=? result + (derivation->output-path origin output))) + (_ + #f)))) (_ #f))) @@ -278,7 +273,7 @@ (define (dependency-grafts items) ((drv . output) ;; If GRAFTS already contains a graft from DRV, do not ;; override it. - (if (find (cut graft-origin? drv <>) grafts) + (if (find (cut graft-origin? drv output <>) grafts) (state-return grafts) (cumulative-grafts store drv grafts #:outputs (list output) diff --git a/tests/grafts.scm b/tests/grafts.scm index 63dbb13830..24c4d24359 100644 --- a/tests/grafts.scm +++ b/tests/grafts.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2014-2019, 2022 Ludovic Courtès +;;; Copyright © 2014-2019, 2022-2023 Ludovic Courtès ;;; Copyright © 2021 Mark H Weaver ;;; ;;; This file is part of GNU Guix. @@ -268,6 +268,54 @@ (define %mkdir (readlink (string-append out "/two"))) (file-exists? (string-append out "/one/replacement"))))))) +(test-assert "graft-derivation, multiple outputs need to be replaced" + ;; Build a reference graph like this: + ;; + ;; ,- p2:out --. + ;; v v + ;; p1:one <---- p1:two + ;; | + ;; `-> p0 + ;; + ;; Graft p0r in lieu of p0, and make sure all the paths from the grafted p2 + ;; lead to p0r. See . + (let* ((p0 (build-expression->derivation + %store "p0" '(mkdir (assoc-ref %outputs "out")))) + (p0r (build-expression->derivation + %store "P0" + '(let ((out (assoc-ref %outputs "out"))) + (mkdir out) + (call-with-output-file (string-append out "/replacement") + (const #t))))) + (p1 (build-expression->derivation + %store "p1" + `(let ((one (assoc-ref %outputs "one")) + (two (assoc-ref %outputs "two")) + (p0 (assoc-ref %build-inputs "p0"))) + (mkdir one) + (mkdir two) + (symlink p0 (string-append one "/p0")) + (symlink one (string-append two "/link"))) + #:inputs `(("p0" ,p0)) + #:outputs '("one" "two"))) + (p2 (build-expression->derivation + %store "p2" + `(let ((out (assoc-ref %outputs "out"))) + (mkdir out) (chdir out) + (symlink (assoc-ref %build-inputs "p1:one") "one") + (symlink (assoc-ref %build-inputs "p1:two") "two")) + #:inputs `(("p1:one" ,p1 "one") + ("p1:two" ,p1 "two")))) + (p0g (list (graft + (origin p0) + (replacement p0r)))) + (p2d (graft-derivation %store p2 p0g))) + + (build-derivations %store (list p2d)) + (let ((out (derivation->output-path (pk 'p2d p2d)))) + (equal? (stat (string-append out "/one/p0/replacement")) + (stat (string-append out "/two/link/p0/replacement")))))) + (test-assert "graft-derivation with #:outputs" ;; Call 'graft-derivation' with a narrowed set of outputs passed as ;; #:outputs.