command-line.scm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. ;;; Parsing Guile's command-line
  2. ;;; Copyright (C) 1994-1998, 2000-2011, 2012 Free Software Foundation, Inc.
  3. ;;;; This library is free software; you can redistribute it and/or
  4. ;;;; modify it under the terms of the GNU Lesser General Public
  5. ;;;; License as published by the Free Software Foundation; either
  6. ;;;; version 3 of the License, or (at your option) any later version.
  7. ;;;;
  8. ;;;; This library is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;;;; Lesser General Public License for more details.
  12. ;;;;
  13. ;;;; You should have received a copy of the GNU Lesser General Public
  14. ;;;; License along with this library; if not, write to the Free Software
  15. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. ;;; Code:
  17. ;;;
  18. ;;; Please be careful not to load up other modules in this file, unless
  19. ;;; they are explicitly requested. Loading modules currently imposes a
  20. ;;; speed penalty of a few stats, an mmap, and some allocation, which
  21. ;;; can range from 1 to 20ms, depending on the state of your disk cache.
  22. ;;; Since `compile-shell-switches' is called even for the most transient
  23. ;;; of command-line programs, we need to keep it lean.
  24. ;;;
  25. ;;; Generally speaking, the goal is for Guile to boot and execute simple
  26. ;;; expressions like "1" within 20ms or less, measured using system time
  27. ;;; from the time of the `guile' invocation to exit.
  28. ;;;
  29. (define-module (ice-9 command-line)
  30. #:autoload (system vm vm) (set-default-vm-engine! set-vm-engine! the-vm)
  31. #:export (compile-shell-switches
  32. version-etc
  33. *GPLv3+*
  34. *LGPLv3+*
  35. emit-bug-reporting-address))
  36. ;; An initial stab at i18n.
  37. (define _ gettext)
  38. (define *GPLv3+*
  39. (_ "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
  40. This is free software: you are free to change and redistribute it.
  41. There is NO WARRANTY, to the extent permitted by law."))
  42. (define *LGPLv3+*
  43. (_ "License LGPLv3+: GNU LGPL 3 or later <http://gnu.org/licenses/lgpl.html>.
  44. This is free software: you are free to change and redistribute it.
  45. There is NO WARRANTY, to the extent permitted by law."))
  46. ;; Display the --version information in the
  47. ;; standard way: command and package names, package version, followed
  48. ;; by a short license notice and a list of up to 10 author names.
  49. ;; If COMMAND_NAME is NULL, the PACKAGE is asumed to be the name of
  50. ;; the program. The formats are therefore:
  51. ;; PACKAGE VERSION
  52. ;; or
  53. ;; COMMAND_NAME (PACKAGE) VERSION.
  54. ;;
  55. ;; Based on the version-etc gnulib module.
  56. ;;
  57. (define* (version-etc package version #:key
  58. (port (current-output-port))
  59. ;; FIXME: authors
  60. (copyright-year 2012)
  61. (copyright-holder "Free Software Foundation, Inc.")
  62. (copyright (format #f "Copyright (C) ~a ~a"
  63. copyright-year copyright-holder))
  64. (license *GPLv3+*)
  65. command-name
  66. packager packager-version)
  67. (if command-name
  68. (format port "~a (~a) ~a\n" command-name package version)
  69. (format port "~a ~a\n" package version))
  70. (if packager
  71. (if packager-version
  72. (format port (_ "Packaged by ~a (~a)\n") packager packager-version)
  73. (format port (_ "Packaged by ~a\n") packager)))
  74. (display copyright port)
  75. (newline port)
  76. (newline port)
  77. (display license port)
  78. (newline port))
  79. ;; Display the usual `Report bugs to' stanza.
  80. ;;
  81. (define* (emit-bug-reporting-address package bug-address #:key
  82. (port (current-output-port))
  83. (url (string-append
  84. "http://www.gnu.org/software/"
  85. package
  86. "/"))
  87. packager packager-bug-address)
  88. (format port (_ "\nReport bugs to: ~a\n") bug-address)
  89. (if (and packager packager-bug-address)
  90. (format port (_ "Report ~a bugs to: ~a\n") packager packager-bug-address))
  91. (format port (_ "~a home page: <~a>\n") package url)
  92. (format port
  93. (_ "General help using GNU software: <http://www.gnu.org/gethelp/>\n")))
  94. (define *usage*
  95. (_ "Evaluate Scheme code, interactively or from a script.
  96. [-s] FILE load Scheme source code from FILE, and exit
  97. -c EXPR evalute Scheme expression EXPR, and exit
  98. -- stop scanning arguments; run interactively
  99. The above switches stop argument processing, and pass all
  100. remaining arguments as the value of (command-line).
  101. If FILE begins with `-' the -s switch is mandatory.
  102. -L DIRECTORY add DIRECTORY to the front of the module load path
  103. -x EXTENSION add EXTENSION to the front of the load extensions
  104. -l FILE load Scheme source code from FILE
  105. -e FUNCTION after reading script, apply FUNCTION to
  106. command line arguments
  107. -ds do -s script at this point
  108. --debug start with the \"debugging\" VM engine
  109. --no-debug start with the normal VM engine, which also supports debugging
  110. Default is to enable debugging for interactive
  111. use, but not for `-s' and `-c'.
  112. --auto-compile compile source files automatically
  113. --fresh-auto-compile invalidate auto-compilation cache
  114. --no-auto-compile disable automatic source file compilation
  115. Default is to enable auto-compilation of source
  116. files.
  117. --listen[=P] Listen on a local port or a path for REPL clients.
  118. If P is not given, the default is local port 37146.
  119. -q inhibit loading of user init file
  120. --use-srfi=LS load SRFI modules for the SRFIs in LS,
  121. which is a list of numbers like \"2,13,14\"
  122. -h, --help display this help and exit
  123. -v, --version display version information and exit
  124. \\ read arguments from following script lines"))
  125. (define* (shell-usage name fatal? #:optional fmt . args)
  126. (let ((port (if fatal?
  127. (current-error-port)
  128. (current-output-port))))
  129. (if fmt
  130. (apply format port fmt args))
  131. (format port (_ "Usage: ~a [OPTION]... [FILE]...\n") name)
  132. (display *usage* port)
  133. (newline port)
  134. (emit-bug-reporting-address
  135. "GNU Guile" "bug-guile@gnu.org"
  136. #:port port
  137. #:url "http://www.gnu.org/software/guile/"
  138. #:packager (assq-ref %guile-build-info 'packager)
  139. #:packager-bug-address
  140. (assq-ref %guile-build-info 'packager-bug-address))
  141. (if fatal?
  142. (exit 1))))
  143. (define (eval-string str)
  144. (call-with-input-string
  145. str
  146. (lambda (port)
  147. (let lp ()
  148. (let ((exp (read port)))
  149. (if (not (eof-object? exp))
  150. (begin
  151. (eval exp (current-module))
  152. (lp))))))))
  153. (define* (compile-shell-switches args #:optional (usage-name "guile"))
  154. (let ((arg0 "guile")
  155. (do-script '())
  156. (entry-point #f)
  157. (user-load-path '())
  158. (user-extensions '())
  159. (interactive? #t)
  160. (inhibit-user-init? #f)
  161. (turn-on-debugging? #f)
  162. (turn-off-debugging? #f))
  163. (define (error fmt . args)
  164. (apply shell-usage usage-name #t fmt args))
  165. (define (parse args out)
  166. (cond
  167. ((null? args)
  168. (finish args out))
  169. (else
  170. (let ((arg (car args))
  171. (args (cdr args)))
  172. (cond
  173. ((not (string-prefix? "-" arg)) ; foo
  174. ;; If we specified the -ds option, do-script is the cdr of
  175. ;; an expression like (load #f). We replace the car (i.e.,
  176. ;; the #f) with the script name.
  177. (set! arg0 arg)
  178. (set! interactive? #f)
  179. (if (pair? do-script)
  180. (begin
  181. (set-car! do-script arg0)
  182. (finish args out))
  183. (finish args (cons `(load ,arg0) out))))
  184. ((string=? arg "-s") ; foo
  185. (if (null? args)
  186. (error "missing argument to `-s' switch"))
  187. (set! arg0 (car args))
  188. (set! interactive? #f)
  189. (if (pair? do-script)
  190. (begin
  191. (set-car! do-script arg0)
  192. (finish (cdr args) out))
  193. (finish (cdr args) (cons `(load ,arg0) out))))
  194. ((string=? arg "-c") ; evaluate expr
  195. (if (null? args)
  196. (error "missing argument to `-c' switch"))
  197. (set! interactive? #f)
  198. (finish (cdr args)
  199. ;; Use our own eval-string to avoid loading (ice-9
  200. ;; eval-string), which loads the compiler.
  201. (cons `((@@ (ice-9 command-line) eval-string) ,(car args))
  202. out)))
  203. ((string=? arg "--") ; end args go interactive
  204. (finish args out))
  205. ((string=? arg "-l") ; load a file
  206. (if (null? args)
  207. (error "missing argument to `-l' switch"))
  208. (parse (cdr args)
  209. (cons `(load ,(car args)) out)))
  210. ((string=? arg "-L") ; add to %load-path
  211. (if (null? args)
  212. (error "missing argument to `-L' switch"))
  213. (set! user-load-path (cons (car args) user-load-path))
  214. (parse (cdr args)
  215. out))
  216. ((string=? arg "-x") ; add to %load-extensions
  217. (if (null? args)
  218. (error "missing argument to `-x' switch"))
  219. (set! user-extensions (cons (car args) user-extensions))
  220. (parse (cdr args)
  221. out))
  222. ((string=? arg "-e") ; entry point
  223. (if (null? args)
  224. (error "missing argument to `-e' switch"))
  225. (let* ((port (open-input-string (car args)))
  226. (arg1 (read port))
  227. (arg2 (read port)))
  228. ;; Recognize syntax of certain versions of guile 1.4 and
  229. ;; transform to (@ MODULE-NAME FUNC).
  230. (set! entry-point
  231. (cond
  232. ((not (eof-object? arg2))
  233. `(@ ,arg1 ,arg2))
  234. ((and (pair? arg1)
  235. (not (memq (car arg1) '(@ @@)))
  236. (and-map symbol? arg1))
  237. `(@ ,arg1 main))
  238. (else
  239. arg1))))
  240. (parse (cdr args)
  241. out))
  242. ((string=? arg "-ds") ; do script here
  243. ;; We put a dummy "load" expression, and let the -s put the
  244. ;; filename in.
  245. (if (pair? do-script)
  246. (error "the -ds switch may only be specified once")
  247. (set! do-script (list #f)))
  248. (parse args
  249. (cons `(load . ,do-script) out)))
  250. ((string=? arg "--debug")
  251. (set! turn-on-debugging? #t)
  252. (set! turn-off-debugging? #f)
  253. (parse args out))
  254. ((string=? arg "--no-debug")
  255. (set! turn-off-debugging? #t)
  256. (set! turn-on-debugging? #f)
  257. (parse args out))
  258. ;; Do auto-compile on/off now, because the form itself might
  259. ;; need this decision.
  260. ((string=? arg "--auto-compile")
  261. (set! %load-should-auto-compile #t)
  262. (parse args out))
  263. ((string=? arg "--fresh-auto-compile")
  264. (set! %load-should-auto-compile #t)
  265. (set! %fresh-auto-compile #t)
  266. (parse args out))
  267. ((string=? arg "--no-auto-compile")
  268. (set! %load-should-auto-compile #f)
  269. (parse args out))
  270. ((string=? arg "-q") ; don't load user init
  271. (set! inhibit-user-init? #t)
  272. (parse args out))
  273. ((string-prefix? "--use-srfi=" arg)
  274. (let ((srfis (map (lambda (x)
  275. (let ((n (string->number x)))
  276. (if (and n (exact? n) (integer? n) (>= n 0))
  277. n
  278. (error "invalid SRFI specification"))))
  279. (string-split (substring arg 11) #\,))))
  280. (if (null? srfis)
  281. (error "invalid SRFI specification"))
  282. (parse args
  283. (cons `(use-srfis ',srfis) out))))
  284. ((string=? arg "--listen") ; start a repl server
  285. (parse args
  286. (cons '((@@ (system repl server) spawn-server)) out)))
  287. ((string-prefix? "--listen=" arg) ; start a repl server
  288. (parse
  289. args
  290. (cons
  291. (let ((where (substring arg 9)))
  292. (cond
  293. ((string->number where) ; --listen=PORT
  294. => (lambda (port)
  295. (if (and (integer? port) (exact? port) (>= port 0))
  296. `((@@ (system repl server) spawn-server)
  297. ((@@ (system repl server) make-tcp-server-socket) #:port ,port))
  298. (error "invalid port for --listen"))))
  299. ((string-prefix? "/" where) ; --listen=/PATH/TO/SOCKET
  300. `((@@ (system repl server) spawn-server)
  301. ((@@ (system repl server) make-unix-domain-server-socket) #:path ,where)))
  302. (else
  303. (error "unknown argument to --listen"))))
  304. out)))
  305. ((or (string=? arg "-h") (string=? arg "--help"))
  306. (shell-usage usage-name #f)
  307. (exit 0))
  308. ((or (string=? arg "-v") (string=? arg "--version"))
  309. (version-etc "GNU Guile" (version)
  310. #:license *LGPLv3+*
  311. #:command-name "guile"
  312. #:packager (assq-ref %guile-build-info 'packager)
  313. #:packager-version
  314. (assq-ref %guile-build-info 'packager-version))
  315. (exit 0))
  316. (else
  317. (error "Unrecognized switch ~a" arg)))))))
  318. (define (finish args out)
  319. ;; Check to make sure the -ds got a -s.
  320. (if (and (pair? do-script) (not (car do-script)))
  321. (error "the `-ds' switch requires the use of `-s' as well"))
  322. ;; Make any remaining arguments available to the
  323. ;; script/command/whatever.
  324. (set-program-arguments (cons arg0 args))
  325. ;; If debugging was requested, or we are interactive and debugging
  326. ;; was not explicitly turned off, use the debug engine.
  327. (if (or turn-on-debugging?
  328. (and interactive? (not turn-off-debugging?)))
  329. (begin
  330. (set-default-vm-engine! 'debug)
  331. (set-vm-engine! (the-vm) 'debug)))
  332. ;; Return this value.
  333. `(;; It would be nice not to load up (ice-9 control), but the
  334. ;; default-prompt-handler is nontrivial.
  335. (@ (ice-9 control) %)
  336. (begin
  337. ;; If we didn't end with a -c or a -s and didn't supply a -q, load
  338. ;; the user's customization file.
  339. ,@(if (and interactive? (not inhibit-user-init?))
  340. '((load-user-init))
  341. '())
  342. ;; Use-specified extensions.
  343. ,@(map (lambda (ext)
  344. `(set! %load-extensions (cons ,ext %load-extensions)))
  345. user-extensions)
  346. ;; Add the user-specified load path here, so it won't be in
  347. ;; effect during the loading of the user's customization file.
  348. ,@(map (lambda (path)
  349. `(set! %load-path (cons ,path %load-path)))
  350. user-load-path)
  351. ;; Put accumulated actions in their correct order.
  352. ,@(reverse! out)
  353. ;; Handle the `-e' switch, if it was specified.
  354. ,@(if entry-point
  355. `((,entry-point (command-line)))
  356. '())
  357. ,(if interactive?
  358. ;; If we didn't end with a -c or a -s, start the
  359. ;; repl.
  360. '((@ (ice-9 top-repl) top-repl))
  361. ;; Otherwise, after doing all the other actions
  362. ;; prescribed by the command line, quit.
  363. '(quit)))))
  364. (if (pair? args)
  365. (begin
  366. (set! arg0 (car args))
  367. (let ((slash (string-rindex arg0 #\/)))
  368. (set! usage-name
  369. (if slash (substring arg0 (1+ slash)) arg0)))
  370. (parse (cdr args) '()))
  371. (parse args '()))))