command-line.scm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. ;;; Parsing Guile's command-line
  2. ;;; Copyright (C) 1994-1998, 2000-2018 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!)
  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 2018)
  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 code with Guile, interactively or from a script.
  96. [-s] FILE load source code from FILE, and exit
  97. -c EXPR evalute 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. -C DIRECTORY like -L, but for compiled files
  104. -x EXTENSION add EXTENSION to the front of the load extensions
  105. -l FILE load source code from FILE
  106. -e FUNCTION after reading script, apply FUNCTION to
  107. command line arguments
  108. --language=LANG change language; default: scheme
  109. -ds do -s script at this point
  110. --debug start with the \"debugging\" VM engine
  111. --no-debug start with the normal VM engine (backtraces but
  112. no breakpoints); default is --debug for interactive
  113. use, but not for `-s' and `-c'.
  114. --auto-compile compile source files automatically
  115. --fresh-auto-compile invalidate auto-compilation cache
  116. --no-auto-compile disable automatic source file compilation;
  117. default is to enable auto-compilation of source
  118. files.
  119. --listen[=P] listen on a local port or a path for REPL clients;
  120. if P is not given, the default is local port 37146
  121. -q inhibit loading of user init file
  122. --use-srfi=LS load SRFI modules for the SRFIs in LS,
  123. which is a list of numbers like \"2,13,14\"
  124. -h, --help display this help and exit
  125. -v, --version display version information and exit
  126. \\ read arguments from following script lines"))
  127. (define* (shell-usage name fatal? #:optional fmt . args)
  128. (let ((port (if fatal?
  129. (current-error-port)
  130. (current-output-port))))
  131. (when fmt
  132. (apply format port fmt args)
  133. (newline port))
  134. (format port (_ "Usage: ~a [OPTION]... [FILE]...\n") name)
  135. (display *usage* port)
  136. (newline port)
  137. (emit-bug-reporting-address
  138. "GNU Guile" "bug-guile@gnu.org"
  139. #:port port
  140. #:url "http://www.gnu.org/software/guile/"
  141. #:packager (assq-ref %guile-build-info 'packager)
  142. #:packager-bug-address
  143. (assq-ref %guile-build-info 'packager-bug-address))
  144. (if fatal?
  145. (exit 1))))
  146. ;; Try to avoid loading (ice-9 eval-string) and (system base compile) if
  147. ;; possible.
  148. (define (eval-string/lang str)
  149. (case (current-language)
  150. ((scheme)
  151. (call-with-input-string
  152. str
  153. (lambda (port)
  154. (let lp ()
  155. (let ((exp (read port)))
  156. (if (not (eof-object? exp))
  157. (begin
  158. (eval exp (current-module))
  159. (lp))))))))
  160. (else
  161. ((module-ref (resolve-module '(ice-9 eval-string)) 'eval-string) str))))
  162. (define (load/lang f)
  163. (case (current-language)
  164. ((scheme)
  165. (load-in-vicinity (getcwd) f))
  166. (else
  167. ((module-ref (resolve-module '(system base compile)) 'compile-file)
  168. f #:to 'value))))
  169. (define* (compile-shell-switches args #:optional (usage-name "guile"))
  170. (let ((arg0 "guile")
  171. (script-cell #f)
  172. (entry-point #f)
  173. (user-load-path '())
  174. (user-load-compiled-path '())
  175. (user-extensions '())
  176. (interactive? #t)
  177. (inhibit-user-init? #f)
  178. (turn-on-debugging? #f)
  179. (turn-off-debugging? #f))
  180. (define (error fmt . args)
  181. (apply shell-usage usage-name #t
  182. (string-append "error: " fmt "~%") args))
  183. (define (parse args out)
  184. (cond
  185. ((null? args)
  186. (finish args out))
  187. (else
  188. (let ((arg (car args))
  189. (args (cdr args)))
  190. (cond
  191. ((not (string-prefix? "-" arg)) ; foo
  192. ;; If we specified the -ds option, script-cell is the cdr of
  193. ;; an expression like (load #f). We replace the car (i.e.,
  194. ;; the #f) with the script name.
  195. (set! arg0 arg)
  196. (set! interactive? #f)
  197. (if script-cell
  198. (begin
  199. (set-car! script-cell arg0)
  200. (finish args out))
  201. (finish args
  202. (cons `((@@ (ice-9 command-line) load/lang) ,arg0)
  203. out))))
  204. ((string=? arg "-s") ; foo
  205. (if (null? args)
  206. (error "missing argument to `-s' switch"))
  207. (set! arg0 (car args))
  208. (set! interactive? #f)
  209. (if script-cell
  210. (begin
  211. (set-car! script-cell arg0)
  212. (finish (cdr args) out))
  213. (finish (cdr args)
  214. (cons `((@@ (ice-9 command-line) load/lang) ,arg0)
  215. out))))
  216. ((string=? arg "-c") ; evaluate expr
  217. (if (null? args)
  218. (error "missing argument to `-c' switch"))
  219. (set! interactive? #f)
  220. (finish (cdr args)
  221. (cons `((@@ (ice-9 command-line) eval-string/lang)
  222. ,(car args))
  223. out)))
  224. ((string=? arg "--") ; end args go interactive
  225. (finish args out))
  226. ((string=? arg "-l") ; load a file
  227. (if (null? args)
  228. (error "missing argument to `-l' switch"))
  229. (parse (cdr args)
  230. (cons `((@@ (ice-9 command-line) load/lang) ,(car args))
  231. out)))
  232. ((string=? arg "-L") ; add to %load-path
  233. (if (null? args)
  234. (error "missing argument to `-L' switch"))
  235. (set! user-load-path (cons (car args) user-load-path))
  236. (parse (cdr args)
  237. out))
  238. ((string=? arg "-C") ; add to %load-compiled-path
  239. (if (null? args)
  240. (error "missing argument to `-C' switch"))
  241. (set! user-load-compiled-path
  242. (cons (car args) user-load-compiled-path))
  243. (parse (cdr args)
  244. out))
  245. ((string=? arg "-x") ; add to %load-extensions
  246. (if (null? args)
  247. (error "missing argument to `-x' switch"))
  248. (set! user-extensions (cons (car args) user-extensions))
  249. (parse (cdr args)
  250. out))
  251. ((string=? arg "-e") ; entry point
  252. (if (null? args)
  253. (error "missing argument to `-e' switch"))
  254. (let* ((port (open-input-string (car args)))
  255. (arg1 (read port))
  256. (arg2 (read port)))
  257. ;; Recognize syntax of certain versions of guile 1.4 and
  258. ;; transform to (@ MODULE-NAME FUNC).
  259. (set! entry-point
  260. (cond
  261. ((not (eof-object? arg2))
  262. `(@ ,arg1 ,arg2))
  263. ((and (pair? arg1)
  264. (not (memq (car arg1) '(@ @@)))
  265. (and-map symbol? arg1))
  266. `(@ ,arg1 main))
  267. (else
  268. arg1))))
  269. (parse (cdr args)
  270. out))
  271. ((string-prefix? "--language=" arg) ; language
  272. (parse args
  273. (cons `(current-language
  274. ',(string->symbol
  275. (substring arg (string-length "--language="))))
  276. out)))
  277. ((string=? "--language" arg) ; language
  278. (when (null? args)
  279. (error "missing argument to `--language' option"))
  280. (parse (cdr args)
  281. (cons `(current-language ',(string->symbol (car args)))
  282. out)))
  283. ((string=? arg "-ds") ; do script here
  284. ;; We put a dummy "load" expression, and let the -s put the
  285. ;; filename in.
  286. (when script-cell
  287. (error "the -ds switch may only be specified once"))
  288. (set! script-cell (list #f))
  289. (parse args
  290. (acons '(@@ (ice-9 command-line) load/lang)
  291. script-cell
  292. out)))
  293. ((string=? arg "--debug")
  294. (set! turn-on-debugging? #t)
  295. (set! turn-off-debugging? #f)
  296. (parse args out))
  297. ((string=? arg "--no-debug")
  298. (set! turn-off-debugging? #t)
  299. (set! turn-on-debugging? #f)
  300. (parse args out))
  301. ;; Do auto-compile on/off now, because the form itself might
  302. ;; need this decision.
  303. ((string=? arg "--auto-compile")
  304. (set! %load-should-auto-compile #t)
  305. (parse args out))
  306. ((string=? arg "--fresh-auto-compile")
  307. (set! %load-should-auto-compile #t)
  308. (set! %fresh-auto-compile #t)
  309. (parse args out))
  310. ((string=? arg "--no-auto-compile")
  311. (set! %load-should-auto-compile #f)
  312. (parse args out))
  313. ((string=? arg "-q") ; don't load user init
  314. (set! inhibit-user-init? #t)
  315. (parse args out))
  316. ((string-prefix? "--use-srfi=" arg)
  317. (let ((srfis (map (lambda (x)
  318. (let ((n (string->number x)))
  319. (if (and n (exact? n) (integer? n) (>= n 0))
  320. n
  321. (error "invalid SRFI specification"))))
  322. (string-split (substring arg 11) #\,))))
  323. (if (null? srfis)
  324. (error "invalid SRFI specification"))
  325. (parse args
  326. (cons `(use-srfis ',srfis) out))))
  327. ((string=? arg "--listen") ; start a repl server
  328. (parse args
  329. (cons '((@@ (system repl server) spawn-server)) out)))
  330. ((string-prefix? "--listen=" arg) ; start a repl server
  331. (parse
  332. args
  333. (cons
  334. (let ((where (substring arg 9)))
  335. (cond
  336. ((string->number where) ; --listen=PORT
  337. => (lambda (port)
  338. (if (and (integer? port) (exact? port) (>= port 0))
  339. `((@@ (system repl server) spawn-server)
  340. ((@@ (system repl server) make-tcp-server-socket) #:port ,port))
  341. (error "invalid port for --listen"))))
  342. ((string-prefix? "/" where) ; --listen=/PATH/TO/SOCKET
  343. `((@@ (system repl server) spawn-server)
  344. ((@@ (system repl server) make-unix-domain-server-socket) #:path ,where)))
  345. (else
  346. (error "unknown argument to --listen"))))
  347. out)))
  348. ((or (string=? arg "-h") (string=? arg "--help"))
  349. (shell-usage usage-name #f)
  350. (exit 0))
  351. ((or (string=? arg "-v") (string=? arg "--version"))
  352. (version-etc "GNU Guile" (version)
  353. #:license *LGPLv3+*
  354. #:command-name "guile"
  355. #:packager (assq-ref %guile-build-info 'packager)
  356. #:packager-version
  357. (assq-ref %guile-build-info 'packager-version))
  358. (exit 0))
  359. (else
  360. (error "unrecognized switch ~a" arg)))))))
  361. (define (finish args out)
  362. ;; Check to make sure the -ds got a -s.
  363. (when (and script-cell (not (car script-cell)))
  364. (error "the `-ds' switch requires the use of `-s' as well"))
  365. ;; Make any remaining arguments available to the
  366. ;; script/command/whatever.
  367. (set-program-arguments (cons arg0 args))
  368. ;; If debugging was requested, or we are interactive and debugging
  369. ;; was not explicitly turned off, use the debug engine.
  370. (if (or turn-on-debugging?
  371. (and interactive? (not turn-off-debugging?)))
  372. (begin
  373. (set-default-vm-engine! 'debug)
  374. (set-vm-engine! 'debug)))
  375. ;; Return this value.
  376. `(;; It would be nice not to load up (ice-9 control), but the
  377. ;; default-prompt-handler is nontrivial.
  378. (@ (ice-9 control) %)
  379. (begin
  380. ;; If we didn't end with a -c or a -s and didn't supply a -q, load
  381. ;; the user's customization file.
  382. ,@(if (and interactive? (not inhibit-user-init?))
  383. '((load-user-init))
  384. '())
  385. ;; Use-specified extensions.
  386. ,@(map (lambda (ext)
  387. `(set! %load-extensions (cons ,ext %load-extensions)))
  388. user-extensions)
  389. ;; Add the user-specified load paths here, so they won't be in
  390. ;; effect during the loading of the user's customization file.
  391. ,@(map (lambda (path)
  392. `(set! %load-path (cons ,path %load-path)))
  393. user-load-path)
  394. ,@(map (lambda (path)
  395. `(set! %load-compiled-path
  396. (cons ,path %load-compiled-path)))
  397. user-load-compiled-path)
  398. ;; Put accumulated actions in their correct order.
  399. ,@(reverse! out)
  400. ;; Handle the `-e' switch, if it was specified.
  401. ,@(if entry-point
  402. `((,entry-point (command-line)))
  403. '())
  404. ,(if interactive?
  405. ;; If we didn't end with a -c or a -s, start the
  406. ;; repl.
  407. '((@ (ice-9 top-repl) top-repl))
  408. ;; Otherwise, after doing all the other actions
  409. ;; prescribed by the command line, quit.
  410. '(quit)))))
  411. (if (pair? args)
  412. (begin
  413. (set! arg0 (car args))
  414. (let ((slash (string-rindex arg0 #\/)))
  415. (set! usage-name
  416. (if slash (substring arg0 (1+ slash)) arg0)))
  417. (parse (cdr args) '()))
  418. (parse args '()))))