build-utils.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2012, 2015, 2016, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
  4. ;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
  5. ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
  6. ;;;
  7. ;;; This file is part of GNU Guix.
  8. ;;;
  9. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  10. ;;; under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 3 of the License, or (at
  12. ;;; your option) any later version.
  13. ;;;
  14. ;;; GNU Guix is distributed in the hope that it will be useful, but
  15. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  21. (define-module (test build-utils)
  22. #:use-module (guix tests)
  23. #:use-module (guix build utils)
  24. #:use-module ((guix utils)
  25. #:select (%current-system call-with-temporary-directory))
  26. #:use-module (gnu packages)
  27. #:use-module (gnu packages bootstrap)
  28. #:use-module (srfi srfi-34)
  29. #:use-module (srfi srfi-35)
  30. #:use-module (srfi srfi-64)
  31. #:use-module (rnrs io ports)
  32. #:use-module (ice-9 popen))
  33. (test-begin "build-utils")
  34. (test-equal "alist-cons-before"
  35. '((a . 1) (x . 42) (b . 2) (c . 3))
  36. (alist-cons-before 'b 'x 42 '((a . 1) (b . 2) (c . 3))))
  37. (test-equal "alist-cons-before, reference not found"
  38. '((a . 1) (b . 2) (c . 3) (x . 42))
  39. (alist-cons-before 'z 'x 42 '((a . 1) (b . 2) (c . 3))))
  40. (test-equal "alist-cons-after"
  41. '((a . 1) (b . 2) (x . 42) (c . 3))
  42. (alist-cons-after 'b 'x 42 '((a . 1) (b . 2) (c . 3))))
  43. (test-equal "alist-cons-after, reference not found"
  44. '((a . 1) (b . 2) (c . 3) (x . 42))
  45. (alist-cons-after 'z 'x 42 '((a . 1) (b . 2) (c . 3))))
  46. (test-equal "alist-replace"
  47. '((a . 1) (b . 77) (c . 3))
  48. (alist-replace 'b 77 '((a . 1) (b . 2) (c . 3))))
  49. (test-assert "alist-replace, key not found"
  50. (not (false-if-exception
  51. (alist-replace 'z 77 '((a . 1) (b . 2) (c . 3))))))
  52. (test-equal "fold-port-matches"
  53. (make-list 3 "Guix")
  54. (call-with-input-string "Guix is cool, Guix rocks, and it uses Guile, Guix!"
  55. (lambda (port)
  56. (fold-port-matches cons '() "Guix" port))))
  57. (test-equal "fold-port-matches, trickier"
  58. (reverse '("Guix" "guix" "Guix" "guiX" "Guix"))
  59. (call-with-input-string "Guix, guix, GuiGuixguiX, Guix"
  60. (lambda (port)
  61. (fold-port-matches cons '()
  62. (list (char-set #\G #\g)
  63. (char-set #\u)
  64. (char-set #\i)
  65. (char-set #\x #\X))
  66. port))))
  67. (test-equal "fold-port-matches, with unmatched chars"
  68. '("Guix" #\, #\space
  69. "guix" #\, #\space
  70. #\G #\u #\i "Guix" "guiX" #\, #\space
  71. "Guix")
  72. (call-with-input-string "Guix, guix, GuiGuixguiX, Guix"
  73. (lambda (port)
  74. (reverse
  75. (fold-port-matches cons '()
  76. (list (char-set #\G #\g)
  77. (char-set #\u)
  78. (char-set #\i)
  79. (char-set #\x #\X))
  80. port
  81. cons)))))
  82. (test-equal "wrap-program, one input, multiple calls"
  83. "hello world\n"
  84. (call-with-temporary-directory
  85. (lambda (directory)
  86. (let ((bash (search-bootstrap-binary "bash" (%current-system)))
  87. (foo (string-append directory "/foo")))
  88. (call-with-output-file foo
  89. (lambda (p)
  90. (format p
  91. "#!~a~%echo \"${GUIX_FOO} ${GUIX_BAR}\"~%"
  92. bash)))
  93. (chmod foo #o777)
  94. ;; wrap-program uses `which' to find bash for the wrapper shebang, but
  95. ;; it can't know about the bootstrap bash in the store, since it's not
  96. ;; named "bash". Help it out a bit by providing a symlink it this
  97. ;; package's output.
  98. (with-environment-variable "PATH" (dirname bash)
  99. (wrap-program foo `("GUIX_FOO" prefix ("hello")))
  100. (wrap-program foo `("GUIX_BAR" prefix ("world")))
  101. ;; The bootstrap Bash is linked against an old libc and would abort
  102. ;; with an assertion failure when trying to load incompatible locale
  103. ;; data.
  104. (unsetenv "LOCPATH")
  105. (let* ((pipe (open-input-pipe foo))
  106. (str (get-string-all pipe)))
  107. (with-directory-excursion directory
  108. (for-each delete-file '("foo" ".foo-real")))
  109. (and (zero? (close-pipe pipe))
  110. str)))))))
  111. (test-assert "invoke/quiet, success"
  112. (begin
  113. (invoke/quiet "true")
  114. #t))
  115. (test-assert "invoke/quiet, failure"
  116. (guard (c ((message-condition? c)
  117. (string-contains (condition-message c) "This is an error.")))
  118. (invoke/quiet "sh" "-c" "echo This is an error. ; false")
  119. #f))
  120. (test-assert "invoke/quiet, failure, message on stderr"
  121. (guard (c ((message-condition? c)
  122. (string-contains (condition-message c)
  123. "This is another error.")))
  124. (invoke/quiet "sh" "-c" "echo This is another error. >&2 ; false")
  125. #f))
  126. (let ((script-contents "\
  127. #!/anything/cabbage-bash-1.2.3/bin/sh
  128. echo hello world"))
  129. (test-equal "wrap-script, simple case"
  130. (string-append
  131. (format #f "\
  132. #!~a --no-auto-compile
  133. #!#; Guix wrapper
  134. #\\-~s
  135. #\\-~s
  136. "
  137. (which "guile")
  138. '(begin (let ((current (getenv "GUIX_FOO")))
  139. (setenv "GUIX_FOO"
  140. (if current
  141. (string-append "/some/path:/some/other/path"
  142. ":" current)
  143. "/some/path:/some/other/path"))))
  144. '(let ((cl (command-line)))
  145. (apply execl "/anything/cabbage-bash-1.2.3/bin/sh"
  146. (car cl)
  147. (cons (car cl)
  148. (append '("") cl)))))
  149. script-contents)
  150. (call-with-temporary-directory
  151. (lambda (directory)
  152. (let ((script-file-name (string-append directory "/foo")))
  153. (call-with-output-file script-file-name
  154. (lambda (port)
  155. (display script-contents port)))
  156. (chmod script-file-name #o777)
  157. (wrap-script script-file-name
  158. `("GUIX_FOO" prefix ("/some/path"
  159. "/some/other/path")))
  160. (let ((str (call-with-input-file script-file-name get-string-all)))
  161. (with-directory-excursion directory
  162. (delete-file "foo"))
  163. str))))))
  164. (let ((script-contents "\
  165. #!/anything/cabbage-bash-1.2.3/bin/python3 -and -args
  166. # vim:fileencoding=utf-8
  167. print('hello world')"))
  168. (test-equal "wrap-script, with encoding declaration"
  169. (string-append
  170. (format #f "\
  171. #!MYGUILE --no-auto-compile
  172. #!#; # vim:fileencoding=utf-8
  173. #\\-~s
  174. #\\-~s
  175. "
  176. '(begin (let ((current (getenv "GUIX_FOO")))
  177. (setenv "GUIX_FOO"
  178. (if current
  179. (string-append "/some/path:/some/other/path"
  180. ":" current)
  181. "/some/path:/some/other/path"))))
  182. `(let ((cl (command-line)))
  183. (apply execl "/anything/cabbage-bash-1.2.3/bin/python3"
  184. (car cl)
  185. (cons (car cl)
  186. (append '("" "-and" "-args") cl)))))
  187. script-contents)
  188. (call-with-temporary-directory
  189. (lambda (directory)
  190. (let ((script-file-name (string-append directory "/foo")))
  191. (call-with-output-file script-file-name
  192. (lambda (port)
  193. (format port script-contents)))
  194. (chmod script-file-name #o777)
  195. (wrap-script script-file-name
  196. #:guile "MYGUILE"
  197. `("GUIX_FOO" prefix ("/some/path"
  198. "/some/other/path")))
  199. (let ((str (call-with-input-file script-file-name get-string-all)))
  200. (with-directory-excursion directory
  201. (delete-file "foo"))
  202. str))))))
  203. (test-assert "wrap-script, raises condition"
  204. (call-with-temporary-directory
  205. (lambda (directory)
  206. (let ((script-file-name (string-append directory "/foo")))
  207. (call-with-output-file script-file-name
  208. (lambda (port)
  209. (format port "This is not a script")))
  210. (chmod script-file-name #o777)
  211. (guard (c ((wrap-error? c) #t))
  212. (wrap-script script-file-name
  213. #:guile "MYGUILE"
  214. `("GUIX_FOO" prefix ("/some/path"
  215. "/some/other/path")))
  216. #f)))))
  217. (test-equal "substitute*, text contains a NUL byte, UTF-8"
  218. "c\0d"
  219. (with-fluids ((%default-port-encoding "UTF-8")
  220. (%default-port-conversion-strategy 'error))
  221. ;; The GNU libc is locale sensitive. Depending on the value of LANG, the
  222. ;; test could fail with "string contains #\\nul character: ~S" or "cannot
  223. ;; convert wide string to output locale".
  224. (setlocale LC_ALL "en_US.UTF-8")
  225. (call-with-temporary-output-file
  226. (lambda (file port)
  227. (format port "a\0b")
  228. (flush-output-port port)
  229. (substitute* file
  230. (("a") "c")
  231. (("b") "d"))
  232. (with-input-from-file file
  233. (lambda _
  234. (get-string-all (current-input-port))))))))
  235. (test-equal "search-input-file: exception if not found"
  236. `((path)
  237. (file . "does-not-exist"))
  238. (guard (e ((search-error? e)
  239. `((path . ,(search-error-path e))
  240. (file . ,(search-error-file e)))))
  241. (search-input-file '() "does-not-exist")))
  242. (test-equal "search-input-file: can find if existent"
  243. (which "guile")
  244. (search-input-file
  245. `(("guile/bin" . ,(dirname (which "guile"))))
  246. "guile"))
  247. (test-equal "search-input-file: can search in multiple directories"
  248. (which "guile")
  249. (call-with-temporary-directory
  250. (lambda (directory)
  251. (search-input-file
  252. `(("irrelevant" . ,directory)
  253. ("guile/bin" . ,(dirname (which "guile"))))
  254. "guile"))))
  255. (test-end)