grafts.scm 15 KB

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