cs: make "main.sps" work in script or boot mode

Now that Chez Scheme supports libraries in boot files, make
"racket.so" work when it is converted to a boot file. Loading it as a
boot file can save around 20ms on every major GC, since the
"racket.so" code will be in the static generation.

For now, however, `racketcs` is not set up to use "racket.so" in boot
form.
This commit is contained in:
Matthew Flatt 2018-07-17 08:26:14 -06:00
parent d67cec6892
commit e1c2e5d4f4

View File

@ -40,475 +40,490 @@
(unless omit-debugging?
(compile-keep-source-locations! #t))
(define-syntax seq (syntax-rules () [(_ expr ...) (define dummy (let () expr ... (void)))]))
(define (run the-command-line-arguments)
(seq
(unless (>= (length the-command-line-arguments) 5)
(error 'racket "expected `self`, `collects`, and `libs` paths plus `segment-offset` and `is-gui?` to start"))
(set-exec-file! (path->complete-path (car the-command-line-arguments))))
(define init-collects-dir (let ([s (cadr the-command-line-arguments)])
(if (equal? s "") 'disable (string->path s))))
(define init-config-dir (string->path (or (getenv "PLTCONFIGDIR")
(caddr the-command-line-arguments))))
(define segment-offset (#%string->number (list-ref the-command-line-arguments 3)))
(define gracket? (string=? "true" (list-ref the-command-line-arguments 4)))
(seq
(when (foreign-entry? "racket_exit")
(#%exit-handler (foreign-procedure "racket_exit" (int) void))))
(seq
(|#%app| use-compiled-file-paths
(list (string->path (string-append "compiled/"
(cond
[(getenv "PLT_ZO_PATH")
=> (lambda (s)
(unless (and (not (equal? s ""))
(relative-path? s))
(error 'racket "PLT_ZO_PATH environment variable is not a valid path"))
s)]
[platform-independent-zo-mode? "cs"]
[else (symbol->string (machine-type))]))))))
(define (see saw . args)
(let loop ([saw saw] [args args])
(if (null? args)
saw
(loop (hash-set saw (car args) #t) (cdr args)))))
(define (saw? saw tag)
(hash-ref saw tag #f))
(define rx:logging-spec (pregexp "^[\\s]*(none|fatal|error|warning|info|debug)(?:@([^\\s @]+))?(.*)$"))
(define rx:all-whitespace (pregexp "^[\\s]*$"))
(define (parse-logging-spec which str where exit-on-fail?)
(define (fail)
(let ([msg (string-append
which " <levels> " where " must be one of the following\n"
" <level>s:\n"
" none fatal error warning info debug\n"
"or up to one such <level> in whitespace-separated sequence of\n"
" <level>@<name>\n"
"given: " str)])
(cond
[exit-on-fail?
(raise-user-error 'racket msg)]
[else
(eprintf "~a\n" msg)])))
(let loop ([str str] [default #f])
(let ([m (regexp-match rx:logging-spec str)])
(cond
[m
(let ([level (string->symbol (cadr m))]
[topic (caddr m)])
(cond
[topic
(cons level (cons (string->symbol topic) (loop (cadddr m) default)))]
[default (fail)]
[else (loop (cadddr m) level)]))]
[(regexp-match? rx:all-whitespace str)
(if default (list default) null)]
[else (fail)]))))
(define (configure-runtime m)
;; New-style configuration through a `configure-runtime` submodule:
(let ([config-m (module-path-index-join '(submod "." configure-runtime) m)])
(when (module-declared? config-m #t)
(dynamic-require config-m #f)))
;; Old-style configuration with module language info:
(let ([info (module->language-info m #t)])
(when (and (vector? info) (= 3 (vector-length info)))
(let* ([info-load (lambda (info)
((dynamic-require (vector-ref info 0) (vector-ref info 1)) (vector-ref info 2)))]
[get (info-load info)]
[infos (get 'configure-runtime '())])
(unless (and (list? infos)
(andmap (lambda (info) (and (vector? info) (= 3 (vector-length info))))
infos))
(raise-argument-error 'runtime-configure "(listof (vector any any any))" infos))
(for-each info-load infos)))))
(define need-runtime-configure? #t)
(define (namespace-require+ mod)
(let ([m (module-path-index-join mod #f)])
(when need-runtime-configure?
(configure-runtime m)
(set! need-runtime-configure? #f))
(namespace-require m)
;; Run `main` submodule, if any:
(let ([main-m (module-path-index-join '(submod "." main) m)])
(when (module-declared? main-m #t)
(dynamic-require main-m #f)))))
(define (get-repl-init-filename)
(call-with-continuation-prompt
(lambda ()
(or (let ([p (build-path (find-system-path 'addon-dir)
(if gracket?
"gui-interactive.rkt"
"interactive.rkt"))])
(and (file-exists? p) p))
(hash-ref (call-with-input-file
(build-path (find-main-config) "config.rktd")
read)
(if gracket? 'gui-interactive-file 'interactive-file)
#f)
(if gracket? 'racket/interactive 'racket/gui/interactive)))
(default-continuation-prompt-tag)
(lambda args #f)))
(define init-library (if gracket?
'(lib "racket/gui/init")
'(lib "racket/init")))
(define loads '())
(define repl? #f)
(define repl-init? #t)
(define version? #f)
(define stderr-logging-arg #f)
(define stdout-logging-arg #f)
(define runtime-for-init? #t)
(define exit-value 0)
(define host-collects-dir init-collects-dir)
(define host-config-dir init-config-dir)
(define (no-init! saw)
(unless (saw? saw 'top)
(set! init-library #f)))
(define (next-arg what flag within-flag args)
(let loop ([args (cdr args)] [accum '()])
(cond
[(null? args)
(error 'racket "missing ~a after ~a switch" what (or within-flag flag))]
[(pair? (car args))
(loop (cdr args) (cons (car args) accum))]
[else
(values (car args) (append (reverse accum) (cdr args)))])))
(define (check-path-arg what flag within-flag)
(when (equal? what "")
(error 'racket "empty ~a after ~a switch" what (or within-flag flag))))
(define-syntax string-case
;; Assumes that `arg` is a variable
(syntax-rules ()
[(_ arg [else body ...])
(let () body ...)]
[(_ arg [(str ...) body ...] rest ...)
(if (or (string=? arg str) ...)
(let () body ...)
(string-case arg rest ...))]))
(seq
(let flags-loop ([args (list-tail the-command-line-arguments 5)]
[saw (hasheq)])
;; An element of `args` can become `(cons _arg _within-arg)`
;; due to splitting multiple flags with a single "-"
(define (loop args) (flags-loop args saw))
;; Called to handle remaining non-switch arguments:
(define (finish args saw)
(cond
[(and (pair? args)
(not (saw? saw 'non-config)))
(loop (cons "-u" args))]
[else
(|#%app| current-command-line-arguments (list->vector args))
(when (and (null? args) (not (saw? saw 'non-config)))
(set! repl? #t)
(unless gracket?
(set! version? #t)))]))
;; Dispatch on first argument:
(if (null? args)
(finish args saw)
(let* ([arg (car args)]
[within-arg (and (pair? arg) (cdr arg))]
[arg (if (pair? arg) (car arg) arg)])
(string-case
arg
[("-l" "--lib")
(let-values ([(lib-name rest-args) (next-arg "library name" arg within-arg args)])
(set! loads
(cons
(lambda ()
(namespace-require+ `(lib ,lib-name)))
loads))
(no-init! saw)
(flags-loop rest-args (see saw 'non-config 'lib)))]
[("-t" "--require")
(let-values ([(file-name rest-args) (next-arg "file name" arg within-arg args)])
(set! loads
(cons
(lambda ()
(namespace-require+ `(file ,file-name)))
loads))
(no-init! saw)
(flags-loop rest-args (see saw 'non-config 'lib)))]
[("-u" "--script")
(let-values ([(file-name rest-args) (next-arg "file name" arg within-arg args)])
(set! loads
(cons
(lambda ()
(namespace-require+ `(file ,file-name)))
loads))
(no-init! saw)
(flags-loop rest-args (see saw 'non-config 'lib)))]
[("-f" "--load")
(let-values ([(file-name rest-args) (next-arg "file name" arg within-arg args)])
(set! loads
(cons
(lambda ()
(load file-name))
loads))
(flags-loop rest-args (see saw 'non-config)))]
[("-e" "--eval")
(let-values ([(expr rest-args) (next-arg "expression" arg within-arg args)])
(set! loads
(cons
(lambda ()
(eval (read (open-input-string expr))))
loads))
(flags-loop rest-args (see saw 'non-config)))]
[("-i" "--repl")
(set! repl? #t)
(set! version? #t)
(flags-loop (cdr args) (see saw 'non-config 'top))]
[("-n" "--no-lib")
(set! init-library #f)
(flags-loop (cdr args) (see saw 'non-config))]
[("-v" "--version")
(set! version? #t)
(flags-loop (cddr args) (see saw 'non-config))]
[("-c" "--no-compiled")
(|#%app| use-compiled-file-paths '())
(loop (cdr args))]
[("-I")
(let-values ([(lib-name rest-args) (next-arg "library name" arg within-arg args)])
(when init-library
(set! init-library `(lib ,lib-name)))
(loop rest-args))]
[("-X" "--collects")
(let-values ([(collects-path rest-args) (next-arg "collects path" arg within-arg args)])
(cond
[(equal? collects-path "")
(set! init-collects-dir 'disable)]
[else
(check-path-arg "collects path" arg within-arg)
(set! init-collects-dir (path->complete-path (string->path collects-path)))])
(loop rest-args))]
[("-G" "--config")
(let-values ([(config-path rest-args) (next-arg "config path" arg within-arg args)])
(check-path-arg "config path" arg within-arg)
(set! init-config-dir (path->complete-path (string->path config-path)))
(loop rest-args))]
[("-C" "--cross")
(set! host-config-dir init-config-dir)
(set! host-collects-dir init-collects-dir)
(loop (cdr args))]
[("-U" "--no-user-path")
(|#%app| use-user-specific-search-paths #f)
(loop (cdr args))]
[("-d")
(|#%app| load-on-demand-enabled #f)
(loop (cdr args))]
[("-q" "--no-init-file")
(set! repl-init? #f)
(loop (cdr args))]
[("-W" "--stderr")
(let-values ([(spec rest-args) (next-arg "stderr level" arg within-arg args)])
(set! stderr-logging-arg (parse-logging-spec "stderr" spec (format "after ~a switch" (or within-arg arg)) #t))
(loop rest-args))]
[("-O" "--stdout")
(let-values ([(spec rest-args) (next-arg "stdout level" arg within-arg args)])
(set! stdout-logging-arg (parse-logging-spec "stdout" spec (format "after ~a switch" (or within-arg arg)) #t))
(loop rest-args))]
[("-N" "--name")
(let-values ([(name rest-args) (next-arg "name" arg within-arg args)])
(set-run-file! (string->path name))
(loop rest-args))]
[("--")
(cond
[(or (null? (cdr args)) (not (pair? (cadr args))))
(finish (cdr args) saw)]
[else
;; Need to handle more switches from a combined flag
(loop (cons (cadr args) (cons (car args) (cddr args))))])]
[else
(cond
[(eqv? (string-ref arg 0) #\-)
(cond
[(and (> (string-length arg) 2)
(not (eqv? (string-ref arg 1) #\-)))
;; Split flags
(loop (append (map (lambda (c) (cons (string #\- c) arg))
(cdr (string->list arg)))
(cdr args)))]
[else
(raise-user-error 'racket "bad switch: ~a~a"
arg
(if within-arg
(format " within: ~a" within-arg)
""))])]
[else
;; Non-flag argument
(finish args saw)])])))))
;; Set up GC logging
(define-values (struct:gc-info make-gc-info gc-info? gc-info-ref gc-info-set!)
(make-struct-type 'gc-info #f 10 0 #f null 'prefab #f '(0 1 2 3 4 5 6 7 8 9)))
(define (K plus n)
(let* ([s (number->string (quotient (abs n) 1000))]
[len (string-length s)]
[len2 (+ len
(quotient (sub1 len) 3)
(if (or (< n 0)
(not (eq? "" plus)))
1
0)
1)]
[s2 (make-string len2)])
(string-set! s2 (sub1 len2) #\K)
(let loop ([i len]
[j (sub1 len2)]
[digits 0])
(cond
[(zero? i)
(cond
[(< n 0) (string-set! s2 0 #\-)]
[(not (eq? plus "")) (string-set! s2 0 (string-ref plus 0))])
s2]
[(= 3 digits)
(let ([j (sub1 j)])
(string-set! s2 j #\,)
(loop i j 0))]
[else
(let ([i (sub1 i)]
[j (sub1 j)])
(string-set! s2 j (string-ref s i))
(loop i j (add1 digits)))]))))
(define minor-gcs 0)
(define major-gcs 0)
(define auto-gcs 0)
(define peak-mem 0)
(seq
(set-garbage-collect-notify!
(let ([root-logger (|#%app| current-logger)])
;; This function can be called in any Chez Scheme thread
(lambda (gen pre-allocated pre-allocated+overhead pre-time pre-cpu-time
post-allocated post-allocated+overhead post-time post-cpu-time)
(let ([minor? (< gen (collect-maximum-generation))])
(if minor?
(set! minor-gcs (add1 minor-gcs))
(set! major-gcs (add1 major-gcs)))
(set! peak-mem (max peak-mem pre-allocated))
(let ([debug-GC? (log-level? root-logger 'debug 'GC)])
(when (or debug-GC?
(and (not minor?)
(log-level? root-logger 'debug 'GC:major)))
(let ([delta (- pre-allocated post-allocated)])
(log-message root-logger 'debug (if debug-GC? 'GC 'GC:major)
(chez:format "GC: 0:~a~a @ ~a(~a); free ~a(~a) ~ams @ ~a"
(if minor? "min" "MAJ") gen
(K "" pre-allocated) (K "+" (- pre-allocated+overhead pre-allocated))
(K "" delta) (K "+" (- (- pre-allocated+overhead post-allocated+overhead)
delta))
(- post-cpu-time pre-cpu-time) pre-cpu-time)
(make-gc-info (if minor? 'minor 'major) pre-allocated pre-allocated+overhead 0
post-allocated post-allocated+overhead
pre-cpu-time post-cpu-time
pre-time post-time)
#f)))))))))
(seq
(|#%app| exit-handler
(let ([orig (|#%app| exit-handler)]
[root-logger (|#%app| current-logger)])
(lambda (v)
(when (log-level? root-logger 'info 'GC)
(log-message root-logger 'info 'GC
(chez:format "0:atexit peak ~a; alloc ~a; major ~a; minor ~a; ~ams"
(K "" peak-mem)
(K "" (- (+ (bytes-deallocated) (bytes-allocated)) (initial-bytes-allocated)))
major-gcs
minor-gcs
(let ([t (sstats-gc-cpu (statistics))])
(+ (* (time-second t) 1000)
(quotient (time-nanosecond t) 1000000))))
#f))
(linklet-performance-report!)
(|#%app| orig v)))))
(define stderr-logging
(or stderr-logging-arg
(let ([spec (getenv "PLTSTDERR")])
(if spec
(parse-logging-spec "stderr" spec "in PLTSTDERR environment variable" #f)
'(error)))))
(define stdout-logging
(or stdout-logging-arg
(let ([spec (getenv "PLTSTDOUT")])
(if spec
(parse-logging-spec "stdout" spec "in PLTSTDOUT environment variable" #f)
'()))))
(when (getenv "PLT_STATS_ON_BREAK")
(keyboard-interrupt-handler
(let ([orig (keyboard-interrupt-handler)])
(lambda args
(dump-memory-stats)
(apply orig args)))))
(when version?
(printf "Welcome to Racket v~a [cs]\n" (version)))
(call-in-main-thread
(lambda ()
(boot)
(when (and stderr-logging
(not (null? stderr-logging)))
(apply add-stderr-log-receiver! (|#%app| current-logger) stderr-logging))
(when (and stdout-logging
(not (null? stdout-logging)))
(apply add-stdout-log-receiver! (|#%app| current-logger) stdout-logging))
(cond
[(eq? init-collects-dir 'disable)
(|#%app| use-collection-link-paths #f)
(set-collects-dir! (build-path 'same))]
[else
(set-collects-dir! init-collects-dir)])
(set-config-dir! init-config-dir)
(unless (eq? init-collects-dir 'disable)
(|#%app| current-library-collection-links
(find-library-collection-links))
(|#%app| current-library-collection-paths
(find-library-collection-paths)))
(when init-library
(namespace-require+ init-library))
(call-with-continuation-prompt
(lambda ()
(for-each (lambda (ld) (ld))
(reverse loads)))
(default-continuation-prompt-tag)
;; If any load escapes, then set the exit value and
;; stop running loads (but maybe continue with the REPL)
(lambda (proc)
(set! exit-value 1)
;; Let the actual default handler report an arity mismatch, etc.
(call-with-continuation-prompt
(lambda () (abort-current-continuation (default-continuation-prompt-tag) proc)))))
(when repl?
(set! exit-value 0)
(when repl-init?
(let ([m (get-repl-init-filename)])
(when m
(call-with-continuation-prompt
(lambda () (dynamic-require m 0))
(default-continuation-prompt-tag)
(lambda args (set! exit-value 1))))))
(|#%app| (if gracket?
(dynamic-require 'racket/gui/init 'graphical-read-eval-print-loop)
(dynamic-require 'racket/base 'read-eval-print-loop)))
(unless gracket?
(newline)))
(|#%app| (|#%app| executable-yield-handler) exit-value)
(exit exit-value))))
(define the-command-line-arguments
(or (and (top-level-bound? 'bytes-command-line-arguments)
(map (lambda (s) (bytes->string/locale s #\?))
(top-level-value 'bytes-command-line-arguments)))
(command-line-arguments)))
(unless (>= (length the-command-line-arguments) 5)
(error 'racket "expected `self`, `collects`, and `libs` paths plus `segment-offset` and `is-gui?` to start"))
(set-exec-file! (path->complete-path (car the-command-line-arguments)))
(define init-collects-dir (let ([s (cadr the-command-line-arguments)])
(if (equal? s "") 'disable (string->path s))))
(define init-config-dir (string->path (or (getenv "PLTCONFIGDIR")
(caddr the-command-line-arguments))))
(define segment-offset (#%string->number (list-ref the-command-line-arguments 3)))
(define gracket? (string=? "true" (list-ref the-command-line-arguments 4)))
(when (foreign-entry? "racket_exit")
(#%exit-handler (foreign-procedure "racket_exit" (int) void)))
(|#%app| use-compiled-file-paths
(list (string->path (string-append "compiled/"
(cond
[(getenv "PLT_ZO_PATH")
=> (lambda (s)
(unless (and (not (equal? s ""))
(relative-path? s))
(error 'racket "PLT_ZO_PATH environment variable is not a valid path"))
s)]
[platform-independent-zo-mode? "cs"]
[else (symbol->string (machine-type))])))))
(define (see saw . args)
(let loop ([saw saw] [args args])
(if (null? args)
saw
(loop (hash-set saw (car args) #t) (cdr args)))))
(define (saw? saw tag)
(hash-ref saw tag #f))
(define rx:logging-spec (pregexp "^[\\s]*(none|fatal|error|warning|info|debug)(?:@([^\\s @]+))?(.*)$"))
(define rx:all-whitespace (pregexp "^[\\s]*$"))
(define (parse-logging-spec which str where exit-on-fail?)
(define (fail)
(let ([msg (string-append
which " <levels> " where " must be one of the following\n"
" <level>s:\n"
" none fatal error warning info debug\n"
"or up to one such <level> in whitespace-separated sequence of\n"
" <level>@<name>\n"
"given: " str)])
(cond
[exit-on-fail?
(raise-user-error 'racket msg)]
[else
(eprintf "~a\n" msg)])))
(let loop ([str str] [default #f])
(let ([m (regexp-match rx:logging-spec str)])
(cond
[m
(let ([level (string->symbol (cadr m))]
[topic (caddr m)])
(cond
[topic
(cons level (cons (string->symbol topic) (loop (cadddr m) default)))]
[default (fail)]
[else (loop (cadddr m) level)]))]
[(regexp-match? rx:all-whitespace str)
(if default (list default) null)]
[else (fail)]))))
(define (configure-runtime m)
;; New-style configuration through a `configure-runtime` submodule:
(let ([config-m (module-path-index-join '(submod "." configure-runtime) m)])
(when (module-declared? config-m #t)
(dynamic-require config-m #f)))
;; Old-style configuration with module language info:
(let ([info (module->language-info m #t)])
(when (and (vector? info) (= 3 (vector-length info)))
(let* ([info-load (lambda (info)
((dynamic-require (vector-ref info 0) (vector-ref info 1)) (vector-ref info 2)))]
[get (info-load info)]
[infos (get 'configure-runtime '())])
(unless (and (list? infos)
(andmap (lambda (info) (and (vector? info) (= 3 (vector-length info))))
infos))
(raise-argument-error 'runtime-configure "(listof (vector any any any))" infos))
(for-each info-load infos)))))
(define need-runtime-configure? #t)
(define (namespace-require+ mod)
(let ([m (module-path-index-join mod #f)])
(when need-runtime-configure?
(configure-runtime m)
(set! need-runtime-configure? #f))
(namespace-require m)
;; Run `main` submodule, if any:
(let ([main-m (module-path-index-join '(submod "." main) m)])
(when (module-declared? main-m #t)
(dynamic-require main-m #f)))))
(define (get-repl-init-filename)
(call-with-continuation-prompt
(lambda ()
(or (let ([p (build-path (find-system-path 'addon-dir)
(if gracket?
"gui-interactive.rkt"
"interactive.rkt"))])
(and (file-exists? p) p))
(hash-ref (call-with-input-file
(build-path (find-main-config) "config.rktd")
read)
(if gracket? 'gui-interactive-file 'interactive-file)
#f)
(if gracket? 'racket/interactive 'racket/gui/interactive)))
(default-continuation-prompt-tag)
(lambda args #f)))
(define init-library (if gracket?
'(lib "racket/gui/init")
'(lib "racket/init")))
(define loads '())
(define repl? #f)
(define repl-init? #t)
(define version? #f)
(define stderr-logging-arg #f)
(define stdout-logging-arg #f)
(define runtime-for-init? #t)
(define exit-value 0)
(define host-collects-dir init-collects-dir)
(define host-config-dir init-config-dir)
(define (no-init! saw)
(unless (saw? saw 'top)
(set! init-library #f)))
(define (next-arg what flag within-flag args)
(let loop ([args (cdr args)] [accum '()])
(cond
[(null? args)
(error 'racket "missing ~a after ~a switch" what (or within-flag flag))]
[(pair? (car args))
(loop (cdr args) (cons (car args) accum))]
[else
(values (car args) (append (reverse accum) (cdr args)))])))
(define (check-path-arg what flag within-flag)
(when (equal? what "")
(error 'racket "empty ~a after ~a switch" what (or within-flag flag))))
(define-syntax string-case
;; Assumes that `arg` is a variable
(syntax-rules ()
[(_ arg [else body ...])
(let () body ...)]
[(_ arg [(str ...) body ...] rest ...)
(if (or (string=? arg str) ...)
(let () body ...)
(string-case arg rest ...))]))
(let flags-loop ([args (list-tail the-command-line-arguments 5)]
[saw (hasheq)])
;; An element of `args` can become `(cons _arg _within-arg)`
;; due to splitting multiple flags with a single "-"
(define (loop args) (flags-loop args saw))
;; Called to handle remaining non-switch arguments:
(define (finish args saw)
(cond
[(and (pair? args)
(not (saw? saw 'non-config)))
(loop (cons "-u" args))]
[else
(|#%app| current-command-line-arguments (list->vector args))
(when (and (null? args) (not (saw? saw 'non-config)))
(set! repl? #t)
(unless gracket?
(set! version? #t)))]))
;; Dispatch on first argument:
(if (null? args)
(finish args saw)
(let* ([arg (car args)]
[within-arg (and (pair? arg) (cdr arg))]
[arg (if (pair? arg) (car arg) arg)])
(string-case
arg
[("-l" "--lib")
(let-values ([(lib-name rest-args) (next-arg "library name" arg within-arg args)])
(set! loads
(cons
(lambda ()
(namespace-require+ `(lib ,lib-name)))
loads))
(no-init! saw)
(flags-loop rest-args (see saw 'non-config 'lib)))]
[("-t" "--require")
(let-values ([(file-name rest-args) (next-arg "file name" arg within-arg args)])
(set! loads
(cons
(lambda ()
(namespace-require+ `(file ,file-name)))
loads))
(no-init! saw)
(flags-loop rest-args (see saw 'non-config 'lib)))]
[("-u" "--script")
(let-values ([(file-name rest-args) (next-arg "file name" arg within-arg args)])
(set! loads
(cons
(lambda ()
(namespace-require+ `(file ,file-name)))
loads))
(no-init! saw)
(flags-loop rest-args (see saw 'non-config 'lib)))]
[("-f" "--load")
(let-values ([(file-name rest-args) (next-arg "file name" arg within-arg args)])
(set! loads
(cons
(lambda ()
(load file-name))
loads))
(flags-loop rest-args (see saw 'non-config)))]
[("-e" "--eval")
(let-values ([(expr rest-args) (next-arg "expression" arg within-arg args)])
(set! loads
(cons
(lambda ()
(eval (read (open-input-string expr))))
loads))
(flags-loop rest-args (see saw 'non-config)))]
[("-i" "--repl")
(set! repl? #t)
(set! version? #t)
(flags-loop (cdr args) (see saw 'non-config 'top))]
[("-n" "--no-lib")
(set! init-library #f)
(flags-loop (cdr args) (see saw 'non-config))]
[("-v" "--version")
(set! version? #t)
(flags-loop (cddr args) (see saw 'non-config))]
[("-c" "--no-compiled")
(|#%app| use-compiled-file-paths '())
(loop (cdr args))]
[("-I")
(let-values ([(lib-name rest-args) (next-arg "library name" arg within-arg args)])
(when init-library
(set! init-library `(lib ,lib-name)))
(loop rest-args))]
[("-X" "--collects")
(let-values ([(collects-path rest-args) (next-arg "collects path" arg within-arg args)])
(cond
[(equal? collects-path "")
(set! init-collects-dir 'disable)]
[else
(check-path-arg "collects path" arg within-arg)
(set! init-collects-dir (path->complete-path (string->path collects-path)))])
(loop rest-args))]
[("-G" "--config")
(let-values ([(config-path rest-args) (next-arg "config path" arg within-arg args)])
(check-path-arg "config path" arg within-arg)
(set! init-config-dir (path->complete-path (string->path config-path)))
(loop rest-args))]
[("-C" "--cross")
(set! host-config-dir init-config-dir)
(set! host-collects-dir init-collects-dir)
(loop (cdr args))]
[("-U" "--no-user-path")
(|#%app| use-user-specific-search-paths #f)
(loop (cdr args))]
[("-d")
(|#%app| load-on-demand-enabled #f)
(loop (cdr args))]
[("-q" "--no-init-file")
(set! repl-init? #f)
(loop (cdr args))]
[("-W" "--stderr")
(let-values ([(spec rest-args) (next-arg "stderr level" arg within-arg args)])
(set! stderr-logging-arg (parse-logging-spec "stderr" spec (format "after ~a switch" (or within-arg arg)) #t))
(loop rest-args))]
[("-O" "--stdout")
(let-values ([(spec rest-args) (next-arg "stdout level" arg within-arg args)])
(set! stdout-logging-arg (parse-logging-spec "stdout" spec (format "after ~a switch" (or within-arg arg)) #t))
(loop rest-args))]
[("-N" "--name")
(let-values ([(name rest-args) (next-arg "name" arg within-arg args)])
(set-run-file! (string->path name))
(loop rest-args))]
[("--")
(cond
[(or (null? (cdr args)) (not (pair? (cadr args))))
(finish (cdr args) saw)]
[else
;; Need to handle more switches from a combined flag
(loop (cons (cadr args) (cons (car args) (cddr args))))])]
[else
(cond
[(eqv? (string-ref arg 0) #\-)
(cond
[(and (> (string-length arg) 2)
(not (eqv? (string-ref arg 1) #\-)))
;; Split flags
(loop (append (map (lambda (c) (cons (string #\- c) arg))
(cdr (string->list arg)))
(cdr args)))]
[else
(raise-user-error 'racket "bad switch: ~a~a"
arg
(if within-arg
(format " within: ~a" within-arg)
""))])]
[else
;; Non-flag argument
(finish args saw)])]))))
;; Set up GC logging
(define-values (struct:gc-info make-gc-info gc-info? gc-info-ref gc-info-set!)
(make-struct-type 'gc-info #f 10 0 #f null 'prefab #f '(0 1 2 3 4 5 6 7 8 9)))
(define (K plus n)
(let* ([s (number->string (quotient (abs n) 1000))]
[len (string-length s)]
[len2 (+ len
(quotient (sub1 len) 3)
(if (or (< n 0)
(not (eq? "" plus)))
1
0)
1)]
[s2 (make-string len2)])
(string-set! s2 (sub1 len2) #\K)
(let loop ([i len]
[j (sub1 len2)]
[digits 0])
(cond
[(zero? i)
(cond
[(< n 0) (string-set! s2 0 #\-)]
[(not (eq? plus "")) (string-set! s2 0 (string-ref plus 0))])
s2]
[(= 3 digits)
(let ([j (sub1 j)])
(string-set! s2 j #\,)
(loop i j 0))]
[else
(let ([i (sub1 i)]
[j (sub1 j)])
(string-set! s2 j (string-ref s i))
(loop i j (add1 digits)))]))))
(define minor-gcs 0)
(define major-gcs 0)
(define auto-gcs 0)
(define peak-mem 0)
(set-garbage-collect-notify!
(let ([root-logger (|#%app| current-logger)])
;; This function can be called in any Chez Scheme thread
(lambda (gen pre-allocated pre-allocated+overhead pre-time pre-cpu-time
post-allocated post-allocated+overhead post-time post-cpu-time)
(let ([minor? (< gen (collect-maximum-generation))])
(if minor?
(set! minor-gcs (add1 minor-gcs))
(set! major-gcs (add1 major-gcs)))
(set! peak-mem (max peak-mem pre-allocated))
(let ([debug-GC? (log-level? root-logger 'debug 'GC)])
(when (or debug-GC?
(and (not minor?)
(log-level? root-logger 'debug 'GC:major)))
(let ([delta (- pre-allocated post-allocated)])
(log-message root-logger 'debug (if debug-GC? 'GC 'GC:major)
(chez:format "GC: 0:~a~a @ ~a(~a); free ~a(~a) ~ams @ ~a"
(if minor? "min" "MAJ") gen
(K "" pre-allocated) (K "+" (- pre-allocated+overhead pre-allocated))
(K "" delta) (K "+" (- (- pre-allocated+overhead post-allocated+overhead)
delta))
(- post-cpu-time pre-cpu-time) pre-cpu-time)
(make-gc-info (if minor? 'minor 'major) pre-allocated pre-allocated+overhead 0
post-allocated post-allocated+overhead
pre-cpu-time post-cpu-time
pre-time post-time)
#f))))))))
(|#%app| exit-handler
(let ([orig (|#%app| exit-handler)]
[root-logger (|#%app| current-logger)])
(lambda (v)
(when (log-level? root-logger 'info 'GC)
(log-message root-logger 'info 'GC
(chez:format "0:atexit peak ~a; alloc ~a; major ~a; minor ~a; ~ams"
(K "" peak-mem)
(K "" (- (+ (bytes-deallocated) (bytes-allocated)) (initial-bytes-allocated)))
major-gcs
minor-gcs
(let ([t (sstats-gc-cpu (statistics))])
(+ (* (time-second t) 1000)
(quotient (time-nanosecond t) 1000000))))
#f))
(linklet-performance-report!)
(|#%app| orig v))))
(define stderr-logging
(or stderr-logging-arg
(let ([spec (getenv "PLTSTDERR")])
(if spec
(parse-logging-spec "stderr" spec "in PLTSTDERR environment variable" #f)
'(error)))))
(define stdout-logging
(or stdout-logging-arg
(let ([spec (getenv "PLTSTDOUT")])
(if spec
(parse-logging-spec "stdout" spec "in PLTSTDOUT environment variable" #f)
'()))))
(when (getenv "PLT_STATS_ON_BREAK")
(keyboard-interrupt-handler
(let ([orig (keyboard-interrupt-handler)])
(lambda args
(dump-memory-stats)
(apply orig args)))))
(when version?
(printf "Welcome to Racket v~a [cs]\n" (version)))
(call-in-main-thread
(lambda ()
(boot)
(when (and stderr-logging
(not (null? stderr-logging)))
(apply add-stderr-log-receiver! (|#%app| current-logger) stderr-logging))
(when (and stdout-logging
(not (null? stdout-logging)))
(apply add-stdout-log-receiver! (|#%app| current-logger) stdout-logging))
(cond
[(eq? init-collects-dir 'disable)
(|#%app| use-collection-link-paths #f)
(set-collects-dir! (build-path 'same))]
[else
(set-collects-dir! init-collects-dir)])
(set-config-dir! init-config-dir)
(unless (eq? init-collects-dir 'disable)
(|#%app| current-library-collection-links
(find-library-collection-links))
(|#%app| current-library-collection-paths
(find-library-collection-paths)))
(when init-library
(namespace-require+ init-library))
(call-with-continuation-prompt
(lambda ()
(for-each (lambda (ld) (ld))
(reverse loads)))
(default-continuation-prompt-tag)
;; If any load escapes, then set the exit value and
;; stop running loads (but maybe continue with the REPL)
(lambda (proc)
(set! exit-value 1)
;; Let the actual default handler report an arity mismatch, etc.
(call-with-continuation-prompt
(lambda () (abort-current-continuation (default-continuation-prompt-tag) proc)))))
(when repl?
(set! exit-value 0)
(when repl-init?
(let ([m (get-repl-init-filename)])
(when m
(call-with-continuation-prompt
(lambda () (dynamic-require m 0))
(default-continuation-prompt-tag)
(lambda args (set! exit-value 1))))))
(|#%app| (if gracket?
(dynamic-require 'racket/gui/init 'graphical-read-eval-print-loop)
(dynamic-require 'racket/base 'read-eval-print-loop)))
(unless gracket?
(newline)))
(|#%app| (|#%app| executable-yield-handler) exit-value)
(exit exit-value))))
(if (null? the-command-line-arguments)
;; Assume that we're running as a boot file
(scheme-start (lambda args (run args)))
;; Assume that we're running as a script
(run the-command-line-arguments)))