build: ruby-build-system: Fix typo.
[jackhill/guix/guix.git] / guix / derivations.scm
index ebeac31..e1073ea 100644 (file)
@@ -36,6 +36,8 @@
   #:use-module (guix memoization)
   #:use-module (guix combinators)
   #:use-module (guix deprecation)
+  #:use-module (guix diagnostics)
+  #:use-module (guix i18n)
   #:use-module (guix monads)
   #:use-module (gcrypt hash)
   #:use-module (guix base32)
@@ -69,6 +71,7 @@
             derivation-input-derivation
             derivation-input-sub-derivations
             derivation-input-output-paths
+            derivation-input-output-path
             valid-derivation-input?
 
             &derivation-error
@@ -219,6 +222,13 @@ download with a fixed hash (aka. `fetchurl')."
      (map (cut derivation->output-path drv <>)
           sub-drvs))))
 
+(define (derivation-input-output-path input)
+  "Return the output file name of INPUT.  If INPUT has more than one outputs,
+an error is raised."
+  (match input
+    (($ <derivation-input> drv (output))
+     (derivation->output-path drv output))))
+
 (define (valid-derivation-input? store input)
   "Return true if INPUT is valid--i.e., if all the outputs it requests are in
 the store."
@@ -293,78 +303,81 @@ result is the set of prerequisites of DRV not already in valid."
             (derivation-output-path (assoc-ref outputs sub-drv)))
           sub-drvs))))
 
-(define* (substitution-oracle store drv
+(define* (substitution-oracle store inputs-or-drv
                               #:key (mode (build-mode normal)))
   "Return a one-argument procedure that, when passed a store file name,
 returns a 'substitutable?' if it's substitutable and #f otherwise.
-The returned procedure
-knows about all substitutes for all the derivations listed in DRV, *except*
-those that are already valid (that is, it won't bother checking whether an
-item is substitutable if it's already on disk); it also knows about their
-prerequisites, unless they are themselves substitutable.
+
+The returned procedure knows about all substitutes for all the derivation
+inputs or derivations listed in INPUTS-OR-DRV, *except* those that are already
+valid (that is, it won't bother checking whether an item is substitutable if
+it's already on disk); it also knows about their prerequisites, unless they
+are themselves substitutable.
 
 Creating a single oracle (thus making a single 'substitutable-path-info' call) and
 reusing it is much more efficient than calling 'has-substitutes?' or similar
 repeatedly, because it avoids the costs associated with launching the
 substituter many times."
-  (define valid?
-    (cut valid-path? store <>))
-
   (define valid-input?
     (cut valid-derivation-input? store <>))
 
-  (define (dependencies drv)
-    ;; Skip prerequisite sub-trees of DRV whose root is valid.  This allows us
-    ;; to ask the substituter for just as much as needed, instead of asking it
-    ;; for the whole world, which can be significantly faster when substitute
-    ;; info is not already in cache.
-    ;; Also, skip derivations marked as non-substitutable.
-    (append-map (lambda (input)
-                  (let ((drv (read-derivation-from-file
-                              (derivation-input-path input))))
-                    (if (substitutable-derivation? drv)
-                        (derivation-input-output-paths input)
-                        '())))
-                (derivation-prerequisites drv valid-input?)))
-
-  (let* ((paths (delete-duplicates
-                 (concatenate
-                  (fold (lambda (drv result)
-                          (let ((self (match (derivation->output-paths drv)
-                                        (((names . paths) ...)
-                                         paths))))
-                            (cond ((eqv? mode (build-mode check))
-                                   (cons (dependencies drv) result))
-                                  ((not (substitutable-derivation? drv))
-                                   (cons (dependencies drv) result))
-                                  ((every valid? self)
-                                   result)
-                                  (else
-                                   (cons* self (dependencies drv) result)))))
-                        '()
-                        drv))))
-         (subst (fold (lambda (subst vhash)
-                        (vhash-cons (substitutable-path subst) subst
-                                    vhash))
-                      vlist-null
-                      (substitutable-path-info store paths))))
+  (define (closure inputs)
+    (let loop ((inputs inputs)
+               (closure '())
+               (visited (set)))
+      (match inputs
+        (()
+         (reverse closure))
+        ((input rest ...)
+         (let ((key (derivation-input-key input)))
+           (cond ((set-contains? visited key)
+                  (loop rest closure visited))
+                 ((valid-input? input)
+                  (loop rest closure (set-insert key visited)))
+                 (else
+                  (let ((drv (derivation-input-derivation input)))
+                    (loop (append (derivation-inputs drv) rest)
+                          (if (substitutable-derivation? drv)
+                              (cons input closure)
+                              closure)
+                          (set-insert key visited))))))))))
+
+  (let* ((inputs (closure (map (match-lambda
+                                 ((? derivation-input? input)
+                                  input)
+                                 ((? derivation? drv)
+                                  (derivation-input drv)))
+                               inputs-or-drv)))
+         (items  (append-map derivation-input-output-paths inputs))
+         (subst  (fold (lambda (subst vhash)
+                         (vhash-cons (substitutable-path subst) subst
+                                     vhash))
+                       vlist-null
+                       (substitutable-path-info store items))))
     (lambda (item)
       (match (vhash-assoc item subst)
         (#f #f)
         ((key . value) value)))))
 
+(define (dependencies-of-substitutables substitutables inputs)
+  "Return the subset of INPUTS whose output file names is among the references
+of SUBSTITUTABLES."
+  (let ((items (fold set-insert (set)
+                     (append-map substitutable-references substitutables))))
+    (filter (lambda (input)
+              (any (cut set-contains? items <>)
+                   (derivation-input-output-paths input)))
+            inputs)))
+
 (define* (derivation-build-plan store inputs
                                 #:key
                                 (mode (build-mode normal))
                                 (substitutable-info
                                  (substitution-oracle
-                                  store
-                                  (map derivation-input-derivation
-                                       inputs)
-                                  #:mode mode)))
+                                  store inputs #:mode mode)))
   "Given INPUTS, a list of derivation-inputs, return two values: the list of
-derivation to build, and the list of substitutable items that, together,
-allows INPUTS to be realized.
+derivations to build, and the list of substitutable items that, together,
+allow INPUTS to be realized.
 
 SUBSTITUTABLE-INFO must be a one-argument procedure similar to that returned
 by 'substitution-oracle'."
@@ -392,7 +405,9 @@ by 'substitution-oracle'."
       (()
        (values build substitute))
       ((input rest ...)
-       (let ((key (derivation-input-key input)))
+       (let ((key  (derivation-input-key input))
+             (deps (derivation-inputs
+                    (derivation-input-derivation input))))
          (cond ((set-contains? visited key)
                 (loop rest build substitute visited))
                ((input-built? input)
@@ -401,16 +416,17 @@ by 'substitution-oracle'."
                ((input-substitutable-info input)
                 =>
                 (lambda (substitutables)
-                  (loop rest build
+                  (loop (append (dependencies-of-substitutables substitutables
+                                                                deps)
+                                rest)
+                        build
                         (append substitutables substitute)
                         (set-insert key visited))))
                (else
-                (let ((deps (derivation-inputs
-                             (derivation-input-derivation input))))
-                  (loop (append deps rest)
-                        (cons (derivation-input-derivation input) build)
-                        substitute
-                        (set-insert key visited))))))))))
+                (loop (append deps rest)
+                      (cons (derivation-input-derivation input) build)
+                      substitute
+                      (set-insert key visited)))))))))
 
 (define-deprecated (derivation-prerequisites-to-build store drv #:rest rest)
   derivation-build-plan
@@ -652,12 +668,10 @@ list of name/path pairs of its outputs."
 ;;; Derivation primitive.
 ;;;
 
-(define derivation-path->base16-hash
-  (mlambda (file)
-    "Return a string containing the base16 representation of the hash of the
-derivation at FILE."
-    (bytevector->base16-string
-     (derivation-hash (read-derivation-from-file file)))))
+(define derivation-base16-hash
+  (mlambdaq (drv)
+    "Return a string containing the base16 representation of the hash of DRV."
+    (bytevector->base16-string (derivation-hash drv))))
 
 (define (derivation/masked-inputs drv)
   "Assuming DRV is a regular derivation (not fixed-output), replace the file
@@ -666,13 +680,12 @@ name of each input with that input's hash."
     (($ <derivation> outputs inputs sources
                      system builder args env-vars)
      (let ((inputs (map (match-lambda
-                          (($ <derivation-input> (= derivation-file-name path)
-                                                 sub-drvs)
-                           (let ((hash (derivation-path->base16-hash path)))
+                          (($ <derivation-input> drv sub-drvs)
+                           (let ((hash (derivation-base16-hash drv)))
                              (make-derivation-input hash sub-drvs))))
                         inputs)))
        (make-derivation outputs
-                        (sort inputs
+                        (sort (delete-duplicates inputs)
                               (lambda (drv1 drv2)
                                 (string<? (derivation-input-derivation drv1)
                                           (derivation-input-derivation drv2))))
@@ -702,16 +715,25 @@ name of each input with that input's hash."
        ;; character.
        (sha256 (derivation->bytevector (derivation/masked-inputs drv)))))))
 
+
+(define (warn-about-derivation-deprecation name)
+  ;; TRANSLATORS: 'derivation' must not be translated; it refers to the
+  ;; 'derivation' procedure.
+  (warning (G_ "in '~a': deprecated 'derivation' calling convention used~%")
+           name))
+
 (define* (derivation store name builder args
                      #:key
                      (system (%current-system)) (env-vars '())
-                     (inputs '()) (outputs '("out"))
+                     (inputs '()) (sources '())
+                     (outputs '("out"))
                      hash hash-algo recursive?
                      references-graphs
                      allowed-references disallowed-references
                      leaked-env-vars local-build?
                      (substitutable? #t)
-                     (properties '()))
+                     (properties '())
+                     (%deprecation-warning? #t))
   "Build a derivation with the given arguments, and return the resulting
 <derivation> object.  When HASH and HASH-ALGO are given, a
 fixed-output derivation is created---i.e., one whose result is known in
@@ -828,17 +850,28 @@ derivation.  It is kept as-is, uninterpreted, in the derivation."
             e
             outputs)))
 
+  (define-syntax-rule (warn-deprecation name)
+    (when %deprecation-warning?
+      (warn-about-derivation-deprecation name)))
+
   (define input->derivation-input
     (match-lambda
+      ((? derivation-input? input)
+       input)
       (((? derivation? drv))
+       (warn-deprecation name)
        (make-derivation-input drv '("out")))
       (((? derivation? drv) sub-drvs ...)
+       (warn-deprecation name)
        (make-derivation-input drv sub-drvs))
-      (_ #f)))
+      (_
+       (warn-deprecation name)
+       #f)))
 
   (define input->source
     (match-lambda
       (((? string? input) . _)
+       (warn-deprecation name)
        (if (direct-store-path? input)
            input
            (add-to-store store (basename input)
@@ -855,7 +888,8 @@ derivation.  It is kept as-is, uninterpreted, in the derivation."
                                                           hash recursive?)))
                           (sort outputs string<?)))
          (sources    (sort (delete-duplicates
-                            (filter-map input->source inputs))
+                            (append (filter-map input->source inputs)
+                                    sources))
                            string<?))
          (inputs     (sort (coalesce-duplicate-inputs
                             (filter-map input->derivation-input inputs))
@@ -886,8 +920,11 @@ long-running processes that know what they're doing.  Use with care!"
   ;; Typically this is meant to be used by Cuirass and Hydra, which can clear
   ;; caches when they start evaluating packages for another architecture.
   (invalidate-memoization! derivation->bytevector)
-  (invalidate-memoization! derivation-path->base16-hash)
-  (hash-clear! %derivation-cache))
+  (invalidate-memoization! derivation-base16-hash)
+
+  ;; FIXME: Comment out to work around <https://bugs.gnu.org/36487>.
+  ;; (hash-clear! %derivation-cache)
+  )
 
 (define derivation-properties
   (mlambdaq (drv)
@@ -923,13 +960,10 @@ recursively."
 
   (define input->output-paths
     (match-lambda
-     (((? derivation? drv))
-      (list (derivation->output-path drv)))
-     (((? derivation? drv) sub-drvs ...)
-      (map (cut derivation->output-path drv <>)
-           sub-drvs))
-     ((file)
-      (list file))))
+      ((? derivation-input? input)
+       (derivation-input-output-paths input))
+      ((? string? file)
+       (list file))))
 
   (let ((mapping (fold (lambda (pair result)
                          (match pair
@@ -945,16 +979,14 @@ recursively."
       ;; in the format used in 'derivation' calls.
       (mlambda (input loop)
         (match input
-          (($ <derivation-input> (= derivation-file-name path)
-                                 (sub-drvs ...))
-           (match (vhash-assoc path mapping)
+          (($ <derivation-input> drv (sub-drvs ...))
+           (match (vhash-assoc (derivation-file-name drv) mapping)
              ((_ . (? derivation? replacement))
-              (cons replacement sub-drvs))
-             ((_ . replacement)
-              (list replacement))
+              (derivation-input replacement sub-drvs))
+             ((_ . (? string? source))
+              source)
              (#f
-              (let* ((drv (loop (read-derivation-from-file path))))
-                (cons drv sub-drvs))))))))
+              (derivation-input (loop drv) sub-drvs)))))))
 
     (let loop ((drv drv))
       (let* ((inputs       (map (cut rewritten-input <> loop)
@@ -993,7 +1025,8 @@ recursively."
                                         . ,(substitute value initial
                                                        replacements))))
                                     (derivation-builder-environment-vars drv))
-                    #:inputs (append (map list sources) inputs)
+                    #:inputs (filter derivation-input? inputs)
+                    #:sources (append sources (filter string? inputs))
                     #:outputs (derivation-output-names drv)
                     #:hash (match (derivation-outputs drv)
                              ((($ <derivation-output> _ algo hash))
@@ -1314,6 +1347,10 @@ and PROPERTIES."
                   ,@(if mod-dir `("-L" ,mod-dir) '())
                   ,builder)
 
+                ;; 'build-expression->derivation' is somewhat deprecated so
+                ;; don't bother warning here.
+                #:%deprecation-warning? #f
+
                 #:system system
 
                 #:inputs `((,(or guile-for-build (%guile-for-build)))