diff --git a/guix/derivations.scm b/guix/derivations.scm index d4f697477b..76593f373b 100644 --- a/guix/derivations.scm +++ b/guix/derivations.scm @@ -176,6 +176,11 @@ download with a fixed hash (aka. `fetchurl')." #t) (_ #f))) +(define (derivation-input." + (string." @@ -190,6 +195,30 @@ the store." (every (cut valid-path? store <>) (derivation-input-output-paths input))) +(define (coalesce-duplicate-inputs inputs) + "Return a list of inputs, such that when INPUTS contains the same DRV twice, +they are coalesced, with their sub-derivations merged. This is needed because +Nix itself keeps only one of them." + (fold (lambda (input result) + (match input + (($ path sub-drvs) + ;; XXX: quadratic + (match (find (match-lambda + (($ p s) + (string=? p path))) + result) + (#f + (cons input result)) + ((and dup ($ _ sub-drvs2)) + ;; Merge DUP with INPUT. + (let ((sub-drvs (delete-duplicates + (append sub-drvs sub-drvs2)))) + (cons (make-derivation-input path + (sort sub-drvs string path sub-drvs) - ;; XXX: quadratic - (match (find (match-lambda - (($ p s) - (string=? p path))) - result) - (#f - (cons input result)) - ((and dup ($ _ sub-drvs2)) - ;; Merge DUP with INPUT. - (let ((sub-drvs (delete-duplicates - (append sub-drvs sub-drvs2)))) - (cons (make-derivation-input path sub-drvs) - (delq dup result)))))))) - '() - inputs)) - (define (write-output output port) (match output ((name . ($ path hash-algo hash recursive?)) @@ -515,7 +521,7 @@ that form." (display "(" port) (write path port) (display "," port) - (write-string-list (sort sub-drvs string outputs inputs sources system builder args env-vars) (display "Derive(" port) - (write-list (sort outputs - (lambda (o1 o2) - (stringstring @@ -653,7 +644,10 @@ derivation at FILE." (let ((hash (derivation-path->base16-hash path))) (make-derivation-input hash sub-drvs)))) inputs)) - (drv (make-derivation outputs inputs sources + (drv (make-derivation outputs + (sort (coalesce-duplicate-inputs inputs) + derivation-inputderivation-input + (match-lambda + (((? derivation? drv)) + (make-derivation-input (derivation-file-name drv) '("out"))) + (((? derivation? drv) sub-drvs ...) + (make-derivation-input (derivation-file-name drv) sub-drvs)) + (((? direct-store-path? input)) + (make-derivation-input input '("out"))) + (((? direct-store-path? input) sub-drvs ...) + (make-derivation-input input sub-drvs)) + ((input . _) + (let ((path (add-to-store store (basename input) + #t "sha256" input))) + (make-derivation-input path '()))))) + + ;; Note: lists are sorted alphabetically, to conform with the behavior of + ;; C++ `std::map' in Nix itself. + (let* ((outputs (map (lambda (name) ;; Return outputs with an empty path. (cons name (make-derivation-output "" hash-algo hash recursive?))) - outputs)) - (inputs (map (match-lambda - (((? derivation? drv)) - (make-derivation-input (derivation-file-name drv) - '("out"))) - (((? derivation? drv) sub-drvs ...) - (make-derivation-input (derivation-file-name drv) - sub-drvs)) - (((? direct-store-path? input)) - (make-derivation-input input '("out"))) - (((? direct-store-path? input) sub-drvs ...) - (make-derivation-input input sub-drvs)) - ((input . _) - (let ((path (add-to-store store - (basename input) - #t "sha256" input))) - (make-derivation-input path '())))) - (delete-duplicates inputs))) - (env-vars (env-vars-with-empty-outputs (user+system-env-vars))) + (sort outputs stringderivation-input + (delete-duplicates inputs))) + derivation-inputstring drv) - (map derivation-input-path - inputs)))) + (map derivation-input-path inputs)))) (set-file-name drv file)))) (define* (map-derivation store drv mapping diff --git a/tests/derivations.scm b/tests/derivations.scm index cb7196e2a9..d8553b223e 100644 --- a/tests/derivations.scm +++ b/tests/derivations.scm @@ -367,6 +367,33 @@ (and (eq? 'one (call-with-input-file one read)) (eq? 'two (call-with-input-file two read))))))) +(test-assert "read-derivation vs. derivation" + ;; Make sure 'derivation' and 'read-derivation' return objects that are + ;; identical. + (let* ((sources (unfold (cut >= <> 10) + (lambda (n) + (add-text-to-store %store + (format #f "input~a" n) + (random-text))) + 1+ + 0)) + (inputs (map (lambda (file) + (derivation %store "derivation-input" + %bash '() + #:inputs `((,%bash) (,file)))) + sources)) + (builder (add-text-to-store %store "builder.sh" + "echo one > $one ; echo two > $two" + '())) + (drv (derivation %store "derivation" + %bash `(,builder) + #:inputs `((,%bash) (,builder) + ,@(map list (append sources inputs))) + #:outputs '("two" "one"))) + (drv* (call-with-input-file (derivation-file-name drv) + read-derivation))) + (equal? drv* drv))) + (test-assert "multiple-output derivation, derivation-path->output-path" (let* ((builder (add-text-to-store %store "builder.sh" "echo one > $out ; echo two > $second"