guix-profiles.el 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. ;;; guix-profiles.el --- Guix profiles
  2. ;; Copyright © 2014–2017 Alex Kost <alezost@gmail.com>
  3. ;; Copyright © 2015 Mathieu Lirzin <mthl@openmailbox.org>
  4. ;; This file is part of Emacs-Guix.
  5. ;; Emacs-Guix is free software; you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;;
  10. ;; Emacs-Guix is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;;
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with Emacs-Guix. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; This file provides a general code related to location and contents of
  19. ;; Guix profiles.
  20. ;;; Code:
  21. (require 'guix-utils)
  22. (defvar guix-state-directory
  23. ;; guix-daemon honors `NIX_STATE_DIR'.
  24. (or (getenv "NIX_STATE_DIR") "/var/guix"))
  25. (defvar guix-user-profile
  26. (expand-file-name "~/.guix-profile")
  27. "User profile.")
  28. (defvar guix-system-profile
  29. (concat guix-state-directory "/profiles/system")
  30. "System profile.")
  31. (defvar guix-default-profile
  32. (concat guix-state-directory
  33. "/profiles/per-user/"
  34. (getenv "USER")
  35. "/guix-profile")
  36. "Default Guix profile.")
  37. (defvar guix-current-profile guix-default-profile
  38. "Current Guix profile.
  39. It is used by various commands as the default working profile.")
  40. (defvar guix-system-profile-regexp
  41. (rx-to-string `(and string-start
  42. (or ,guix-system-profile
  43. "/run/booted-system"
  44. "/run/current-system"))
  45. t)
  46. "Regexp matching system profiles.")
  47. (defun guix-current-profile? (profile)
  48. "Return non-nil, if package PROFILE is `guix-current-profile'."
  49. (string= (guix-package-profile profile)
  50. guix-current-profile))
  51. (defun guix-system-profile? (profile)
  52. "Return non-nil, if PROFILE is a system one."
  53. (string-match-p guix-system-profile-regexp profile))
  54. (defun guix-assert-non-system-profile (profile)
  55. "Raise an error when PROFILE is a system one."
  56. (when (guix-system-profile? profile)
  57. (user-error "\
  58. Packages cannot be installed or removed to/from profile '%s'.
  59. Use 'guix system reconfigure' shell command to modify a system profile."
  60. profile)))
  61. (defun guix-generation-file (profile generation)
  62. "Return the file name of a PROFILE's GENERATION."
  63. (format "%s-%s-link" profile generation))
  64. (defun guix-profile (profile)
  65. "Return normalized file name of PROFILE.
  66. \"Normalized\" means the returned file name is expanded, does not
  67. have a trailing slash and it is `guix-default-profile' if PROFILE
  68. is `guix-user-profile'. `guix-user-profile' is special because
  69. it is actually a symlink to a real user profile, and the HOME
  70. directory does not contain profile generations."
  71. (let ((profile (directory-file-name (expand-file-name profile))))
  72. (if (string= profile guix-user-profile)
  73. guix-default-profile
  74. profile)))
  75. (defun guix-generation-profile (profile &optional generation)
  76. "Return file name of PROFILE or its GENERATION.
  77. The returned file name is the one that have generations in the
  78. same parent directory.
  79. If PROFILE matches `guix-system-profile-regexp', then it is
  80. considered to be a system profile. Unlike usual profiles, for a
  81. system profile, packages are placed in 'profile' sub-directory,
  82. so the returned file name does not contain this potential
  83. trailing '/profile'."
  84. (let* ((profile (guix-profile profile))
  85. (profile (if (and (guix-system-profile? profile)
  86. (string-match (rx (group (* any))
  87. "/profile" string-end)
  88. profile))
  89. (match-string 1 profile)
  90. profile)))
  91. (if generation
  92. (guix-generation-file profile generation)
  93. profile)))
  94. (defun guix-package-profile (profile &optional generation)
  95. "Return file name of PROFILE or its GENERATION.
  96. The returned file name is the one where packages are installed.
  97. If PROFILE is a system one (see `guix-generation-profile'), then
  98. the returned file name ends with '/profile'."
  99. (let* ((profile (guix-generation-profile profile))
  100. (profile (if generation
  101. (guix-generation-file profile generation)
  102. profile)))
  103. (if (guix-system-profile? profile)
  104. (expand-file-name "profile" profile)
  105. profile)))
  106. (defun guix-manifest-file (profile &optional generation)
  107. "Return manifest file name of PROFILE or its GENERATION."
  108. (expand-file-name "manifest"
  109. (guix-package-profile profile generation)))
  110. (defun guix-profile-number-of-packages (profile &optional generation)
  111. "Return the number of packages installed in PROFILE or its GENERATION."
  112. (let ((manifest (guix-manifest-file profile generation)))
  113. ;; Just count a number of sexps inside (packages ...) of manifest
  114. ;; file. It should be much faster than running the REPL and
  115. ;; calculating manifest entries on the Scheme side.
  116. (when (file-exists-p manifest)
  117. (with-temp-buffer
  118. (insert-file-contents-literally manifest)
  119. (goto-char (point-min))
  120. (re-search-forward "(packages" nil t)
  121. (down-list)
  122. (let ((num 0)
  123. (pos (point)))
  124. (while (setq pos (condition-case nil
  125. (scan-sexps pos 1)
  126. (error nil)))
  127. (setq num (1+ num)))
  128. num)))))
  129. (defun guix-profile-number-of-generations (profile)
  130. "Return the number of generations of PROFILE."
  131. (let* ((profile (guix-generation-profile profile))
  132. (dir-name (file-name-directory profile))
  133. (base-name (file-name-nondirectory profile))
  134. (regexp (concat (regexp-quote base-name)
  135. "-[[:digit:]]+-link")))
  136. (when (file-exists-p profile)
  137. (length (directory-files dir-name nil regexp 'no-sort)))))
  138. ;;; Minibuffer readers
  139. (defun guix-read-profile (&optional default)
  140. "Prompt for profile and return it.
  141. Use DEFAULT as a start directory. If it is nil, use
  142. `guix-current-profile'."
  143. (guix-read-file-name "Profile: "
  144. (file-name-directory
  145. (or default guix-current-profile))))
  146. (defun guix-read-package-profile (&optional default)
  147. "Prompt for a package profile and return it.
  148. See `guix-read-profile' for the meaning of DEFAULT, and
  149. `guix-package-profile' for the meaning of package profile."
  150. (guix-package-profile (guix-read-profile default)))
  151. (defun guix-read-generation-profile (&optional default)
  152. "Prompt for a generation profile and return it.
  153. See `guix-read-profile' for the meaning of DEFAULT, and
  154. `guix-generation-profile' for the meaning of generation profile."
  155. (guix-generation-profile (guix-read-profile default)))
  156. ;;;###autoload
  157. (defun guix-set-current-profile (file-name)
  158. "Set `guix-current-profile' to FILE-NAME.
  159. Interactively, prompt for FILE-NAME. With prefix, use
  160. `guix-default-profile'."
  161. (interactive
  162. (list (if current-prefix-arg
  163. guix-default-profile
  164. (guix-read-package-profile))))
  165. (setq guix-current-profile file-name)
  166. (message "Current profile has been set to '%s'."
  167. guix-current-profile))
  168. (provide 'guix-profiles)
  169. ;;; guix-profiles.el ends here