grafts.scm 22 KB

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