search-paths.scm 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2013, 2014, 2015, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2022 Maxime Devos <maximedevos@telenet.be>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (guix search-paths)
  20. #:use-module (guix records)
  21. #:use-module (guix build utils)
  22. #:use-module (srfi srfi-1)
  23. #:use-module (srfi srfi-26)
  24. #:use-module (ice-9 match)
  25. #:export (<search-path-specification>
  26. search-path-specification
  27. search-path-specification?
  28. search-path-specification-variable
  29. search-path-specification-files
  30. search-path-specification-separator
  31. search-path-specification-file-type
  32. search-path-specification-file-pattern
  33. $PATH
  34. $GUIX_EXTENSIONS_PATH
  35. $SSL_CERT_DIR
  36. $SSL_CERT_FILE
  37. search-path-specification->sexp
  38. sexp->search-path-specification
  39. string-tokenize*
  40. evaluate-search-paths
  41. environment-variable-definition
  42. search-path-definition
  43. set-search-paths))
  44. ;;; Commentary:
  45. ;;;
  46. ;;; This module defines "search path specifications", which allow packages to
  47. ;;; declare environment variables that they use to define search paths. For
  48. ;;; instance, GCC has the 'CPATH' variable, Guile has the 'GUILE_LOAD_PATH'
  49. ;;; variable, etc.
  50. ;;;
  51. ;;; Code:
  52. ;; The specification of a search path.
  53. (define-record-type* <search-path-specification>
  54. search-path-specification make-search-path-specification
  55. search-path-specification?
  56. (variable search-path-specification-variable) ;string
  57. (files search-path-specification-files) ;list of strings
  58. (separator search-path-specification-separator ;string | #f
  59. (default ":"))
  60. (file-type search-path-specification-file-type ;symbol
  61. (default 'directory))
  62. (file-pattern search-path-specification-file-pattern ;#f | string
  63. (default #f)))
  64. (define $PATH
  65. ;; The 'PATH' variable. This variable is a bit special: it is not attached
  66. ;; to any package in particular.
  67. (search-path-specification
  68. (variable "PATH")
  69. (files '("bin" "sbin"))))
  70. (define $GUIX_EXTENSIONS_PATH
  71. ;; 'GUIX_EXTENSIONS_PATH' is used by Guix to locate extension commands.
  72. ;; Unlike 'PATH', it is attached to a package, Guix; however, it is
  73. ;; convenient to define it by default because the 'guix' package is not
  74. ;; supposed to be installed in a profile.
  75. (search-path-specification
  76. (variable "GUIX_EXTENSIONS_PATH")
  77. (files '("share/guix/extensions"))))
  78. ;; Two variables for certificates (info "(guix)X.509 Certificates"),
  79. ;; respected by OpenSSL and possibly GnuTLS in the future
  80. ;; (https://gitlab.com/gnutls/gnutls/-/merge_requests/1541)
  81. ;; and many of their dependents -- even some GnuTLS dependents
  82. ;; like Guile. As they are not tied to a single package, define
  83. ;; them here to avoid duplication.
  84. ;;
  85. ;; Additionally, the 'native-search-paths' field is not thunked,
  86. ;; so doing (package-native-search-paths openssl)
  87. ;; could cause import cycle issues.
  88. (define $SSL_CERT_DIR
  89. (search-path-specification
  90. (variable "SSL_CERT_DIR")
  91. (separator #f) ;single entry
  92. (files '("etc/ssl/certs"))))
  93. (define $SSL_CERT_FILE
  94. (search-path-specification
  95. (variable "SSL_CERT_FILE")
  96. (file-type 'regular)
  97. (separator #f) ;single entry
  98. (files '("etc/ssl/certs/ca-certificates.crt"))))
  99. (define (search-path-specification->sexp spec)
  100. "Return an sexp representing SPEC, a <search-path-specification>. The sexp
  101. corresponds to the arguments expected by `set-path-environment-variable'."
  102. ;; Note that this sexp format is used both by build systems and in
  103. ;; (guix profiles), so think twice before you change it.
  104. (match spec
  105. (($ <search-path-specification> variable files separator type pattern)
  106. `(,variable ,files ,separator ,type ,pattern))))
  107. (define (sexp->search-path-specification sexp)
  108. "Convert SEXP, which is as returned by 'search-path-specification->sexp', to
  109. a <search-path-specification> object."
  110. (match sexp
  111. ((variable files separator type pattern)
  112. (search-path-specification
  113. (variable variable)
  114. (files files)
  115. (separator separator)
  116. (file-type type)
  117. (file-pattern pattern)))))
  118. (define-syntax-rule (with-null-error-port exp)
  119. "Evaluate EXP with the error port pointing to the bit bucket."
  120. (with-error-to-port (%make-void-port "w")
  121. (lambda () exp)))
  122. ;; XXX: This procedure used to be in (guix utils) but since we want to be able
  123. ;; to use (guix search-paths) on the build side, we want to avoid the
  124. ;; dependency on (guix utils), and so this procedure is back here for now.
  125. (define (string-tokenize* string separator)
  126. "Return the list of substrings of STRING separated by SEPARATOR. This is
  127. like `string-tokenize', but SEPARATOR is a string."
  128. (define (index string what)
  129. (let loop ((string string)
  130. (offset 0))
  131. (cond ((string-null? string)
  132. #f)
  133. ((string-prefix? what string)
  134. offset)
  135. (else
  136. (loop (string-drop string 1) (+ 1 offset))))))
  137. (define len
  138. (string-length separator))
  139. (let loop ((string string)
  140. (result '()))
  141. (cond ((index string separator)
  142. =>
  143. (lambda (offset)
  144. (loop (string-drop string (+ offset len))
  145. (cons (substring string 0 offset)
  146. result))))
  147. (else
  148. (reverse (cons string result))))))
  149. (define* (evaluate-search-paths search-paths directories
  150. #:optional (getenv (const #f)))
  151. "Evaluate SEARCH-PATHS, a list of search-path specifications, for
  152. DIRECTORIES, a list of directory names, and return a list of
  153. specification/value pairs. Use GETENV to determine the current settings and
  154. report only settings not already effective."
  155. (define (search-path-definition spec)
  156. (match spec
  157. (($ <search-path-specification> variable files #f type pattern)
  158. ;; Separator is #f so return the first match.
  159. (match (with-null-error-port
  160. (search-path-as-list files directories
  161. #:type type
  162. #:pattern pattern))
  163. (()
  164. #f)
  165. ((head . _)
  166. (let ((value (getenv variable)))
  167. (if (and value (string=? value head))
  168. #f ;VARIABLE already set appropriately
  169. (cons spec head))))))
  170. (($ <search-path-specification> variable files separator
  171. type pattern)
  172. (let* ((values (or (and=> (getenv variable)
  173. (cut string-tokenize* <> separator))
  174. '()))
  175. ;; XXX: Silence 'find-files' when it stumbles upon non-existent
  176. ;; directories (see
  177. ;; <http://lists.gnu.org/archive/html/guix-devel/2015-01/msg00269.html>.)
  178. (path (with-null-error-port
  179. (search-path-as-list files directories
  180. #:type type
  181. #:pattern pattern))))
  182. (if (every (cut member <> values) path)
  183. #f ;VARIABLE is already set appropriately
  184. (cons spec (string-join path separator)))))))
  185. (filter-map search-path-definition search-paths))
  186. (define* (environment-variable-definition variable value
  187. #:key
  188. (kind 'exact)
  189. (separator ":"))
  190. "Return a the definition of VARIABLE to VALUE in Bash syntax.
  191. KIND can be either 'exact (return the definition of VARIABLE=VALUE),
  192. 'prefix (return the definition where VALUE is added as a prefix to VARIABLE's
  193. current value), or 'suffix (return the definition where VALUE is added as a
  194. suffix to VARIABLE's current value.) In the case of 'prefix and 'suffix,
  195. SEPARATOR is used as the separator between VARIABLE's current value and its
  196. prefix/suffix."
  197. (match (if (not separator) 'exact kind)
  198. ('exact
  199. (format #f "export ~a=\"~a\"" variable value))
  200. ('prefix
  201. (format #f "export ~a=\"~a${~a:+~a}$~a\""
  202. variable value variable separator variable))
  203. ('suffix
  204. (format #f "export ~a=\"$~a${~a:+~a}~a\""
  205. variable variable variable separator value))))
  206. (define* (search-path-definition search-path value
  207. #:key (kind 'exact))
  208. "Similar to 'environment-variable-definition', but applied to a
  209. <search-path-specification>."
  210. (match search-path
  211. (($ <search-path-specification> variable _ separator)
  212. (environment-variable-definition variable value
  213. #:kind kind
  214. #:separator separator))))
  215. (define* (set-search-paths search-paths directories
  216. #:key (setenv setenv))
  217. "Set the search path environment variables specified by SEARCH-PATHS for the
  218. given directories."
  219. (for-each (match-lambda
  220. ((spec . value)
  221. (setenv (search-path-specification-variable spec)
  222. value)))
  223. (evaluate-search-paths search-paths directories)))
  224. ;;; search-paths.scm ends here