repl.scm 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2020, 2021 Simon Tournier <zimon.toutoune@gmail.com>
  4. ;;; Copyright © 2020 Konrad Hinsen <konrad.hinsen@fastmail.net>
  5. ;;;
  6. ;;; This file is part of GNU Guix.
  7. ;;;
  8. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  9. ;;; under the terms of the GNU General Public License as published by
  10. ;;; the Free Software Foundation; either version 3 of the License, or (at
  11. ;;; your option) any later version.
  12. ;;;
  13. ;;; GNU Guix is distributed in the hope that it will be useful, but
  14. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;;; GNU General Public License for more details.
  17. ;;;
  18. ;;; You should have received a copy of the GNU General Public License
  19. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  20. (define-module (guix scripts repl)
  21. #:use-module (guix ui)
  22. #:use-module (guix scripts)
  23. #:use-module (guix repl)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (srfi srfi-26)
  26. #:use-module (srfi srfi-37)
  27. #:use-module (ice-9 match)
  28. #:use-module (rnrs bytevectors)
  29. #:autoload (guix describe) (current-profile)
  30. #:autoload (system repl repl) (start-repl)
  31. #:autoload (system repl server)
  32. (make-tcp-server-socket make-unix-domain-server-socket)
  33. #:export (guix-repl))
  34. ;;; Commentary:
  35. ;;;
  36. ;;; This command provides a Guile script runner and REPL in an environment
  37. ;;; that contains all the modules comprising Guix.
  38. (define %default-options
  39. `((type . guile)))
  40. (define %options
  41. (list (option '(#\h "help") #f #f
  42. (lambda args
  43. (show-help)
  44. (exit 0)))
  45. (option '(#\V "version") #f #f
  46. (lambda args
  47. (show-version-and-exit "guix repl")))
  48. (option '(#\t "type") #t #f
  49. (lambda (opt name arg result)
  50. (alist-cons 'type (string->symbol arg) result)))
  51. (option '("listen") #t #f
  52. (lambda (opt name arg result)
  53. (alist-cons 'listen arg result)))
  54. (option '(#\q) #f #f
  55. (lambda (opt name arg result)
  56. (alist-cons 'ignore-dot-guile? #t result)))
  57. (option '(#\L "load-path") #t #f
  58. (lambda (opt name arg result)
  59. ;; XXX: Imperatively modify the search paths.
  60. (set! %load-path (cons arg %load-path))
  61. (set! %load-compiled-path (cons arg %load-compiled-path))
  62. result))))
  63. (define (show-help)
  64. (display (G_ "Usage: guix repl [OPTIONS...] [-- FILE ARGS...]
  65. In the Guix execution environment, run FILE as a Guile script with
  66. command-line arguments ARGS. If no FILE is given, start a Guile REPL.\n"))
  67. (display (G_ "
  68. -t, --type=TYPE start a REPL of the given TYPE"))
  69. (display (G_ "
  70. --listen=ENDPOINT listen to ENDPOINT instead of standard input"))
  71. (display (G_ "
  72. -q inhibit loading of ~/.guile"))
  73. (newline)
  74. (display (G_ "
  75. -L, --load-path=DIR prepend DIR to the package module search path"))
  76. (newline)
  77. (display (G_ "
  78. -h, --help display this help and exit"))
  79. (display (G_ "
  80. -V, --version display version information and exit"))
  81. (newline)
  82. (show-bug-report-information))
  83. (define user-module
  84. ;; Module where we execute user code.
  85. (let ((module (resolve-module '(guix-user) #f #f #:ensure #t)))
  86. (beautify-user-module! module)
  87. module))
  88. (define (call-with-connection spec thunk)
  89. "Dynamically-bind the current input and output ports according to SPEC and
  90. call THUNK."
  91. (if (not spec)
  92. (thunk)
  93. ;; Note: the "PROTO:" prefix in SPEC is here so that we can eventually
  94. ;; parse things like "fd:123" in a non-ambiguous way.
  95. (match (string-index spec #\:)
  96. (#f
  97. (leave (G_ "~A: invalid listen specification~%") spec))
  98. (index
  99. (let ((protocol (string-take spec index))
  100. (address (string-drop spec (+ index 1))))
  101. (define socket
  102. (match protocol
  103. ("tcp"
  104. (make-tcp-server-socket #:port (string->number address)))
  105. ("unix"
  106. (make-unix-domain-server-socket #:path address))
  107. (_
  108. (leave (G_ "~A: unsupported protocol family~%")
  109. protocol))))
  110. (listen socket 10)
  111. (let loop ()
  112. (match (accept socket)
  113. ((connection . address)
  114. (if (= AF_UNIX (sockaddr:fam address))
  115. (info (G_ "accepted connection~%"))
  116. (info (G_ "accepted connection from ~a~%")
  117. (inet-ntop (sockaddr:fam address)
  118. (sockaddr:addr address))))
  119. (dynamic-wind
  120. (const #t)
  121. (lambda ()
  122. (parameterize ((current-input-port connection)
  123. (current-output-port connection))
  124. (thunk)))
  125. (lambda ()
  126. (false-if-exception (close-port connection))
  127. (info (G_ "connection closed~%"))))))
  128. (loop)))))))
  129. (define-command (guix-repl . args)
  130. (category plumbing)
  131. (synopsis "read-eval-print loop (REPL) for interactive programming")
  132. (define opts
  133. (parse-command-line args %options (list %default-options)
  134. #:build-options? #f
  135. #:argument-handler
  136. (lambda (arg result)
  137. (append `((script . ,arg)
  138. (ignore-dot-guile? . #t))
  139. result))))
  140. (define user-config
  141. (and=> (getenv "HOME")
  142. (lambda (home)
  143. (string-append home "/.guile"))))
  144. (define (set-user-module)
  145. (set-current-module user-module)
  146. (when (and (not (assoc-ref opts 'ignore-dot-guile?))
  147. user-config
  148. (file-exists? user-config))
  149. (load user-config)))
  150. (define script
  151. (reverse
  152. (filter-map (match-lambda
  153. (('script . script) script)
  154. (_ #f))
  155. opts)))
  156. (with-error-handling
  157. (unless (null? script)
  158. ;; Run script
  159. (save-module-excursion
  160. (lambda ()
  161. ;; Invoke 'current-profile' so that it memoizes the correct value
  162. ;; based on (program-arguments), before we call
  163. ;; 'set-program-arguments'. This in turn ensures that
  164. ;; (%package-module-path) will contain entries for the channels
  165. ;; available in the current profile.
  166. (current-profile)
  167. (set-program-arguments script)
  168. (set-user-module)
  169. ;; When passed a relative file name, 'load-in-vicinity' searches the
  170. ;; file in %LOAD-PATH. Thus, pass (getcwd) instead of ".".
  171. (load-in-vicinity (getcwd) (car script)))))
  172. (when (null? script)
  173. ;; Start REPL
  174. (let ((type (assoc-ref opts 'type)))
  175. (call-with-connection (assoc-ref opts 'listen)
  176. (lambda ()
  177. (case type
  178. ((guile)
  179. (save-module-excursion
  180. (lambda ()
  181. (set-user-module)
  182. ;; Do not exit repl on SIGINT.
  183. ((@@ (ice-9 top-repl) call-with-sigint)
  184. (lambda ()
  185. (start-repl))))))
  186. ((machine)
  187. (machine-repl))
  188. (else
  189. (leave (G_ "~a: unknown type of REPL~%") type)))))))))
  190. ;; Local Variables:
  191. ;; eval: (put 'call-with-connection 'scheme-indent-function 1)
  192. ;; End: