import.scm 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
  3. ;;; Copyright © 2021 Andrew Tropin <andrew@trop.in>
  4. ;;; Copyright © 2021-2022 Ludovic Courtès <ludo@gnu.org>
  5. ;;; Copyright © 2022 Arjan Adriaanse <arjan@adriaan.se>
  6. ;;; Copyright © 2022 Antero Mejr <antero@mailbox.org>
  7. ;;;
  8. ;;; This file is part of GNU Guix.
  9. ;;;
  10. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  11. ;;; under the terms of the GNU General Public License as published by
  12. ;;; the Free Software Foundation; either version 3 of the License, or (at
  13. ;;; your option) any later version.
  14. ;;;
  15. ;;; GNU Guix is distributed in the hope that it will be useful, but
  16. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ;;; GNU General Public License for more details.
  19. ;;;
  20. ;;; You should have received a copy of the GNU General Public License
  21. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  22. (define-module (guix scripts home import)
  23. #:use-module (guix profiles)
  24. #:use-module (guix ui)
  25. #:use-module (guix utils)
  26. #:use-module (guix packages)
  27. #:autoload (guix scripts package) (manifest-entry-version-prefix)
  28. #:use-module (guix read-print)
  29. #:use-module (gnu packages)
  30. #:use-module (ice-9 match)
  31. #:use-module (ice-9 rdelim)
  32. #:use-module (ice-9 regex)
  33. #:use-module (ice-9 popen)
  34. #:use-module (srfi srfi-1)
  35. #:use-module (srfi srfi-26)
  36. #:export (import-manifest
  37. ;; For tests.
  38. manifest+configuration-files->code))
  39. ;;; Commentary:
  40. ;;;
  41. ;;; This module provides utilities for generating home service
  42. ;;; configurations from existing "dotfiles".
  43. ;;;
  44. ;;; Code:
  45. (define (basename+remove-dots file-name)
  46. "Remove the dot from the dotfile FILE-NAME; replace the other dots in
  47. FILE-NAME with \"-\", and return the basename of it."
  48. (string-map (match-lambda
  49. (#\. #\-)
  50. (c c))
  51. (let ((base (basename file-name)))
  52. (if (string-prefix? "." base)
  53. (string-drop base 1)
  54. base))))
  55. (define (generate-bash-configuration+modules destination-directory)
  56. (define (destination-append path)
  57. (string-append destination-directory "/" path))
  58. (define alias-rx
  59. (make-regexp "^alias ([^=]+)=[\"'](.+)[\"']$"))
  60. (define (bash-alias->pair line)
  61. (match (regexp-exec alias-rx line)
  62. (#f #f)
  63. (matched
  64. `(,(match:substring matched 1) . ,(match:substring matched 2)))))
  65. (define (parse-aliases input)
  66. (let loop ((result '()))
  67. (match (read-line input)
  68. ((? eof-object?)
  69. (reverse result))
  70. (line
  71. (match (bash-alias->pair line)
  72. (#f (loop result))
  73. (alias (loop (cons alias result))))))))
  74. (let ((rc (destination-append ".bashrc"))
  75. (profile (destination-append ".bash_profile"))
  76. (logout (destination-append ".bash_logout")))
  77. `((service home-bash-service-type
  78. (home-bash-configuration
  79. ,@(if (file-exists? rc)
  80. `((aliases
  81. ',(let* ((port (open-pipe* OPEN_READ "bash" "-i" "-c" "alias"))
  82. (alist (parse-aliases port)))
  83. (close-port port)
  84. alist)))
  85. '())
  86. ,@(if (file-exists? rc)
  87. `((bashrc
  88. (list (local-file ,rc
  89. ,(basename+remove-dots rc)))))
  90. '())
  91. ,@(if (file-exists? profile)
  92. `((bash-profile
  93. (list (local-file ,profile
  94. ,(basename+remove-dots profile)))))
  95. '())
  96. ,@(if (file-exists? logout)
  97. `((bash-logout
  98. (list (local-file ,logout
  99. ,(basename+remove-dots logout)))))
  100. '())))
  101. (guix gexp)
  102. (gnu home services shells))))
  103. (define %files+configurations-alist
  104. `((".bashrc" . ,generate-bash-configuration+modules)
  105. (".bash_profile" . ,generate-bash-configuration+modules)
  106. (".bash_logout" . ,generate-bash-configuration+modules)))
  107. (define (configurations+modules configuration-directory)
  108. "Return a list of procedures which when called, generate code for a home
  109. service declaration. Copy configuration files to CONFIGURATION-DIRECTORY; the
  110. generated service declarations will refer to those files that have been saved
  111. in CONFIGURATION-DIRECTORY."
  112. (define configurations
  113. (delete-duplicates
  114. (filter-map (match-lambda
  115. ((file . proc)
  116. (let ((absolute-path (string-append (getenv "HOME")
  117. "/" file)))
  118. (and (file-exists? absolute-path)
  119. (begin
  120. (copy-file absolute-path
  121. (string-append
  122. configuration-directory "/" file))
  123. proc)))))
  124. %files+configurations-alist)
  125. eq?))
  126. (map (lambda (proc) (proc configuration-directory)) configurations))
  127. (define (manifest+configuration-files->code manifest
  128. configuration-directory)
  129. "Read MANIFEST and the user's configuration files listed in
  130. %FILES+CONFIGURATIONS-ALIST, and return a 'home-environment' sexp. Copy the
  131. user's files to CONFIGURATION-DIRECTORY; the generated sexp refers to them."
  132. (match (manifest->code manifest
  133. #:entry-package-version
  134. manifest-entry-version-prefix)
  135. (('begin ('use-modules profile-modules ...)
  136. definitions ... ('packages->manifest packages))
  137. (match (configurations+modules configuration-directory)
  138. (((services . modules) ...)
  139. `(begin
  140. (use-modules (gnu home)
  141. (gnu packages)
  142. (gnu services)
  143. ,@(delete-duplicates
  144. (append profile-modules (concatenate modules))))
  145. ,@definitions
  146. (home-environment
  147. (packages ,packages)
  148. (services (list ,@services)))))))
  149. (('begin ('specifications->manifest packages))
  150. (match (configurations+modules configuration-directory)
  151. (((services . modules) ...)
  152. `(begin
  153. (use-modules (gnu home)
  154. (gnu packages)
  155. (gnu services)
  156. ,@(delete-duplicates (concatenate modules)))
  157. ,(vertical-space 1)
  158. (home-environment
  159. ,(comment (G_ "\
  160. ;; Below is the list of packages that will show up in your
  161. ;; Home profile, under ~/.guix-home/profile.\n"))
  162. (packages
  163. (specifications->packages ,packages))
  164. ,(vertical-space 1)
  165. ,(comment (G_ "\
  166. ;; Below is the list of Home services. To search for available
  167. ;; services, run 'guix home search KEYWORD' in a terminal.\n"))
  168. (services (list ,@services)))))))))
  169. (define* (import-manifest
  170. manifest destination-directory
  171. #:optional (port (current-output-port)))
  172. "Write to PORT a <home-environment> corresponding to MANIFEST."
  173. (match (manifest+configuration-files->code manifest
  174. destination-directory)
  175. (('begin exp ...)
  176. (format port (G_ "\
  177. ;; This \"home-environment\" file can be passed to 'guix home reconfigure'
  178. ;; to reproduce the content of your profile. This is \"symbolic\": it only
  179. ;; specifies package names. To reproduce the exact same profile, you also
  180. ;; need to capture the channels being used, as returned by \"guix describe\".
  181. ;; See the \"Replicating Guix\" section in the manual.\n"))
  182. (newline port)
  183. (pretty-print-with-comments/splice port exp))))