asdf-build-system.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2016, 2017 Andy Patterson <ajpatter@uwaterloo.ca>
  3. ;;; Copyright © 2020, 2021, 2022 Guillaume Le Vaillant <glv@posteo.net>
  4. ;;; Copyright © 2022 Pierre Neidhardt <mail@ambrevar.xyz>
  5. ;;;
  6. ;;; This file is part of GNU Guix.
  7. ;;;
  8. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  9. ;;; under the terms of the GNU General Public License as published by
  10. ;;; the Free Software Foundation; either version 3 of the License, or (at
  11. ;;; your option) any later version.
  12. ;;;
  13. ;;; GNU Guix is distributed in the hope that it will be useful, but
  14. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;;; GNU General Public License for more details.
  17. ;;;
  18. ;;; You should have received a copy of the GNU General Public License
  19. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  20. (define-module (guix build asdf-build-system)
  21. #:use-module ((guix build gnu-build-system) #:prefix gnu:)
  22. #:use-module (guix build utils)
  23. #:use-module (guix build union)
  24. #:use-module (guix build lisp-utils)
  25. #:use-module (srfi srfi-1)
  26. #:use-module (srfi srfi-11)
  27. #:use-module (srfi srfi-26)
  28. #:use-module (ice-9 rdelim)
  29. #:use-module (ice-9 receive)
  30. #:use-module (ice-9 regex)
  31. #:use-module (ice-9 match)
  32. #:use-module (ice-9 format)
  33. #:use-module (ice-9 ftw)
  34. #:export (%standard-phases
  35. %standard-phases/source
  36. asdf-build
  37. asdf-build/source))
  38. ;; Commentary:
  39. ;;
  40. ;; System for building ASDF packages; creating executable programs and images
  41. ;; from them.
  42. ;;
  43. ;; Code:
  44. (define %object-prefix "/lib/common-lisp")
  45. (define (%lisp-source-install-prefix)
  46. (string-append %source-install-prefix "/" (%lisp-type)))
  47. (define %system-install-prefix
  48. (string-append %source-install-prefix "/systems"))
  49. (define (main-system-name output)
  50. ;; FIXME: Find a more reliable way to get the main system name.
  51. (let* ((full-name (strip-store-file-name output))
  52. (lisp-prefix (string-append (%lisp-type) "-"))
  53. (package-name (if (string-prefix? lisp-prefix full-name)
  54. (string-drop full-name (string-length lisp-prefix))
  55. full-name)))
  56. (package-name->name+version package-name)))
  57. (define (lisp-source-directory output name)
  58. (string-append output (%lisp-source-install-prefix) "/" name))
  59. (define (source-directory output name)
  60. (string-append output %source-install-prefix "/source/" name))
  61. (define (library-directory output)
  62. (string-append output %object-prefix
  63. "/" (%lisp-type)))
  64. (define (output-translation source-path
  65. object-output)
  66. "Return a translation for the system's source path to its binary output."
  67. `((,source-path
  68. :**/ :*.*.*)
  69. (,(library-directory object-output)
  70. :**/ :*.*.*)))
  71. (define (copy-files-to-output out name)
  72. "Copy all files from the current directory to OUT. Create an extra link to
  73. any system-defining files in the source to a convenient location. This is
  74. done before any compiling so that the compiled source locations will be
  75. valid."
  76. (let ((source (getcwd))
  77. (target (source-directory out name))
  78. (system-path (string-append out %system-install-prefix)))
  79. ;; SBCL keeps the modification time of the source file in the compiled
  80. ;; file, and the source files might just have been patched by a custom
  81. ;; phase. Therefore we reset the modification time of all the source
  82. ;; files before compiling.
  83. (for-each (lambda (file)
  84. (let ((s (lstat file)))
  85. (unless (or (eq? (stat:type s) 'symlink)
  86. (not (access? file W_OK)))
  87. (utime file 0 0 0 0))))
  88. (find-files source #:directories? #t))
  89. (copy-recursively source target #:keep-mtime? #t)
  90. (mkdir-p system-path)
  91. (for-each
  92. (lambda (file)
  93. (symlink file
  94. (string-append system-path "/" (basename file))))
  95. (find-files target "\\.asd$"))
  96. #t))
  97. (define* (install #:key inputs outputs #:allow-other-keys)
  98. "Copy and symlink all the source files.
  99. The source files are taken from the corresponding compile package (e.g. SBCL)
  100. if it's present in the native-inputs."
  101. (define output (assoc-ref outputs "out"))
  102. (define package-name
  103. (package-name->name+version
  104. (strip-store-file-name output)))
  105. (define (no-prefix pkgname)
  106. (let ((index (string-index pkgname #\-)))
  107. (if index
  108. (string-drop pkgname (1+ index))
  109. pkgname)))
  110. (define parent
  111. (match (assoc package-name inputs
  112. (lambda (key alist-car)
  113. (let* ((alt-key (no-prefix key))
  114. (alist-car (no-prefix alist-car)))
  115. (or (string=? alist-car key)
  116. (string=? alist-car alt-key)))))
  117. (#f #f)
  118. (p (cdr p))))
  119. (define parent-name
  120. (and parent
  121. (package-name->name+version (strip-store-file-name parent))))
  122. (define parent-source
  123. (and parent
  124. (string-append parent "/share/common-lisp/"
  125. (let ((index (string-index parent-name #\-)))
  126. (if index
  127. (string-take parent-name index)
  128. parent-name)))))
  129. (define (first-subdirectory directory) ; From gnu-build-system.
  130. "Return the file name of the first sub-directory of DIRECTORY."
  131. (match (scandir directory
  132. (lambda (file)
  133. (and (not (member file '("." "..")))
  134. (file-is-directory? (string-append directory "/"
  135. file)))))
  136. ((first . _) first)))
  137. (define source-directory
  138. (if (and parent-source
  139. (file-exists? parent-source))
  140. (string-append parent-source "/" (first-subdirectory parent-source))
  141. "."))
  142. (with-directory-excursion source-directory
  143. (copy-files-to-output output package-name)))
  144. (define* (copy-source #:key outputs asd-systems #:allow-other-keys)
  145. "Copy the source to the library output."
  146. (let* ((out (library-output outputs))
  147. (install-path (string-append out %source-install-prefix))
  148. (system-name (main-system-name out)))
  149. (copy-files-to-output out system-name)
  150. ;; Hide the files from asdf
  151. (with-directory-excursion install-path
  152. (rename-file "source" (%lisp-type))
  153. (delete-file-recursively "systems")))
  154. #t)
  155. (define* (configure #:key inputs #:allow-other-keys)
  156. ;; Create a directory having the configuration files for
  157. ;; all the dependencies in 'etc/common-lisp/'.
  158. (let ((out (string-append (getcwd) "/.cl-union")))
  159. (match inputs
  160. (((name . directories) ...)
  161. (union-build out (filter directory-exists? directories)
  162. #:create-all-directories? #t
  163. #:log-port (%make-void-port "w"))))
  164. (setenv "CL_UNION" out)
  165. (setenv "XDG_CONFIG_DIRS" (string-append out "/etc")))
  166. #t)
  167. (define* (build #:key outputs inputs asd-systems asd-operation
  168. #:allow-other-keys)
  169. "Compile the system."
  170. (let* ((out (library-output outputs))
  171. (system-name (main-system-name out))
  172. (source-path (string-append out (%lisp-source-install-prefix)))
  173. (translations (wrap-output-translations
  174. `(,(output-translation source-path
  175. out)))))
  176. (setenv "ASDF_OUTPUT_TRANSLATIONS"
  177. (replace-escaped-macros (format #f "~S" translations)))
  178. (setenv "HOME" out) ; ecl's asdf sometimes wants to create $HOME/.cache
  179. (compile-systems asd-systems
  180. (lisp-source-directory out system-name)
  181. asd-operation))
  182. #t)
  183. (define* (check #:key tests? outputs inputs asd-test-systems
  184. #:allow-other-keys)
  185. "Test the system."
  186. (let* ((out (library-output outputs))
  187. (system-name (main-system-name out)))
  188. (if tests?
  189. (test-system asd-test-systems (lisp-source-directory out system-name))
  190. (format #t "test suite not run~%")))
  191. #t)
  192. (define* (create-asdf-configuration #:key inputs outputs #:allow-other-keys)
  193. "Create the ASDF configuration files for the built systems."
  194. (let* ((system-name (main-system-name (assoc-ref outputs "out")))
  195. (out (library-output outputs))
  196. (conf-dir (string-append out "/etc/common-lisp"))
  197. (deps-conf-dir (string-append (getenv "CL_UNION") "/etc/common-lisp"))
  198. (source-dir (lisp-source-directory out system-name))
  199. (lib-dir (string-append (library-directory out) "/" system-name)))
  200. (make-asdf-configuration system-name conf-dir deps-conf-dir
  201. source-dir lib-dir)
  202. #t))
  203. (define* (cleanup-files #:key outputs
  204. #:allow-other-keys)
  205. "Remove any compiled files which are not a part of the final bundle."
  206. (let* ((out (library-output outputs))
  207. (cache-directory (string-append out "/.cache")))
  208. ;; Remove the cache directory in case the lisp implementation wrote
  209. ;; something in there when compiling or testing a system.
  210. (when (directory-exists? cache-directory)
  211. (delete-file-recursively cache-directory)))
  212. #t)
  213. (define* (strip #:rest args)
  214. ;; stripping sbcl binaries removes their entry program and extra systems
  215. (or (string=? (%lisp-type) "sbcl")
  216. (apply (assoc-ref gnu:%standard-phases 'strip) args)))
  217. (define %standard-phases/source
  218. (modify-phases gnu:%standard-phases
  219. (delete 'bootstrap)
  220. (delete 'configure)
  221. (delete 'check)
  222. (delete 'build)
  223. (replace 'install install)))
  224. (define %standard-phases
  225. (modify-phases gnu:%standard-phases
  226. (delete 'bootstrap)
  227. (replace 'configure configure)
  228. (add-before 'configure 'copy-source copy-source)
  229. (replace 'build build)
  230. (replace 'check check)
  231. (add-after 'check 'create-asdf-configuration create-asdf-configuration)
  232. (add-after 'create-asdf-configuration 'cleanup cleanup-files)
  233. (delete 'install)
  234. (replace 'strip strip)))
  235. (define* (asdf-build #:key inputs
  236. (phases %standard-phases)
  237. #:allow-other-keys
  238. #:rest args)
  239. (apply gnu:gnu-build
  240. #:inputs inputs
  241. #:phases phases
  242. args))
  243. (define* (asdf-build/source #:key inputs
  244. (phases %standard-phases/source)
  245. #:allow-other-keys
  246. #:rest args)
  247. (apply gnu:gnu-build
  248. #:inputs inputs
  249. #:phases phases
  250. args))
  251. ;;; asdf-build-system.scm ends here