utils: Add 'cc-for-target'.
[jackhill/guix/guix.git] / guix / grafts.scm
CommitLineData
7adf9b84 1;;; GNU Guix --- Functional package management for GNU
71085430 2;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
7adf9b84
LC
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(define-module (guix grafts)
c22a1324
LC
20 #:use-module (guix store)
21 #:use-module (guix monads)
7adf9b84
LC
22 #:use-module (guix records)
23 #:use-module (guix derivations)
24 #:use-module ((guix utils) #:select (%current-system))
aad086d8 25 #:use-module (guix sets)
7adf9b84 26 #:use-module (srfi srfi-1)
acb01e37 27 #:use-module (srfi srfi-9 gnu)
7adf9b84 28 #:use-module (srfi srfi-26)
c90cb5c9 29 #:use-module (srfi srfi-34)
7adf9b84 30 #:use-module (ice-9 match)
c90cb5c9 31 #:use-module (ice-9 vlist)
7adf9b84
LC
32 #:export (graft?
33 graft
34 graft-origin
35 graft-replacement
36 graft-origin-output
37 graft-replacement-output
38
39 graft-derivation
c22a1324 40 graft-derivation/shallow
7adf9b84
LC
41
42 %graft?
c6080c32
LC
43 set-grafting
44 grafting?))
7adf9b84
LC
45
46(define-record-type* <graft> graft make-graft
47 graft?
48 (origin graft-origin) ;derivation | store item
49 (origin-output graft-origin-output ;string | #f
50 (default "out"))
51 (replacement graft-replacement) ;derivation | store item
52 (replacement-output graft-replacement-output ;string | #f
53 (default "out")))
54
acb01e37
LC
55(define (write-graft graft port)
56 "Write a concise representation of GRAFT to PORT."
57 (define (->string thing output)
58 (if (derivation? thing)
59 (derivation->output-path thing output)
60 thing))
61
62 (match graft
63 (($ <graft> origin origin-output replacement replacement-output)
64 (format port "#<graft ~a ==> ~a ~a>"
65 (->string origin origin-output)
66 (->string replacement replacement-output)
67 (number->string (object-address graft) 16)))))
68
69(set-record-type-printer! <graft> write-graft)
70
c22a1324
LC
71(define (graft-origin-file-name graft)
72 "Return the output file name of the origin of GRAFT."
73 (match graft
74 (($ <graft> (? derivation? origin) output)
75 (derivation->output-path origin output))
76 (($ <graft> (? string? item))
77 item)))
78
79(define* (graft-derivation/shallow store drv grafts
80 #:key
81 (name (derivation-name drv))
fd7d1235 82 (outputs (derivation-output-names drv))
c22a1324
LC
83 (guile (%guile-for-build))
84 (system (%current-system)))
fd7d1235
LC
85 "Return a derivation called NAME, which applies GRAFTS to the specified
86OUTPUTS of DRV. This procedure performs \"shallow\" grafting in that GRAFTS
87are not recursively applied to dependencies of DRV."
7adf9b84
LC
88 ;; XXX: Someday rewrite using gexps.
89 (define mapping
90 ;; List of store item pairs.
91 (map (match-lambda
92 (($ <graft> source source-output target target-output)
93 (cons (if (derivation? source)
94 (derivation->output-path source source-output)
95 source)
96 (if (derivation? target)
97 (derivation->output-path target target-output)
98 target))))
99 grafts))
100
fd7d1235
LC
101 (define output-pairs
102 (map (lambda (output)
103 (cons output
104 (derivation-output-path
105 (assoc-ref (derivation-outputs drv) output))))
106 outputs))
7adf9b84
LC
107
108 (define build
109 `(begin
110 (use-modules (guix build graft)
111 (guix build utils)
112 (ice-9 match))
113
fd7d1235 114 (let* ((old-outputs ',output-pairs)
f376dc3a
LC
115 (mapping (append ',mapping
116 (map (match-lambda
117 ((name . file)
118 (cons (assoc-ref old-outputs name)
119 file)))
120 %outputs))))
e4297aa8 121 (graft old-outputs %outputs mapping))))
7adf9b84
LC
122
123 (define add-label
124 (cut cons "x" <>))
125
64fd1c01
LC
126 (define properties
127 `((type . graft)
128 (graft (count . ,(length grafts)))))
129
7adf9b84
LC
130 (match grafts
131 ((($ <graft> sources source-outputs targets target-outputs) ...)
132 (let ((sources (zip sources source-outputs))
133 (targets (zip targets target-outputs)))
134 (build-expression->derivation store name build
135 #:system system
136 #:guile-for-build guile
137 #:modules '((guix build graft)
93c33389
LC
138 (guix build utils)
139 (guix build debug-link)
140 (guix elf))
7adf9b84
LC
141 #:inputs `(,@(map (lambda (out)
142 `("x" ,drv ,out))
fd7d1235 143 outputs)
7adf9b84
LC
144 ,@(append (map add-label sources)
145 (map add-label targets)))
fd7d1235 146 #:outputs outputs
7bc5657f
LC
147
148 ;; Grafts are computationally cheap so no
149 ;; need to offload or substitute.
64fd1c01 150 #:local-build? #t
7bc5657f
LC
151 #:substitutable? #f
152
64fd1c01 153 #:properties properties)))))
c22a1324 154
4b75a706 155(define (non-self-references store drv outputs)
c22a1324 156 "Return the list of references of the OUTPUTS of DRV, excluding self
4b75a706 157references."
c90cb5c9 158 (define (references* items)
71085430 159 ;; Return the references of ITEMS.
f9e8a123 160 (guard (c ((store-protocol-error? c)
71085430 161 ;; ITEMS are not in store so build INPUT first.
4b75a706
LC
162 (and (build-derivations store (list drv))
163 (append-map (cut references/cached store <>) items))))
164 (append-map (cut references/cached store <>) items)))
c90cb5c9 165
4b75a706
LC
166 (let ((refs (references* (map (cut derivation->output-path drv <>)
167 outputs)))
168 (self (match (derivation->output-paths drv)
169 (((names . items) ...)
170 items))))
171 (remove (cut member <> self) refs)))
c90cb5c9 172
d38bc9a9
LC
173(define-syntax-rule (with-cache key exp ...)
174 "Cache the value of monadic expression EXP under KEY."
175 (mlet %state-monad ((cache (current-state)))
482fda27 176 (match (vhash-assoc key cache)
d38bc9a9
LC
177 ((_ . result) ;cache hit
178 (return result))
179 (#f ;cache miss
0aeed5e3
LC
180 (mlet %state-monad ((result (begin exp ...))
181 (cache (current-state)))
90ad5c88 182 (mbegin %state-monad
482fda27 183 (set-current-state (vhash-cons key result cache))
90ad5c88 184 (return result)))))))
d38bc9a9 185
aad086d8
LC
186(define (reference-origin drv item)
187 "Return the derivation/output pair among the inputs of DRV, recursively,
188that produces ITEM. Return #f if ITEM is not produced by a derivation (i.e.,
189it's a content-addressed \"source\"), or if it's not produced by a dependency
190of DRV."
191 ;; Perform a breadth-first traversal of the dependency graph of DRV in
192 ;; search of the derivation that produces ITEM.
193 (let loop ((drv (list drv))
194 (visited (setq)))
195 (match drv
196 (()
197 #f)
198 ((drv . rest)
199 (if (set-contains? visited drv)
200 (loop rest visited)
201 (let ((inputs (derivation-inputs drv)))
202 (or (any (lambda (input)
203 (let ((drv (derivation-input-derivation input)))
204 (any (match-lambda
205 ((output . file)
206 (and (string=? file item)
207 (cons drv output))))
208 (derivation->output-paths drv))))
209 inputs)
210 (loop (append rest (map derivation-input-derivation inputs))
211 (set-insert drv visited)))))))))
212
c22a1324
LC
213(define* (cumulative-grafts store drv grafts
214 #:key
215 (outputs (derivation-output-names drv))
216 (guile (%guile-for-build))
217 (system (%current-system)))
218 "Augment GRAFTS with additional grafts resulting from the application of
4b75a706 219GRAFTS to the dependencies of DRV. Return the resulting list of grafts.
d4da602e
LC
220
221This is a monadic procedure in %STATE-MONAD where the state is a vhash mapping
222derivations to the corresponding set of grafts."
b013c33f
LC
223 (define (graft-origin? drv graft)
224 ;; Return true if DRV corresponds to the origin of GRAFT.
225 (match graft
226 (($ <graft> (? derivation? origin) output)
227 (match (assoc-ref (derivation->output-paths drv) output)
228 ((? string? result)
229 (string=? result
230 (derivation->output-path origin output)))
231 (_
232 #f)))
233 (_
234 #f)))
235
c22a1324 236 (define (dependency-grafts item)
aad086d8
LC
237 (match (reference-origin drv item)
238 ((drv . output)
239 ;; If GRAFTS already contains a graft from DRV, do not override it.
240 (if (find (cut graft-origin? drv <>) grafts)
241 (state-return grafts)
4b75a706 242 (cumulative-grafts store drv grafts
aad086d8
LC
243 #:outputs (list output)
244 #:guile guile
245 #:system system)))
246 (#f
247 (state-return grafts))))
d4da602e 248
482fda27 249 (with-cache (cons (derivation-file-name drv) outputs)
4b75a706 250 (match (non-self-references store drv outputs)
d38bc9a9 251 (() ;no dependencies
d4da602e 252 (return grafts))
d38bc9a9
LC
253 (deps ;one or more dependencies
254 (mlet %state-monad ((grafts (mapm %state-monad dependency-grafts deps)))
255 (let ((grafts (delete-duplicates (concatenate grafts) equal?)))
256 (match (filter (lambda (graft)
257 (member (graft-origin-file-name graft) deps))
258 grafts)
259 (()
260 (return grafts))
261 ((applicable ..1)
262 ;; Use APPLICABLE, the subset of GRAFTS that is really
263 ;; applicable to DRV, to avoid creating several identical
264 ;; grafted variants of DRV.
265 (let* ((new (graft-derivation/shallow store drv applicable
482fda27 266 #:outputs outputs
d38bc9a9
LC
267 #:guile guile
268 #:system system))
d38bc9a9
LC
269 (grafts (append (map (lambda (output)
270 (graft
271 (origin drv)
272 (origin-output output)
273 (replacement new)
274 (replacement-output output)))
482fda27 275 outputs)
d38bc9a9
LC
276 grafts)))
277 (return grafts))))))))))
c22a1324
LC
278
279(define* (graft-derivation store drv grafts
482fda27
LC
280 #:key
281 (guile (%guile-for-build))
282 (outputs (derivation-output-names drv))
c22a1324 283 (system (%current-system)))
482fda27
LC
284 "Apply GRAFTS to the OUTPUTS of DRV and all their dependencies, recursively.
285That is, if GRAFTS apply only indirectly to DRV, graft the dependencies of
286DRV, and graft DRV itself to refer to those grafted dependencies."
d4da602e 287 (match (run-with-state
4b75a706 288 (cumulative-grafts store drv grafts
482fda27 289 #:outputs outputs
d4da602e
LC
290 #:guile guile #:system system)
291 vlist-null) ;the initial cache
c22a1324
LC
292 ((first . rest)
293 ;; If FIRST is not a graft for DRV, it means that GRAFTS are not
294 ;; applicable to DRV and nothing needs to be done.
295 (if (equal? drv (graft-origin first))
296 (graft-replacement first)
297 drv))))
7adf9b84
LC
298
299\f
300;; The following might feel more at home in (guix packages) but since (guix
301;; gexp), which is a lower level, needs them, we put them here.
302
303(define %graft?
304 ;; Whether to honor package grafts by default.
305 (make-parameter #t))
306
307(define (set-grafting enable?)
308 "This monadic procedure enables grafting when ENABLE? is true, and disables
309it otherwise. It returns the previous setting."
310 (lambda (store)
311 (values (%graft? enable?) store)))
312
c6080c32
LC
313(define (grafting?)
314 "Return a Boolean indicating whether grafting is enabled."
315 (lambda (store)
316 (values (%graft?) store)))
317
d38bc9a9
LC
318;; Local Variables:
319;; eval: (put 'with-cache 'scheme-indent-function 1)
320;; End:
321
7adf9b84 322;;; grafts.scm ends here