describe.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018, 2019, 2020, 2021 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 (guix describe)
  19. #:use-module (guix memoization)
  20. #:use-module (guix profiles)
  21. #:use-module (guix packages)
  22. #:use-module ((guix utils) #:select (location-file))
  23. #:use-module ((guix store) #:select (%store-prefix store-path?))
  24. #:use-module ((guix config) #:select (%state-directory))
  25. #:autoload (guix channels) (channel-name
  26. sexp->channel
  27. manifest-entry-channel)
  28. #:use-module (srfi srfi-1)
  29. #:use-module (ice-9 match)
  30. #:export (current-profile
  31. current-profile-date
  32. current-profile-entries
  33. current-channels
  34. package-path-entries
  35. package-provenance
  36. package-channels
  37. manifest-entry-with-provenance
  38. manifest-entry-provenance))
  39. ;;; Commentary:
  40. ;;;
  41. ;;; This module provides supporting code to allow a Guix instance to find, at
  42. ;;; run time, which profile it's in (profiles created by 'guix pull'). That
  43. ;;; allows it to read meta-information about itself (e.g., repository URL and
  44. ;;; commit ID) and to find other channels available in the same profile. It's
  45. ;;; a bit like ELPA's pkg-info.el.
  46. ;;;
  47. ;;; Code:
  48. (define initial-program-arguments
  49. ;; Save the initial program arguments. This allows us to see the "real"
  50. ;; 'guix' program, even if 'guix repl -s' calls 'set-program-arguments'
  51. ;; later on.
  52. (program-arguments))
  53. (define current-profile
  54. (mlambda ()
  55. "Return the profile (created by 'guix pull') the calling process lives in,
  56. or #f if this is not applicable."
  57. (match initial-program-arguments
  58. ((program . _)
  59. (and (string-suffix? "/bin/guix" program)
  60. ;; Note: We want to do _lexical dot-dot resolution_. Using ".."
  61. ;; for real would instead take us into the /gnu/store directory
  62. ;; that ~/.config/guix/current/bin points to, whereas we want to
  63. ;; obtain ~/.config/guix/current.
  64. (let ((candidate (dirname (dirname program))))
  65. (and (file-exists? (string-append candidate "/manifest"))
  66. candidate)))))))
  67. (define (current-profile-date)
  68. "Return the creation date of the current profile (produced by 'guix pull'),
  69. as a number of seconds since the Epoch, or #f if it could not be determined."
  70. ;; Normally 'current-profile' will return ~/.config/guix/current. We need
  71. ;; to 'readlink' once to get '/var/guix/…/guix-profile', whose mtime is the
  72. ;; piece of information we're looking for.
  73. (let loop ((profile (current-profile)))
  74. (match profile
  75. (#f #f)
  76. ((? store-path?) #f)
  77. (file
  78. (if (string-prefix? %state-directory file)
  79. (and=> (lstat file) stat:mtime)
  80. (catch 'system-error
  81. (lambda ()
  82. (let ((target (readlink file)))
  83. (loop (if (string-prefix? "/" target)
  84. target
  85. (string-append (dirname file) "/" target)))))
  86. (const #f)))))))
  87. (define (channel-metadata)
  88. "Return the 'guix' channel metadata sexp from (guix config) if available;
  89. otherwise return #f."
  90. ;; Older 'build-self.scm' would create a (guix config) file without the
  91. ;; '%channel-metadata' variable. Thus, properly deal with a lack of
  92. ;; information.
  93. (let ((module (resolve-interface '(guix config))))
  94. (and=> (module-variable module '%channel-metadata) variable-ref)))
  95. (define current-profile-entries
  96. (mlambda ()
  97. "Return the list of entries in the 'guix pull' profile the calling process
  98. lives in, or the empty list if this is not applicable."
  99. (match (current-profile)
  100. (#f '())
  101. (profile
  102. (let ((manifest (profile-manifest profile)))
  103. (manifest-entries manifest))))))
  104. (define current-channel-entries
  105. (mlambda ()
  106. "Return manifest entries corresponding to extra channels--i.e., not the
  107. 'guix' channel."
  108. (remove (lambda (entry)
  109. (or (string=? (manifest-entry-name entry) "guix")
  110. ;; If ENTRY lacks the 'source' property, it's not an entry
  111. ;; from 'guix pull'. See <https://bugs.gnu.org/48778>.
  112. (not (assq 'source (manifest-entry-properties entry)))))
  113. (current-profile-entries))))
  114. (define current-channels
  115. (mlambda ()
  116. "Return the list of channels currently available, including the 'guix'
  117. channel. Return the empty list if this information is missing."
  118. (define (build-time-metadata)
  119. (match (channel-metadata)
  120. (#f '())
  121. (sexp (or (and=> (sexp->channel sexp 'guix) list) '()))))
  122. (match (current-profile-entries)
  123. (()
  124. ;; As a fallback, if we're not running from a profile, use 'guix'
  125. ;; channel metadata from (guix config).
  126. (build-time-metadata))
  127. (entries
  128. (match (filter-map manifest-entry-channel entries)
  129. (()
  130. ;; This profile lacks provenance metadata, so fall back to
  131. ;; build-time metadata as returned by 'channel-metadata'.
  132. (build-time-metadata))
  133. (lst
  134. lst))))))
  135. (define (package-path-entries)
  136. "Return two values: the list of package path entries to be added to the
  137. package search path, and the list to be added to %LOAD-COMPILED-PATH. These
  138. entries are taken from the 'guix pull' profile the calling process lives in,
  139. when applicable."
  140. ;; Filter out Guix itself.
  141. (unzip2 (map (lambda (entry)
  142. (list (string-append (manifest-entry-item entry)
  143. "/share/guile/site/"
  144. (effective-version))
  145. (string-append (manifest-entry-item entry)
  146. "/lib/guile/" (effective-version)
  147. "/site-ccache")))
  148. (current-channel-entries))))
  149. (define (package-channels package)
  150. "Return the list of channels providing PACKAGE or an empty list if it could
  151. not be determined."
  152. (match (and=> (package-location package) location-file)
  153. (#f '())
  154. (file
  155. (let ((file (if (string-prefix? "/" file)
  156. file
  157. (search-path %load-path file))))
  158. (if (and file
  159. (string-prefix? (%store-prefix) file))
  160. (filter-map
  161. (lambda (entry)
  162. (let ((item (manifest-entry-item entry)))
  163. (and (or (string-prefix? item file)
  164. (string=? "guix" (manifest-entry-name entry)))
  165. (manifest-entry-channel entry))))
  166. (current-profile-entries))
  167. '())))))
  168. (define (package-provenance package)
  169. "Return the provenance of PACKAGE as an sexp for use as the 'provenance'
  170. property of manifest entries, or #f if it could not be determined."
  171. (define (entry-source entry)
  172. (match (assq 'source
  173. (manifest-entry-properties entry))
  174. (('source value) value)
  175. (_ #f)))
  176. (let* ((channels (package-channels package))
  177. (names (map (compose symbol->string channel-name) channels)))
  178. ;; Always store information about the 'guix' channel and
  179. ;; optionally about the specific channel FILE comes from.
  180. (or (let ((main (and=> (find (lambda (entry)
  181. (string=? "guix"
  182. (manifest-entry-name entry)))
  183. (current-profile-entries))
  184. entry-source))
  185. (extra (any (lambda (entry)
  186. (let ((item (manifest-entry-item entry))
  187. (name (manifest-entry-name entry)))
  188. (and (member name names)
  189. (not (string=? name "guix"))
  190. (entry-source entry))))
  191. (current-profile-entries))))
  192. (and main
  193. `(,main
  194. ,@(if extra (list extra) '())))))))
  195. (define (manifest-entry-with-provenance entry)
  196. "Return ENTRY with an additional 'provenance' property if it's not already
  197. there."
  198. (let ((properties (manifest-entry-properties entry)))
  199. (if (assq 'provenance properties)
  200. entry
  201. (let ((item (manifest-entry-item entry)))
  202. (manifest-entry
  203. (inherit entry)
  204. (properties
  205. (match (and (package? item) (package-provenance item))
  206. (#f properties)
  207. (sexp `((provenance ,@sexp)
  208. ,@properties)))))))))
  209. (define (manifest-entry-provenance entry)
  210. "Return the list of channels ENTRY comes from. Return the empty list if
  211. that information is missing."
  212. (match (assq-ref (manifest-entry-properties entry) 'provenance)
  213. ((main extras ...)
  214. ;; XXX: Until recently, channel sexps lacked the channel name. For
  215. ;; entries created by 'manifest-entry-with-provenance', the first sexp
  216. ;; is known to be the 'guix channel, and for the other ones, invent a
  217. ;; fallback name (it's OK as the name is just a "pet name").
  218. (match (sexp->channel main 'guix)
  219. (#f '())
  220. (channel
  221. (let loop ((extras extras)
  222. (counter 1)
  223. (channels (list channel)))
  224. (match extras
  225. (()
  226. (reverse channels))
  227. ((head . tail)
  228. (let* ((name (string->symbol
  229. (format #f "channel~a" counter)))
  230. (extra (sexp->channel head name)))
  231. (if extra
  232. (loop tail (+ 1 counter) (cons extra channels))
  233. (loop tail counter channels)))))))))
  234. (_
  235. '())))