grafts.scm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2014, 2015, 2016, 2017, 2018 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 (test-grafts)
  19. #:use-module (guix gexp)
  20. #:use-module (guix monads)
  21. #:use-module (guix derivations)
  22. #:use-module (guix store)
  23. #:use-module (guix utils)
  24. #:use-module (guix grafts)
  25. #:use-module (guix tests)
  26. #:use-module ((gnu packages) #:select (search-bootstrap-binary))
  27. #:use-module (gnu packages bootstrap)
  28. #:use-module (srfi srfi-1)
  29. #:use-module (srfi srfi-64)
  30. #:use-module (rnrs bytevectors)
  31. #:use-module (rnrs io ports)
  32. #:use-module (ice-9 vlist))
  33. (define %store
  34. (open-connection-for-tests))
  35. (define (bootstrap-binary name)
  36. (let ((bin (search-bootstrap-binary name (%current-system))))
  37. (and %store
  38. (add-to-store %store name #t "sha256" bin))))
  39. (define %bash
  40. (bootstrap-binary "bash"))
  41. (define %mkdir
  42. (bootstrap-binary "mkdir"))
  43. (define make-derivation-input
  44. (@@ (guix derivations) make-derivation-input))
  45. (test-begin "grafts")
  46. (test-equal "graft-derivation, grafted item is a direct dependency"
  47. '((type . graft) (graft (count . 2)))
  48. (let* ((build `(begin
  49. (mkdir %output)
  50. (chdir %output)
  51. (symlink %output "self")
  52. (call-with-output-file "text"
  53. (lambda (output)
  54. (format output "foo/~a/bar" ,%mkdir)))
  55. (symlink ,%bash "sh")))
  56. (orig (build-expression->derivation %store "grafted" build
  57. #:inputs `(("a" ,%bash)
  58. ("b" ,%mkdir))))
  59. (one (add-text-to-store %store "bash" "fake bash"))
  60. (two (build-expression->derivation %store "mkdir"
  61. '(call-with-output-file %output
  62. (lambda (port)
  63. (display "fake mkdir" port)))))
  64. (grafted (graft-derivation %store orig
  65. (list (graft
  66. (origin %bash)
  67. (replacement one))
  68. (graft
  69. (origin %mkdir)
  70. (replacement two))))))
  71. (and (build-derivations %store (list grafted))
  72. (let ((properties (derivation-properties grafted))
  73. (two (derivation->output-path two))
  74. (grafted (derivation->output-path grafted)))
  75. (and (string=? (format #f "foo/~a/bar" two)
  76. (call-with-input-file (string-append grafted "/text")
  77. get-string-all))
  78. (string=? (readlink (string-append grafted "/sh")) one)
  79. (string=? (readlink (string-append grafted "/self"))
  80. grafted)
  81. properties)))))
  82. (test-assert "graft-derivation, grafted item uses a different name"
  83. (let* ((build `(begin
  84. (mkdir %output)
  85. (chdir %output)
  86. (symlink %output "self")
  87. (symlink ,%bash "sh")))
  88. (orig (build-expression->derivation %store "grafted" build
  89. #:inputs `(("a" ,%bash))))
  90. (repl (add-text-to-store %store "BaSH" "fake bash"))
  91. (grafted (graft-derivation %store orig
  92. (list (graft
  93. (origin %bash)
  94. (replacement repl))))))
  95. (and (build-derivations %store (list grafted))
  96. (let ((grafted (derivation->output-path grafted)))
  97. (and (string=? (readlink (string-append grafted "/sh")) repl)
  98. (string=? (readlink (string-append grafted "/self"))
  99. grafted))))))
  100. ;; Make sure 'derivation-file-name' always gets to see an absolute file name.
  101. (fluid-set! %file-port-name-canonicalization 'absolute)
  102. (test-assert "graft-derivation, grafted item is an indirect dependency"
  103. (let* ((build `(begin
  104. (mkdir %output)
  105. (chdir %output)
  106. (symlink %output "self")
  107. (call-with-output-file "text"
  108. (lambda (output)
  109. (format output "foo/~a/bar" ,%mkdir)))
  110. (symlink ,%bash "sh")))
  111. (dep (build-expression->derivation %store "dep" build
  112. #:inputs `(("a" ,%bash)
  113. ("b" ,%mkdir))))
  114. (orig (build-expression->derivation %store "thing"
  115. '(symlink
  116. (assoc-ref %build-inputs
  117. "dep")
  118. %output)
  119. #:inputs `(("dep" ,dep))))
  120. (one (add-text-to-store %store "bash" "fake bash"))
  121. (two (build-expression->derivation %store "mkdir"
  122. '(call-with-output-file %output
  123. (lambda (port)
  124. (display "fake mkdir" port)))))
  125. (grafted (graft-derivation %store orig
  126. (list (graft
  127. (origin %bash)
  128. (replacement one))
  129. (graft
  130. (origin %mkdir)
  131. (replacement two))))))
  132. (and (build-derivations %store (list grafted))
  133. (let* ((two (derivation->output-path two))
  134. (grafted (derivation->output-path grafted))
  135. (dep (readlink grafted)))
  136. (and (string=? (format #f "foo/~a/bar" two)
  137. (call-with-input-file (string-append dep "/text")
  138. get-string-all))
  139. (string=? (readlink (string-append dep "/sh")) one)
  140. (string=? (readlink (string-append dep "/self")) dep)
  141. (equal? (references %store grafted) (list dep))
  142. (lset= string=?
  143. (list one two dep)
  144. (references %store dep)))))))
  145. (test-assert "graft-derivation, preserve empty directories"
  146. (run-with-store %store
  147. (mlet* %store-monad ((fake (text-file "bash" "Fake bash."))
  148. (graft -> (graft
  149. (origin %bash)
  150. (replacement fake)))
  151. (drv (gexp->derivation
  152. "to-graft"
  153. (with-imported-modules '((guix build utils))
  154. #~(begin
  155. (use-modules (guix build utils))
  156. (mkdir-p (string-append #$output
  157. "/a/b/c/d"))
  158. (symlink #$%bash
  159. (string-append #$output
  160. "/bash"))))))
  161. (grafted ((store-lift graft-derivation) drv
  162. (list graft)))
  163. (_ (built-derivations (list grafted)))
  164. (out -> (derivation->output-path grafted)))
  165. (return (and (string=? (readlink (string-append out "/bash"))
  166. fake)
  167. (file-is-directory? (string-append out "/a/b/c/d")))))))
  168. (test-assert "graft-derivation, no dependencies on grafted output"
  169. (run-with-store %store
  170. (mlet* %store-monad ((fake (text-file "bash" "Fake bash."))
  171. (graft -> (graft
  172. (origin %bash)
  173. (replacement fake)))
  174. (drv (gexp->derivation "foo" #~(mkdir #$output)))
  175. (grafted ((store-lift graft-derivation) drv
  176. (list graft))))
  177. (return (eq? grafted drv)))))
  178. (test-assert "graft-derivation, multiple outputs"
  179. (let* ((build `(begin
  180. (symlink (assoc-ref %build-inputs "a")
  181. (assoc-ref %outputs "one"))
  182. (symlink (assoc-ref %outputs "one")
  183. (assoc-ref %outputs "two"))))
  184. (orig (build-expression->derivation %store "grafted" build
  185. #:inputs `(("a" ,%bash))
  186. #:outputs '("one" "two")))
  187. (repl (add-text-to-store %store "bash" "fake bash"))
  188. (grafted (graft-derivation %store orig
  189. (list (graft
  190. (origin %bash)
  191. (replacement repl))))))
  192. (and (build-derivations %store (list grafted))
  193. (let ((one (derivation->output-path grafted "one"))
  194. (two (derivation->output-path grafted "two")))
  195. (and (string=? (readlink one) repl)
  196. (string=? (readlink two) one))))))
  197. (test-assert "graft-derivation, replaced derivation has multiple outputs"
  198. ;; Here we have a replacement just for output "one" of P1 and not for the
  199. ;; other output. Make sure the graft for P1:one correctly applies to the
  200. ;; dependents of P1. See <http://bugs.gnu.org/24712>.
  201. (let* ((p1 (build-expression->derivation
  202. %store "p1"
  203. `(let ((one (assoc-ref %outputs "one"))
  204. (two (assoc-ref %outputs "two")))
  205. (mkdir one)
  206. (mkdir two))
  207. #:outputs '("one" "two")))
  208. (p1r (build-expression->derivation
  209. %store "P1"
  210. `(let ((other (assoc-ref %outputs "ONE")))
  211. (mkdir other)
  212. (call-with-output-file (string-append other "/replacement")
  213. (const #t)))
  214. #:outputs '("ONE")))
  215. (p2 (build-expression->derivation
  216. %store "p2"
  217. `(let ((out (assoc-ref %outputs "aaa")))
  218. (mkdir (assoc-ref %outputs "zzz"))
  219. (mkdir out) (chdir out)
  220. (symlink (assoc-ref %build-inputs "p1:one") "one")
  221. (symlink (assoc-ref %build-inputs "p1:two") "two"))
  222. #:outputs '("aaa" "zzz")
  223. #:inputs `(("p1:one" ,p1 "one")
  224. ("p1:two" ,p1 "two"))))
  225. (p3 (build-expression->derivation
  226. %store "p3"
  227. `(symlink (assoc-ref %build-inputs "p2:aaa")
  228. (assoc-ref %outputs "out"))
  229. #:inputs `(("p2:aaa" ,p2 "aaa")
  230. ("p2:zzz" ,p2 "zzz"))))
  231. (p1g (graft
  232. (origin p1)
  233. (origin-output "one")
  234. (replacement p1r)
  235. (replacement-output "ONE")))
  236. (p3d (graft-derivation %store p3 (list p1g))))
  237. (and (not (find (lambda (input)
  238. ;; INPUT should not be P2:zzz since the result of P3
  239. ;; does not depend on it. See
  240. ;; <http://bugs.gnu.org/24886>.
  241. (and (string=? (derivation-input-path input)
  242. (derivation-file-name p2))
  243. (member "zzz"
  244. (derivation-input-sub-derivations input))))
  245. (derivation-inputs p3d)))
  246. (build-derivations %store (list p3d))
  247. (let ((out (derivation->output-path (pk 'p2d p3d))))
  248. (and (not (string=? (readlink out)
  249. (derivation->output-path p2 "aaa")))
  250. (string=? (derivation->output-path p1 "two")
  251. (readlink (string-append out "/two")))
  252. (file-exists? (string-append out "/one/replacement")))))))
  253. (test-assert "graft-derivation with #:outputs"
  254. ;; Call 'graft-derivation' with a narrowed set of outputs passed as
  255. ;; #:outputs.
  256. (let* ((p1 (build-expression->derivation
  257. %store "p1"
  258. `(let ((one (assoc-ref %outputs "one"))
  259. (two (assoc-ref %outputs "two")))
  260. (mkdir one)
  261. (mkdir two))
  262. #:outputs '("one" "two")))
  263. (p1r (build-expression->derivation
  264. %store "P1"
  265. `(let ((other (assoc-ref %outputs "ONE")))
  266. (mkdir other)
  267. (call-with-output-file (string-append other "/replacement")
  268. (const #t)))
  269. #:outputs '("ONE")))
  270. (p2 (build-expression->derivation
  271. %store "p2"
  272. `(let ((aaa (assoc-ref %outputs "aaa"))
  273. (zzz (assoc-ref %outputs "zzz")))
  274. (mkdir zzz) (chdir zzz)
  275. (mkdir aaa) (chdir aaa)
  276. (symlink (assoc-ref %build-inputs "p1:two") "two"))
  277. #:outputs '("aaa" "zzz")
  278. #:inputs `(("p1:one" ,p1 "one")
  279. ("p1:two" ,p1 "two"))))
  280. (p1g (graft
  281. (origin p1)
  282. (origin-output "one")
  283. (replacement p1r)
  284. (replacement-output "ONE")))
  285. (p2g (graft-derivation %store p2 (list p1g)
  286. #:outputs '("aaa"))))
  287. ;; P2:aaa depends on P1:two, but not on P1:one, so nothing to graft.
  288. (eq? p2g p2)))
  289. (test-equal "graft-derivation, unused outputs not depended on"
  290. '("aaa")
  291. ;; Make sure that the result of 'graft-derivation' does not pull outputs
  292. ;; that are irrelevant to the grafting process. See
  293. ;; <http://bugs.gnu.org/24886>.
  294. (let* ((p1 (build-expression->derivation
  295. %store "p1"
  296. `(let ((one (assoc-ref %outputs "one"))
  297. (two (assoc-ref %outputs "two")))
  298. (mkdir one)
  299. (mkdir two))
  300. #:outputs '("one" "two")))
  301. (p1r (build-expression->derivation
  302. %store "P1"
  303. `(let ((other (assoc-ref %outputs "ONE")))
  304. (mkdir other)
  305. (call-with-output-file (string-append other "/replacement")
  306. (const #t)))
  307. #:outputs '("ONE")))
  308. (p2 (build-expression->derivation
  309. %store "p2"
  310. `(let ((aaa (assoc-ref %outputs "aaa"))
  311. (zzz (assoc-ref %outputs "zzz")))
  312. (mkdir zzz) (chdir zzz)
  313. (symlink (assoc-ref %build-inputs "p1:two") "two")
  314. (mkdir aaa) (chdir aaa)
  315. (symlink (assoc-ref %build-inputs "p1:one") "one"))
  316. #:outputs '("aaa" "zzz")
  317. #:inputs `(("p1:one" ,p1 "one")
  318. ("p1:two" ,p1 "two"))))
  319. (p1g (graft
  320. (origin p1)
  321. (origin-output "one")
  322. (replacement p1r)
  323. (replacement-output "ONE")))
  324. (p2g (graft-derivation %store p2 (list p1g)
  325. #:outputs '("aaa"))))
  326. ;; Here P2G should only depend on P1:one and P1R:one; it must not depend
  327. ;; on P1:two or P1R:two since these are unused in the grafting process.
  328. (and (not (eq? p2g p2))
  329. (let* ((inputs (derivation-inputs p2g))
  330. (match-input (lambda (drv)
  331. (lambda (input)
  332. (string=? (derivation-input-path input)
  333. (derivation-file-name drv)))))
  334. (p1-inputs (filter (match-input p1) inputs))
  335. (p1r-inputs (filter (match-input p1r) inputs))
  336. (p2-inputs (filter (match-input p2) inputs)))
  337. (and (equal? p1-inputs
  338. (list (make-derivation-input (derivation-file-name p1)
  339. '("one"))))
  340. (equal? p1r-inputs
  341. (list
  342. (make-derivation-input (derivation-file-name p1r)
  343. '("ONE"))))
  344. (equal? p2-inputs
  345. (list
  346. (make-derivation-input (derivation-file-name p2)
  347. '("aaa"))))
  348. (derivation-output-names p2g))))))
  349. (test-assert "graft-derivation, renaming" ;<http://bugs.gnu.org/23132>
  350. (let* ((build `(begin
  351. (use-modules (guix build utils))
  352. (mkdir-p (string-append (assoc-ref %outputs "out") "/"
  353. (assoc-ref %build-inputs "in")))))
  354. (orig (build-expression->derivation %store "thing-to-graft" build
  355. #:modules '((guix build utils))
  356. #:inputs `(("in" ,%bash))))
  357. (repl (add-text-to-store %store "bash" "fake bash"))
  358. (grafted (graft-derivation %store orig
  359. (list (graft
  360. (origin %bash)
  361. (replacement repl))))))
  362. (and (build-derivations %store (list grafted))
  363. (let ((out (derivation->output-path grafted)))
  364. (file-is-directory? (string-append out "/" repl))))))
  365. (test-assert "graft-derivation, grafts are not shadowed"
  366. ;; We build a DAG as below, where dotted arrows represent replacements and
  367. ;; solid arrows represent dependencies:
  368. ;;
  369. ;; P1 ·············> P1R
  370. ;; |\__________________.
  371. ;; v v
  372. ;; P2 ·············> P2R
  373. ;; |
  374. ;; v
  375. ;; P3
  376. ;;
  377. ;; We want to make sure that the two grafts we want to apply to P3 are
  378. ;; honored and not shadowed by other computed grafts.
  379. (let* ((p1 (build-expression->derivation
  380. %store "p1"
  381. '(mkdir (assoc-ref %outputs "out"))))
  382. (p1r (build-expression->derivation
  383. %store "P1"
  384. '(let ((out (assoc-ref %outputs "out")))
  385. (mkdir out)
  386. (call-with-output-file (string-append out "/replacement")
  387. (const #t)))))
  388. (p2 (build-expression->derivation
  389. %store "p2"
  390. `(let ((out (assoc-ref %outputs "out")))
  391. (mkdir out)
  392. (chdir out)
  393. (symlink (assoc-ref %build-inputs "p1") "p1"))
  394. #:inputs `(("p1" ,p1))))
  395. (p2r (build-expression->derivation
  396. %store "P2"
  397. `(let ((out (assoc-ref %outputs "out")))
  398. (mkdir out)
  399. (chdir out)
  400. (symlink (assoc-ref %build-inputs "p1") "p1")
  401. (call-with-output-file (string-append out "/replacement")
  402. (const #t)))
  403. #:inputs `(("p1" ,p1))))
  404. (p3 (build-expression->derivation
  405. %store "p3"
  406. `(let ((out (assoc-ref %outputs "out")))
  407. (mkdir out)
  408. (chdir out)
  409. (symlink (assoc-ref %build-inputs "p2") "p2"))
  410. #:inputs `(("p2" ,p2))))
  411. (p1g (graft
  412. (origin p1)
  413. (replacement p1r)))
  414. (p2g (graft
  415. (origin p2)
  416. (replacement (graft-derivation %store p2r (list p1g)))))
  417. (p3d (graft-derivation %store p3 (list p1g p2g))))
  418. (and (build-derivations %store (list p3d))
  419. (let ((out (derivation->output-path (pk p3d))))
  420. ;; Make sure OUT refers to the replacement of P2, which in turn
  421. ;; refers to the replacement of P1, as specified by P1G and P2G.
  422. ;; It used to be the case that P2G would be shadowed by a simple
  423. ;; P2->P2R graft, which is not what we want.
  424. (and (file-exists? (string-append out "/p2/replacement"))
  425. (file-exists? (string-append out "/p2/p1/replacement")))))))
  426. (define buffer-size
  427. ;; Must be equal to REQUEST-SIZE in 'replace-store-references'.
  428. (expt 2 20))
  429. (test-equal "replace-store-references, <http://bugs.gnu.org/28212>"
  430. (string-append (make-string (- buffer-size 47) #\a)
  431. "/gnu/store/" (make-string 32 #\8)
  432. "-SoMeTHiNG"
  433. (list->string (map integer->char (iota 77 33))))
  434. ;; Create input data where the right-hand-size of the dash ("-something"
  435. ;; here) goes beyond the end of the internal buffer of
  436. ;; 'replace-store-references'.
  437. (let* ((content (string-append (make-string (- buffer-size 47) #\a)
  438. "/gnu/store/" (make-string 32 #\7)
  439. "-something"
  440. (list->string
  441. (map integer->char (iota 77 33)))))
  442. (replacement (alist->vhash
  443. `((,(make-string 32 #\7)
  444. . ,(string->utf8 (string-append
  445. (make-string 32 #\8)
  446. "-SoMeTHiNG")))))))
  447. (call-with-output-string
  448. (lambda (output)
  449. ((@@ (guix build graft) replace-store-references)
  450. (open-input-string content) output
  451. replacement
  452. "/gnu/store")))))
  453. (test-end)