command-line.scm 19 KB

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