Mirror of GNU Guix
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

342 lines
14 KiB

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