guix-profiles.el 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. ;;; guix-profiles.el --- Guix profiles
  2. ;; Copyright © 2014–2016 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. (defvar guix-state-directory
  22. ;; guix-daemon honors `NIX_STATE_DIR'.
  23. (or (getenv "NIX_STATE_DIR") "/var/guix"))
  24. (defvar guix-user-profile
  25. (expand-file-name "~/.guix-profile")
  26. "User profile.")
  27. (defvar guix-system-profile
  28. (concat guix-state-directory "/profiles/system")
  29. "System profile.")
  30. (defvar guix-default-profile
  31. (concat guix-state-directory
  32. "/profiles/per-user/"
  33. (getenv "USER")
  34. "/guix-profile")
  35. "Default Guix profile.")
  36. (defvar guix-current-profile guix-default-profile
  37. "Current Guix profile.
  38. It is used by various commands as the default working profile.")
  39. (defvar guix-system-profile-regexp
  40. (rx-to-string `(and string-start
  41. (or ,guix-system-profile
  42. "/run/booted-system"
  43. "/run/current-system"))
  44. t)
  45. "Regexp matching system profiles.")
  46. (defun guix-current-profile? (profile)
  47. "Return non-nil, if package PROFILE is `guix-current-profile'."
  48. (string= (guix-package-profile profile)
  49. guix-current-profile))
  50. (defun guix-system-profile? (profile)
  51. "Return non-nil, if PROFILE is a system one."
  52. (string-match-p guix-system-profile-regexp profile))
  53. (defun guix-assert-non-system-profile (profile)
  54. "Raise an error when PROFILE is a system one."
  55. (when (guix-system-profile? profile)
  56. (user-error "\
  57. Packages cannot be installed or removed to/from profile '%s'.
  58. Use 'guix system reconfigure' shell command to modify a system profile."
  59. profile)))
  60. (defun guix-generation-file (profile generation)
  61. "Return the file name of a PROFILE's GENERATION."
  62. (format "%s-%s-link" profile generation))
  63. (defun guix-profile (profile)
  64. "Return normalized file name of PROFILE.
  65. \"Normalized\" means the returned file name is expanded, does not
  66. have a trailing slash and it is `guix-default-profile' if PROFILE
  67. is `guix-user-profile'. `guix-user-profile' is special because
  68. it is actually a symlink to a real user profile, and the HOME
  69. directory does not contain profile generations."
  70. (let ((profile (directory-file-name (expand-file-name profile))))
  71. (if (string= profile guix-user-profile)
  72. guix-default-profile
  73. profile)))
  74. (defun guix-generation-profile (profile &optional generation)
  75. "Return file name of PROFILE or its GENERATION.
  76. The returned file name is the one that have generations in the
  77. same parent directory.
  78. If PROFILE matches `guix-system-profile-regexp', then it is
  79. considered to be a system profile. Unlike usual profiles, for a
  80. system profile, packages are placed in 'profile' sub-directory,
  81. so the returned file name does not contain this potential
  82. trailing '/profile'."
  83. (let* ((profile (guix-profile profile))
  84. (profile (if (and (guix-system-profile? profile)
  85. (string-match (rx (group (* any))
  86. "/profile" string-end)
  87. profile))
  88. (match-string 1 profile)
  89. profile)))
  90. (if generation
  91. (guix-generation-file profile generation)
  92. profile)))
  93. (defun guix-package-profile (profile &optional generation)
  94. "Return file name of PROFILE or its GENERATION.
  95. The returned file name is the one where packages are installed.
  96. If PROFILE is a system one (see `guix-generation-profile'), then
  97. the returned file name ends with '/profile'."
  98. (let* ((profile (guix-generation-profile profile))
  99. (profile (if generation
  100. (guix-generation-file profile generation)
  101. profile)))
  102. (if (guix-system-profile? profile)
  103. (expand-file-name "profile" profile)
  104. profile)))
  105. (defun guix-manifest-file (profile &optional generation)
  106. "Return manifest file name of PROFILE or its GENERATION."
  107. (expand-file-name "manifest"
  108. (guix-package-profile profile generation)))
  109. (defun guix-profile-number-of-packages (profile &optional generation)
  110. "Return the number of packages installed in PROFILE or its GENERATION."
  111. (let ((manifest (guix-manifest-file profile generation)))
  112. ;; Just count a number of sexps inside (packages ...) of manifest
  113. ;; file. It should be much faster than running the REPL and
  114. ;; calculating manifest entries on the Scheme side.
  115. (when (file-exists-p manifest)
  116. (with-temp-buffer
  117. (insert-file-contents-literally manifest)
  118. (goto-char (point-min))
  119. (re-search-forward "(packages" nil t)
  120. (down-list)
  121. (let ((num 0)
  122. (pos (point)))
  123. (while (setq pos (condition-case nil
  124. (scan-sexps pos 1)
  125. (error nil)))
  126. (setq num (1+ num)))
  127. num)))))
  128. (defun guix-profile-number-of-generations (profile)
  129. "Return the number of generations of PROFILE."
  130. (let* ((profile (guix-generation-profile profile))
  131. (dir-name (file-name-directory profile))
  132. (base-name (file-name-nondirectory profile))
  133. (regexp (concat (regexp-quote base-name)
  134. "-[[:digit:]]+-link")))
  135. (when (file-exists-p profile)
  136. (length (directory-files dir-name nil regexp 'no-sort)))))
  137. ;;; Minibuffer readers
  138. (defun guix-read-profile (&optional default)
  139. "Prompt for profile and return it.
  140. Use DEFAULT as a start directory. If it is nil, use
  141. `guix-current-profile'."
  142. (read-file-name "Profile: "
  143. (file-name-directory
  144. (or default guix-current-profile))))
  145. (defun guix-read-package-profile (&optional default)
  146. "Prompt for a package profile and return it.
  147. See `guix-read-profile' for the meaning of DEFAULT, and
  148. `guix-package-profile' for the meaning of package profile."
  149. (guix-package-profile (guix-read-profile default)))
  150. (defun guix-read-generation-profile (&optional default)
  151. "Prompt for a generation profile and return it.
  152. See `guix-read-profile' for the meaning of DEFAULT, and
  153. `guix-generation-profile' for the meaning of generation profile."
  154. (guix-generation-profile (guix-read-profile default)))
  155. ;;;###autoload
  156. (defun guix-set-current-profile (file-name)
  157. "Set `guix-current-profile' to FILE-NAME.
  158. Interactively, prompt for FILE-NAME. With prefix, use
  159. `guix-default-profile'."
  160. (interactive
  161. (list (if current-prefix-arg
  162. guix-default-profile
  163. (guix-read-package-profile))))
  164. (setq guix-current-profile file-name)
  165. (message "Current profile has been set to '%s'."
  166. guix-current-profile))
  167. (provide 'guix-profiles)
  168. ;;; guix-profiles.el ends here