shells.scm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2021 Andrew Tropin <andrew@trop.in>
  3. ;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
  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 (gnu home services shells)
  20. #:use-module (gnu services configuration)
  21. #:use-module (gnu home services utils)
  22. #:use-module (gnu home services)
  23. #:use-module (gnu packages shells)
  24. #:use-module (gnu packages bash)
  25. #:use-module (guix gexp)
  26. #:use-module (guix packages)
  27. #:use-module (srfi srfi-1)
  28. #:use-module (srfi srfi-26)
  29. #:use-module (ice-9 match)
  30. #:export (home-shell-profile-service-type
  31. home-shell-profile-configuration
  32. home-bash-service-type
  33. home-bash-configuration
  34. home-bash-extension
  35. home-zsh-service-type
  36. home-zsh-configuration
  37. home-zsh-extension
  38. home-fish-service-type
  39. home-fish-configuration
  40. home-fish-extension))
  41. ;;; Commentary:
  42. ;;;
  43. ;;; This module contains shell related services like Zsh.
  44. ;;;
  45. ;;; Code:
  46. ;;;
  47. ;;; Shell profile.
  48. ;;;
  49. (define path? string?)
  50. (define (serialize-path field-name val) val)
  51. (define-configuration home-shell-profile-configuration
  52. (profile
  53. (text-config '())
  54. "\
  55. @code{home-shell-profile} is instantiated automatically by
  56. @code{home-environment}, DO NOT create this service manually, it can
  57. only be extended.
  58. @code{profile} is a list of file-like objects, which will go to
  59. @file{~/.profile}. By default @file{~/.profile} contains the
  60. initialization code, which have to be evaluated by login shell to make
  61. home-environment's profile avaliable to the user, but other commands
  62. can be added to the file if it is really necessary.
  63. In most cases shell's configuration files are preferred places for
  64. user's customizations. Extend home-shell-profile service only if you
  65. really know what you do."))
  66. (define (add-shell-profile-file config)
  67. `(("profile"
  68. ,(mixed-text-file
  69. "shell-profile"
  70. "\
  71. HOME_ENVIRONMENT=$HOME/.guix-home
  72. . $HOME_ENVIRONMENT/setup-environment
  73. $HOME_ENVIRONMENT/on-first-login\n"
  74. (serialize-configuration
  75. config
  76. (filter-configuration-fields
  77. home-shell-profile-configuration-fields '(profile)))))))
  78. (define (add-profile-extensions config extensions)
  79. (home-shell-profile-configuration
  80. (inherit config)
  81. (profile
  82. (append (home-shell-profile-configuration-profile config)
  83. extensions))))
  84. (define home-shell-profile-service-type
  85. (service-type (name 'home-shell-profile)
  86. (extensions
  87. (list (service-extension
  88. home-files-service-type
  89. add-shell-profile-file)))
  90. (compose concatenate)
  91. (extend add-profile-extensions)
  92. (default-value (home-shell-profile-configuration))
  93. (description "Create @file{~/.profile}, which is used
  94. for environment initialization of POSIX compliant login shells. This
  95. service type can be extended with a list of file-like objects.")))
  96. (define (serialize-boolean field-name val) "")
  97. (define (serialize-posix-env-vars field-name val)
  98. #~(string-append
  99. #$@(map
  100. (match-lambda
  101. ((key . #f)
  102. "")
  103. ((key . #t)
  104. #~(string-append "export " #$key "\n"))
  105. ((key . value)
  106. #~(string-append "export " #$key "=" #$value "\n")))
  107. val)))
  108. ;;;
  109. ;;; Zsh.
  110. ;;;
  111. (define-configuration home-zsh-configuration
  112. (package
  113. (package zsh)
  114. "The Zsh package to use.")
  115. (xdg-flavor?
  116. (boolean #t)
  117. "Place all the configs to @file{$XDG_CONFIG_HOME/zsh}. Makes
  118. @file{~/.zshenv} to set @env{ZDOTDIR} to @file{$XDG_CONFIG_HOME/zsh}.
  119. Shell startup process will continue with
  120. @file{$XDG_CONFIG_HOME/zsh/.zshenv}.")
  121. (environment-variables
  122. (alist '())
  123. "Association list of environment variables to set for the Zsh session."
  124. serialize-posix-env-vars)
  125. (zshenv
  126. (text-config '())
  127. "List of file-like objects, which will be added to @file{.zshenv}.
  128. Used for setting user's shell environment variables. Must not contain
  129. commands assuming the presence of tty or producing output. Will be
  130. read always. Will be read before any other file in @env{ZDOTDIR}.")
  131. (zprofile
  132. (text-config '())
  133. "List of file-like objects, which will be added to @file{.zprofile}.
  134. Used for executing user's commands at start of login shell (In most
  135. cases the shell started on tty just after login). Will be read before
  136. @file{.zlogin}.")
  137. (zshrc
  138. (text-config '())
  139. "List of file-like objects, which will be added to @file{.zshrc}.
  140. Used for executing user's commands at start of interactive shell (The
  141. shell for interactive usage started by typing @code{zsh} or by
  142. terminal app or any other program).")
  143. (zlogin
  144. (text-config '())
  145. "List of file-like objects, which will be added to @file{.zlogin}.
  146. Used for executing user's commands at the end of starting process of
  147. login shell.")
  148. (zlogout
  149. (text-config '())
  150. "List of file-like objects, which will be added to @file{.zlogout}.
  151. Used for executing user's commands at the exit of login shell. It
  152. won't be read in some cases (if the shell terminates by exec'ing
  153. another process for example)."))
  154. (define (add-zsh-configuration config)
  155. (let* ((xdg-flavor? (home-zsh-configuration-xdg-flavor? config)))
  156. (define prefix-file
  157. (cut string-append
  158. (if xdg-flavor?
  159. "config/zsh/."
  160. "") <>))
  161. (define (filter-fields field)
  162. (filter-configuration-fields home-zsh-configuration-fields
  163. (list field)))
  164. (define (serialize-field field)
  165. (serialize-configuration
  166. config
  167. (filter-fields field)))
  168. (define (file-if-not-empty field)
  169. (let ((file-name (symbol->string field))
  170. (field-obj (car (filter-fields field))))
  171. (if (not (null? ((configuration-field-getter field-obj) config)))
  172. `(,(prefix-file file-name)
  173. ,(mixed-text-file
  174. file-name
  175. (serialize-field field)))
  176. '())))
  177. (filter
  178. (compose not null?)
  179. `(,(if xdg-flavor?
  180. `("zshenv"
  181. ,(mixed-text-file
  182. "auxiliary-zshenv"
  183. (if xdg-flavor?
  184. "source ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/.zshenv\n"
  185. "")))
  186. '())
  187. (,(prefix-file "zshenv")
  188. ,(mixed-text-file
  189. "zshenv"
  190. (if xdg-flavor?
  191. "export ZDOTDIR=${XDG_CONFIG_HOME:-$HOME/.config}/zsh\n"
  192. "")
  193. (serialize-field 'zshenv)
  194. (serialize-field 'environment-variables)))
  195. (,(prefix-file "zprofile")
  196. ,(mixed-text-file
  197. "zprofile"
  198. "\
  199. # Setups system and user profiles and related variables
  200. source /etc/profile
  201. # Setups home environment profile
  202. source ~/.profile
  203. # It's only necessary if zsh is a login shell, otherwise profiles will
  204. # be already sourced by bash
  205. "
  206. (serialize-field 'zprofile)))
  207. ,@(list (file-if-not-empty 'zshrc)
  208. (file-if-not-empty 'zlogin)
  209. (file-if-not-empty 'zlogout))))))
  210. (define (add-zsh-packages config)
  211. (list (home-zsh-configuration-package config)))
  212. (define-configuration/no-serialization home-zsh-extension
  213. (environment-variables
  214. (alist '())
  215. "Association list of environment variables to set.")
  216. (zshrc
  217. (text-config '())
  218. "List of file-like objects.")
  219. (zshenv
  220. (text-config '())
  221. "List of file-like objects.")
  222. (zprofile
  223. (text-config '())
  224. "List of file-like objects.")
  225. (zlogin
  226. (text-config '())
  227. "List of file-like objects.")
  228. (zlogout
  229. (text-config '())
  230. "List of file-like objects."))
  231. (define (home-zsh-extensions original-config extension-configs)
  232. (home-zsh-configuration
  233. (inherit original-config)
  234. (environment-variables
  235. (append (home-zsh-configuration-environment-variables original-config)
  236. (append-map
  237. home-zsh-extension-environment-variables extension-configs)))
  238. (zshrc
  239. (append (home-zsh-configuration-zshrc original-config)
  240. (append-map
  241. home-zsh-extension-zshrc extension-configs)))
  242. (zshenv
  243. (append (home-zsh-configuration-zshenv original-config)
  244. (append-map
  245. home-zsh-extension-zshenv extension-configs)))
  246. (zprofile
  247. (append (home-zsh-configuration-zprofile original-config)
  248. (append-map
  249. home-zsh-extension-zprofile extension-configs)))
  250. (zlogin
  251. (append (home-zsh-configuration-zlogin original-config)
  252. (append-map
  253. home-zsh-extension-zlogin extension-configs)))
  254. (zlogout
  255. (append (home-zsh-configuration-zlogout original-config)
  256. (append-map
  257. home-zsh-extension-zlogout extension-configs)))))
  258. (define home-zsh-service-type
  259. (service-type (name 'home-zsh)
  260. (extensions
  261. (list (service-extension
  262. home-files-service-type
  263. add-zsh-configuration)
  264. (service-extension
  265. home-profile-service-type
  266. add-zsh-packages)))
  267. (compose identity)
  268. (extend home-zsh-extensions)
  269. (default-value (home-zsh-configuration))
  270. (description "Install and configure Zsh.")))
  271. ;;;
  272. ;;; Bash.
  273. ;;;
  274. (define-configuration home-bash-configuration
  275. (package
  276. (package bash)
  277. "The Bash package to use.")
  278. (guix-defaults?
  279. (boolean #t)
  280. "Add sane defaults like reading @file{/etc/bashrc}, coloring output
  281. for @code{ls} provided by guix to @file{.bashrc}.")
  282. (environment-variables
  283. (alist '())
  284. "Association list of environment variables to set for the Bash session."
  285. serialize-posix-env-vars)
  286. (bash-profile
  287. (text-config '())
  288. "List of file-like objects, which will be added to @file{.bash_profile}.
  289. Used for executing user's commands at start of login shell (In most
  290. cases the shell started on tty just after login). @file{.bash_login}
  291. won't be ever read, because @file{.bash_profile} always present.")
  292. (bashrc
  293. (text-config '())
  294. "List of file-like objects, which will be added to @file{.bashrc}.
  295. Used for executing user's commands at start of interactive shell (The
  296. shell for interactive usage started by typing @code{bash} or by
  297. terminal app or any other program).")
  298. (bash-logout
  299. (text-config '())
  300. "List of file-like objects, which will be added to @file{.bash_logout}.
  301. Used for executing user's commands at the exit of login shell. It
  302. won't be read in some cases (if the shell terminates by exec'ing
  303. another process for example)."))
  304. ;; TODO: Use value from (gnu system shadow)
  305. (define guix-bashrc
  306. "\
  307. # Bash initialization for interactive non-login shells and
  308. # for remote shells (info \"(bash) Bash Startup Files\").
  309. # Export 'SHELL' to child processes. Programs such as 'screen'
  310. # honor it and otherwise use /bin/sh.
  311. export SHELL
  312. if [[ $- != *i* ]]
  313. then
  314. # We are being invoked from a non-interactive shell. If this
  315. # is an SSH session (as in \"ssh host command\"), source
  316. # /etc/profile so we get PATH and other essential variables.
  317. [[ -n \"$SSH_CLIENT\" ]] && source /etc/profile
  318. # Don't do anything else.
  319. return
  320. fi
  321. # Source the system-wide file.
  322. source /etc/bashrc
  323. # Adjust the prompt depending on whether we're in 'guix environment'.
  324. if [ -n \"$GUIX_ENVIRONMENT\" ]
  325. then
  326. PS1='\\u@\\h \\w [env]\\$ '
  327. else
  328. PS1='\\u@\\h \\w\\$ '
  329. fi
  330. alias ls='ls -p --color=auto'
  331. alias ll='ls -l'
  332. alias grep='grep --color=auto'\n")
  333. (define (add-bash-configuration config)
  334. (define (filter-fields field)
  335. (filter-configuration-fields home-bash-configuration-fields
  336. (list field)))
  337. (define (serialize-field field)
  338. (serialize-configuration
  339. config
  340. (filter-fields field)))
  341. (define* (file-if-not-empty field #:optional (extra-content #f))
  342. (let ((file-name (symbol->string field))
  343. (field-obj (car (filter-fields field))))
  344. (if (or extra-content
  345. (not (null? ((configuration-field-getter field-obj) config))))
  346. `(,(object->snake-case-string file-name)
  347. ,(mixed-text-file
  348. (object->snake-case-string file-name)
  349. (if extra-content extra-content "")
  350. (serialize-field field)))
  351. '())))
  352. (filter
  353. (compose not null?)
  354. `(("bash_profile"
  355. ,(mixed-text-file
  356. "bash_profile"
  357. "\
  358. # Setups system and user profiles and related variables
  359. # /etc/profile will be sourced by bash automatically
  360. # Setups home environment profile
  361. if [ -f ~/.profile ]; then source ~/.profile; fi
  362. # Honor per-interactive-shell startup file
  363. if [ -f ~/.bashrc ]; then source ~/.bashrc; fi
  364. "
  365. (serialize-field 'bash-profile)
  366. (serialize-field 'environment-variables)))
  367. ,@(list (file-if-not-empty
  368. 'bashrc
  369. (if (home-bash-configuration-guix-defaults? config)
  370. guix-bashrc
  371. #f))
  372. (file-if-not-empty 'bash-logout)))))
  373. (define (add-bash-packages config)
  374. (list (home-bash-configuration-package config)))
  375. (define-configuration/no-serialization home-bash-extension
  376. (environment-variables
  377. (alist '())
  378. "Association list of environment variables to set.")
  379. (bash-profile
  380. (text-config '())
  381. "List of file-like objects.")
  382. (bashrc
  383. (text-config '())
  384. "List of file-like objects.")
  385. (bash-logout
  386. (text-config '())
  387. "List of file-like objects."))
  388. (define (home-bash-extensions original-config extension-configs)
  389. (home-bash-configuration
  390. (inherit original-config)
  391. (environment-variables
  392. (append (home-bash-configuration-environment-variables original-config)
  393. (append-map
  394. home-bash-extension-environment-variables extension-configs)))
  395. (bash-profile
  396. (append (home-bash-configuration-bash-profile original-config)
  397. (append-map
  398. home-bash-extension-bash-profile extension-configs)))
  399. (bashrc
  400. (append (home-bash-configuration-bashrc original-config)
  401. (append-map
  402. home-bash-extension-bashrc extension-configs)))
  403. (bash-logout
  404. (append (home-bash-configuration-bash-logout original-config)
  405. (append-map
  406. home-bash-extension-bash-logout extension-configs)))))
  407. (define home-bash-service-type
  408. (service-type (name 'home-bash)
  409. (extensions
  410. (list (service-extension
  411. home-files-service-type
  412. add-bash-configuration)
  413. (service-extension
  414. home-profile-service-type
  415. add-bash-packages)))
  416. (compose identity)
  417. (extend home-bash-extensions)
  418. (default-value (home-bash-configuration))
  419. (description "Install and configure GNU Bash.")))
  420. ;;;
  421. ;;; Fish.
  422. ;;;
  423. (define (serialize-fish-aliases field-name val)
  424. #~(string-append
  425. #$@(map (match-lambda
  426. ((key . value)
  427. #~(string-append "alias " #$key " \"" #$value "\"\n"))
  428. (_ ""))
  429. val)))
  430. (define (serialize-fish-abbreviations field-name val)
  431. #~(string-append
  432. #$@(map (match-lambda
  433. ((key . value)
  434. #~(string-append "abbr --add " #$key " " #$value "\n"))
  435. (_ ""))
  436. val)))
  437. (define (serialize-fish-env-vars field-name val)
  438. #~(string-append
  439. #$@(map (match-lambda
  440. ((key . #f)
  441. "")
  442. ((key . #t)
  443. #~(string-append "set " #$key "\n"))
  444. ((key . value)
  445. #~(string-append "set " #$key " " #$value "\n")))
  446. val)))
  447. (define-configuration home-fish-configuration
  448. (package
  449. (package fish)
  450. "The Fish package to use.")
  451. (config
  452. (text-config '())
  453. "List of file-like objects, which will be added to
  454. @file{$XDG_CONFIG_HOME/fish/config.fish}.")
  455. (environment-variables
  456. (alist '())
  457. "Association list of environment variables to set in Fish."
  458. serialize-fish-env-vars)
  459. (aliases
  460. (alist '())
  461. "Association list of aliases for Fish, both the key and the value
  462. should be a string. An alias is just a simple function that wraps a
  463. command, If you want something more akin to @dfn{aliases} in POSIX
  464. shells, see the @code{abbreviations} field."
  465. serialize-fish-aliases)
  466. (abbreviations
  467. (alist '())
  468. "Association list of abbreviations for Fish. These are words that,
  469. when typed in the shell, will automatically expand to the full text."
  470. serialize-fish-abbreviations))
  471. (define (fish-files-service config)
  472. `(("config/fish/config.fish"
  473. ,(mixed-text-file
  474. "fish-config.fish"
  475. #~(string-append "\
  476. # if we haven't sourced the login config, do it
  477. status --is-login; and not set -q __fish_login_config_sourced
  478. and begin
  479. set --prepend fish_function_path "
  480. #$fish-foreign-env
  481. "/share/fish/functions
  482. fenv source $HOME/.profile
  483. set -e fish_function_path[1]
  484. set -g __fish_login_config_sourced 1
  485. end\n\n")
  486. (serialize-configuration
  487. config
  488. home-fish-configuration-fields)))))
  489. (define (fish-profile-service config)
  490. (list (home-fish-configuration-package config)))
  491. (define-configuration/no-serialization home-fish-extension
  492. (config
  493. (text-config '())
  494. "List of file-like objects for extending the Fish initialization file.")
  495. (environment-variables
  496. (alist '())
  497. "Association list of environment variables to set.")
  498. (aliases
  499. (alist '())
  500. "Association list of Fish aliases.")
  501. (abbreviations
  502. (alist '())
  503. "Association list of Fish abbreviations."))
  504. (define (home-fish-extensions original-config extension-configs)
  505. (home-fish-configuration
  506. (inherit original-config)
  507. (config
  508. (append (home-fish-configuration-config original-config)
  509. (append-map
  510. home-fish-extension-config extension-configs)))
  511. (environment-variables
  512. (append (home-fish-configuration-environment-variables original-config)
  513. (append-map
  514. home-fish-extension-environment-variables extension-configs)))
  515. (aliases
  516. (append (home-fish-configuration-aliases original-config)
  517. (append-map
  518. home-fish-extension-aliases extension-configs)))
  519. (abbreviations
  520. (append (home-fish-configuration-abbreviations original-config)
  521. (append-map
  522. home-fish-extension-abbreviations extension-configs)))))
  523. ;; TODO: Support for generating completion files
  524. ;; TODO: Support for installing plugins
  525. (define home-fish-service-type
  526. (service-type (name 'home-fish)
  527. (extensions
  528. (list (service-extension
  529. home-files-service-type
  530. fish-files-service)
  531. (service-extension
  532. home-profile-service-type
  533. fish-profile-service)))
  534. (compose identity)
  535. (extend home-fish-extensions)
  536. (default-value (home-fish-configuration))
  537. (description "\
  538. Install and configure Fish, the friendly interactive shell.")))
  539. (define (generate-home-shell-profile-documentation)
  540. (generate-documentation
  541. `((home-shell-profile-configuration
  542. ,home-shell-profile-configuration-fields))
  543. 'home-shell-profile-configuration))
  544. (define (generate-home-bash-documentation)
  545. (generate-documentation
  546. `((home-bash-configuration
  547. ,home-bash-configuration-fields))
  548. 'home-bash-configuration))
  549. (define (generate-home-zsh-documentation)
  550. (generate-documentation
  551. `((home-zsh-configuration
  552. ,home-zsh-configuration-fields))
  553. 'home-zsh-configuration))
  554. (define (generate-home-fish-documentation)
  555. (string-append
  556. (generate-documentation
  557. `((home-fish-configuration
  558. ,home-fish-configuration-fields))
  559. 'home-fish-configuration)
  560. "\n\n"
  561. (generate-documentation
  562. `((home-fish-extension
  563. ,home-fish-extension-fields))
  564. 'home-fish-extension)))