command-line.scm 19 KB

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