2016-02-22 10:29:44 -05:00
|
|
|
|
;;; GNU Guix --- Functional package management for GNU
|
|
|
|
|
;;; Copyright © 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.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/>.
|
|
|
|
|
|
|
|
|
|
(define-module (guix grafts)
|
2016-02-27 17:06:50 -05:00
|
|
|
|
#:use-module (guix store)
|
|
|
|
|
#:use-module (guix monads)
|
2016-02-22 10:29:44 -05:00
|
|
|
|
#:use-module (guix records)
|
|
|
|
|
#:use-module (guix derivations)
|
|
|
|
|
#:use-module ((guix utils) #:select (%current-system))
|
|
|
|
|
#:use-module (srfi srfi-1)
|
2016-02-26 06:42:15 -05:00
|
|
|
|
#:use-module (srfi srfi-9 gnu)
|
2016-02-27 17:06:50 -05:00
|
|
|
|
#:use-module (srfi srfi-11)
|
2016-02-22 10:29:44 -05:00
|
|
|
|
#:use-module (srfi srfi-26)
|
2016-03-04 15:49:08 -05:00
|
|
|
|
#:use-module (srfi srfi-34)
|
2016-02-22 10:29:44 -05:00
|
|
|
|
#:use-module (ice-9 match)
|
2016-03-04 15:49:08 -05:00
|
|
|
|
#:use-module (ice-9 vlist)
|
2016-02-22 10:29:44 -05:00
|
|
|
|
#:export (graft?
|
|
|
|
|
graft
|
|
|
|
|
graft-origin
|
|
|
|
|
graft-replacement
|
|
|
|
|
graft-origin-output
|
|
|
|
|
graft-replacement-output
|
|
|
|
|
|
|
|
|
|
graft-derivation
|
2016-02-27 17:06:50 -05:00
|
|
|
|
graft-derivation/shallow
|
2016-02-22 10:29:44 -05:00
|
|
|
|
|
|
|
|
|
%graft?
|
|
|
|
|
set-grafting))
|
|
|
|
|
|
|
|
|
|
(define-record-type* <graft> graft make-graft
|
|
|
|
|
graft?
|
|
|
|
|
(origin graft-origin) ;derivation | store item
|
|
|
|
|
(origin-output graft-origin-output ;string | #f
|
|
|
|
|
(default "out"))
|
|
|
|
|
(replacement graft-replacement) ;derivation | store item
|
|
|
|
|
(replacement-output graft-replacement-output ;string | #f
|
|
|
|
|
(default "out")))
|
|
|
|
|
|
2016-02-26 06:42:15 -05:00
|
|
|
|
(define (write-graft graft port)
|
|
|
|
|
"Write a concise representation of GRAFT to PORT."
|
|
|
|
|
(define (->string thing output)
|
|
|
|
|
(if (derivation? thing)
|
|
|
|
|
(derivation->output-path thing output)
|
|
|
|
|
thing))
|
|
|
|
|
|
|
|
|
|
(match graft
|
|
|
|
|
(($ <graft> origin origin-output replacement replacement-output)
|
|
|
|
|
(format port "#<graft ~a ==> ~a ~a>"
|
|
|
|
|
(->string origin origin-output)
|
|
|
|
|
(->string replacement replacement-output)
|
|
|
|
|
(number->string (object-address graft) 16)))))
|
|
|
|
|
|
|
|
|
|
(set-record-type-printer! <graft> write-graft)
|
|
|
|
|
|
2016-02-27 17:06:50 -05:00
|
|
|
|
(define (graft-origin-file-name graft)
|
|
|
|
|
"Return the output file name of the origin of GRAFT."
|
|
|
|
|
(match graft
|
|
|
|
|
(($ <graft> (? derivation? origin) output)
|
|
|
|
|
(derivation->output-path origin output))
|
|
|
|
|
(($ <graft> (? string? item))
|
|
|
|
|
item)))
|
|
|
|
|
|
|
|
|
|
(define* (graft-derivation/shallow store drv grafts
|
|
|
|
|
#:key
|
|
|
|
|
(name (derivation-name drv))
|
|
|
|
|
(guile (%guile-for-build))
|
|
|
|
|
(system (%current-system)))
|
2016-02-22 10:29:44 -05:00
|
|
|
|
"Return a derivation called NAME, based on DRV but with all the GRAFTS
|
2016-02-27 17:06:50 -05:00
|
|
|
|
applied. This procedure performs \"shallow\" grafting in that GRAFTS are not
|
|
|
|
|
recursively applied to dependencies of DRV."
|
2016-02-22 10:29:44 -05:00
|
|
|
|
;; XXX: Someday rewrite using gexps.
|
|
|
|
|
(define mapping
|
|
|
|
|
;; List of store item pairs.
|
|
|
|
|
(map (match-lambda
|
|
|
|
|
(($ <graft> source source-output target target-output)
|
|
|
|
|
(cons (if (derivation? source)
|
|
|
|
|
(derivation->output-path source source-output)
|
|
|
|
|
source)
|
|
|
|
|
(if (derivation? target)
|
|
|
|
|
(derivation->output-path target target-output)
|
|
|
|
|
target))))
|
|
|
|
|
grafts))
|
|
|
|
|
|
|
|
|
|
(define outputs
|
2016-02-27 17:28:35 -05:00
|
|
|
|
(map (match-lambda
|
|
|
|
|
((name . output)
|
|
|
|
|
(cons name (derivation-output-path output))))
|
|
|
|
|
(derivation-outputs drv)))
|
2016-02-22 10:29:44 -05:00
|
|
|
|
|
|
|
|
|
(define output-names
|
2016-02-27 17:10:48 -05:00
|
|
|
|
(derivation-output-names drv))
|
2016-02-22 10:29:44 -05:00
|
|
|
|
|
|
|
|
|
(define build
|
|
|
|
|
`(begin
|
|
|
|
|
(use-modules (guix build graft)
|
|
|
|
|
(guix build utils)
|
|
|
|
|
(ice-9 match))
|
|
|
|
|
|
2016-02-27 17:28:35 -05:00
|
|
|
|
(let* ((old-outputs ',outputs)
|
|
|
|
|
(mapping (append ',mapping
|
|
|
|
|
(map (match-lambda
|
|
|
|
|
((name . file)
|
|
|
|
|
(cons (assoc-ref old-outputs name)
|
|
|
|
|
file)))
|
|
|
|
|
%outputs))))
|
2016-02-22 10:29:44 -05:00
|
|
|
|
(for-each (lambda (input output)
|
|
|
|
|
(format #t "grafting '~a' -> '~a'...~%" input output)
|
|
|
|
|
(force-output)
|
2016-02-27 17:28:35 -05:00
|
|
|
|
(rewrite-directory input output mapping))
|
|
|
|
|
(match old-outputs
|
|
|
|
|
(((names . files) ...)
|
|
|
|
|
files))
|
2016-02-22 10:29:44 -05:00
|
|
|
|
(match %outputs
|
|
|
|
|
(((names . files) ...)
|
|
|
|
|
files))))))
|
|
|
|
|
|
|
|
|
|
(define add-label
|
|
|
|
|
(cut cons "x" <>))
|
|
|
|
|
|
|
|
|
|
(match grafts
|
|
|
|
|
((($ <graft> sources source-outputs targets target-outputs) ...)
|
|
|
|
|
(let ((sources (zip sources source-outputs))
|
|
|
|
|
(targets (zip targets target-outputs)))
|
|
|
|
|
(build-expression->derivation store name build
|
|
|
|
|
#:system system
|
|
|
|
|
#:guile-for-build guile
|
|
|
|
|
#:modules '((guix build graft)
|
|
|
|
|
(guix build utils))
|
|
|
|
|
#:inputs `(,@(map (lambda (out)
|
|
|
|
|
`("x" ,drv ,out))
|
|
|
|
|
output-names)
|
|
|
|
|
,@(append (map add-label sources)
|
|
|
|
|
(map add-label targets)))
|
|
|
|
|
#:outputs output-names
|
|
|
|
|
#:local-build? #t)))))
|
2016-02-27 17:06:50 -05:00
|
|
|
|
(define (item->deriver store item)
|
|
|
|
|
"Return two values: the derivation that led to ITEM (a store item), and the
|
|
|
|
|
name of the output of that derivation ITEM corresponds to (for example
|
|
|
|
|
\"out\"). When ITEM has no deriver, for instance because it is a plain file,
|
|
|
|
|
#f and #f are returned."
|
|
|
|
|
(match (valid-derivers store item)
|
|
|
|
|
(() ;ITEM is a plain file
|
|
|
|
|
(values #f #f))
|
|
|
|
|
((drv-file _ ...)
|
|
|
|
|
(let ((drv (call-with-input-file drv-file read-derivation)))
|
|
|
|
|
(values drv
|
|
|
|
|
(any (match-lambda
|
|
|
|
|
((name . path)
|
|
|
|
|
(and (string=? item path) name)))
|
|
|
|
|
(derivation->output-paths drv)))))))
|
|
|
|
|
|
2016-03-04 15:49:08 -05:00
|
|
|
|
(define (non-self-references references drv outputs)
|
2016-02-27 17:06:50 -05:00
|
|
|
|
"Return the list of references of the OUTPUTS of DRV, excluding self
|
2016-03-04 15:49:08 -05:00
|
|
|
|
references. Call REFERENCES to get the list of references."
|
|
|
|
|
(let ((refs (append-map (compose references
|
|
|
|
|
(cut derivation->output-path drv <>))
|
2016-02-27 17:06:50 -05:00
|
|
|
|
outputs))
|
|
|
|
|
(self (match (derivation->output-paths drv)
|
|
|
|
|
(((names . items) ...)
|
|
|
|
|
items))))
|
|
|
|
|
(remove (cut member <> self) refs)))
|
|
|
|
|
|
2016-03-04 15:49:08 -05:00
|
|
|
|
(define (references-oracle store drv)
|
|
|
|
|
"Return a one-argument procedure that, when passed the file name of DRV's
|
|
|
|
|
outputs or their dependencies, returns the list of references of that item.
|
|
|
|
|
Use either local info or substitute info; build DRV if no information is
|
|
|
|
|
available."
|
|
|
|
|
(define (output-paths drv)
|
|
|
|
|
(match (derivation->output-paths drv)
|
|
|
|
|
(((names . items) ...)
|
|
|
|
|
items)))
|
|
|
|
|
|
|
|
|
|
(define (references* items)
|
|
|
|
|
(guard (c ((nix-protocol-error? c)
|
|
|
|
|
;; As a last resort, build DRV and query the references of the
|
|
|
|
|
;; build result.
|
2016-03-14 17:49:51 -04:00
|
|
|
|
|
|
|
|
|
;; Warm up the narinfo cache, otherwise each derivation build
|
|
|
|
|
;; will result in one HTTP request to get one narinfo, which is
|
|
|
|
|
;; much less efficient than fetching them all upfront.
|
|
|
|
|
(substitution-oracle store (list drv))
|
|
|
|
|
|
2016-03-04 15:49:08 -05:00
|
|
|
|
(and (build-derivations store (list drv))
|
|
|
|
|
(map (cut references store <>) items))))
|
|
|
|
|
(references/substitutes store items)))
|
|
|
|
|
|
|
|
|
|
(let loop ((items (output-paths drv))
|
|
|
|
|
(result vlist-null))
|
|
|
|
|
(match items
|
|
|
|
|
(()
|
|
|
|
|
(lambda (item)
|
|
|
|
|
(match (vhash-assoc item result)
|
|
|
|
|
((_ . refs) refs)
|
|
|
|
|
(#f #f))))
|
|
|
|
|
(_
|
|
|
|
|
(let* ((refs (references* items))
|
|
|
|
|
(result (fold vhash-cons result items refs)))
|
|
|
|
|
(loop (remove (cut vhash-assoc <> result)
|
|
|
|
|
(delete-duplicates (concatenate refs) string=?))
|
|
|
|
|
result))))))
|
|
|
|
|
|
2016-02-27 17:06:50 -05:00
|
|
|
|
(define* (cumulative-grafts store drv grafts
|
2016-03-04 15:49:08 -05:00
|
|
|
|
references
|
2016-02-27 17:06:50 -05:00
|
|
|
|
#:key
|
|
|
|
|
(outputs (derivation-output-names drv))
|
|
|
|
|
(guile (%guile-for-build))
|
|
|
|
|
(system (%current-system)))
|
|
|
|
|
"Augment GRAFTS with additional grafts resulting from the application of
|
2016-03-04 15:49:08 -05:00
|
|
|
|
GRAFTS to the dependencies of DRV; REFERENCES must be a one-argument procedure
|
|
|
|
|
that returns the list of references of the store item it is given. Return the
|
2016-03-04 17:10:28 -05:00
|
|
|
|
resulting list of grafts.
|
|
|
|
|
|
|
|
|
|
This is a monadic procedure in %STATE-MONAD where the state is a vhash mapping
|
|
|
|
|
derivations to the corresponding set of grafts."
|
2016-02-27 17:06:50 -05:00
|
|
|
|
(define (dependency-grafts item)
|
|
|
|
|
(let-values (((drv output) (item->deriver store item)))
|
|
|
|
|
(if drv
|
2016-03-04 15:49:08 -05:00
|
|
|
|
(cumulative-grafts store drv grafts references
|
2016-02-27 17:06:50 -05:00
|
|
|
|
#:outputs (list output)
|
|
|
|
|
#:guile guile
|
|
|
|
|
#:system system)
|
2016-03-04 17:10:28 -05:00
|
|
|
|
(state-return grafts))))
|
|
|
|
|
|
|
|
|
|
(define (return/cache cache value)
|
|
|
|
|
(mbegin %store-monad
|
|
|
|
|
(set-current-state (vhash-consq drv value cache))
|
|
|
|
|
(return value)))
|
2016-02-27 17:06:50 -05:00
|
|
|
|
|
2016-03-04 17:10:28 -05:00
|
|
|
|
(mlet %state-monad ((cache (current-state)))
|
|
|
|
|
(match (vhash-assq drv cache)
|
|
|
|
|
((_ . grafts) ;hit
|
|
|
|
|
(return grafts))
|
|
|
|
|
(#f ;miss
|
|
|
|
|
(match (non-self-references references drv outputs)
|
|
|
|
|
(() ;no dependencies
|
|
|
|
|
(return/cache cache grafts))
|
|
|
|
|
(deps ;one or more dependencies
|
|
|
|
|
(mlet %state-monad ((grafts (mapm %state-monad dependency-grafts deps))
|
|
|
|
|
(cache (current-state)))
|
|
|
|
|
(let* ((grafts (delete-duplicates (concatenate grafts) equal?))
|
|
|
|
|
(origins (map graft-origin-file-name grafts)))
|
|
|
|
|
(if (find (cut member <> deps) origins)
|
|
|
|
|
(let* ((new (graft-derivation/shallow store drv grafts
|
|
|
|
|
#:guile guile
|
|
|
|
|
#:system system))
|
|
|
|
|
(grafts (cons (graft (origin drv) (replacement new))
|
|
|
|
|
grafts)))
|
|
|
|
|
(return/cache cache grafts))
|
|
|
|
|
(return/cache cache grafts))))))))))
|
2016-02-27 17:06:50 -05:00
|
|
|
|
|
|
|
|
|
(define* (graft-derivation store drv grafts
|
|
|
|
|
#:key (guile (%guile-for-build))
|
|
|
|
|
(system (%current-system)))
|
|
|
|
|
"Applied GRAFTS to DRV and all its dependencies, recursively. That is, if
|
|
|
|
|
GRAFTS apply only indirectly to DRV, graft the dependencies of DRV, and graft
|
|
|
|
|
DRV itself to refer to those grafted dependencies."
|
|
|
|
|
|
2016-03-04 15:49:08 -05:00
|
|
|
|
;; First, pre-compute the dependency tree of the outputs of DRV. Do this
|
|
|
|
|
;; upfront to have as much parallelism as possible when querying substitute
|
|
|
|
|
;; info or when building DRV.
|
|
|
|
|
(define references
|
|
|
|
|
(references-oracle store drv))
|
2016-02-27 17:06:50 -05:00
|
|
|
|
|
2016-03-04 17:10:28 -05:00
|
|
|
|
(match (run-with-state
|
|
|
|
|
(cumulative-grafts store drv grafts references
|
|
|
|
|
#:guile guile #:system system)
|
|
|
|
|
vlist-null) ;the initial cache
|
2016-02-27 17:06:50 -05:00
|
|
|
|
((first . rest)
|
|
|
|
|
;; If FIRST is not a graft for DRV, it means that GRAFTS are not
|
|
|
|
|
;; applicable to DRV and nothing needs to be done.
|
|
|
|
|
(if (equal? drv (graft-origin first))
|
|
|
|
|
(graft-replacement first)
|
|
|
|
|
drv))))
|
2016-02-22 10:29:44 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; The following might feel more at home in (guix packages) but since (guix
|
|
|
|
|
;; gexp), which is a lower level, needs them, we put them here.
|
|
|
|
|
|
|
|
|
|
(define %graft?
|
|
|
|
|
;; Whether to honor package grafts by default.
|
|
|
|
|
(make-parameter #t))
|
|
|
|
|
|
|
|
|
|
(define (set-grafting enable?)
|
|
|
|
|
"This monadic procedure enables grafting when ENABLE? is true, and disables
|
|
|
|
|
it otherwise. It returns the previous setting."
|
|
|
|
|
(lambda (store)
|
|
|
|
|
(values (%graft? enable?) store)))
|
|
|
|
|
|
|
|
|
|
;;; grafts.scm ends here
|