pman/potato/makevars.scm

407 lines
14 KiB
Scheme
Raw Normal View History

2021-02-08 07:46:24 +01:00
(define-module (potato makevars)
#:use-module (srfi srfi-1)
#:use-module (ice-9 format)
#:use-module (potato exceptions)
#:use-module (potato builtins)
2021-02-10 15:28:32 +01:00
#:use-module (potato text)
2021-02-08 07:46:24 +01:00
#:export (initialize-makevars
%makevars
2021-02-10 15:28:32 +01:00
%elevate-environment?
2021-02-08 07:46:24 +01:00
lazy-assign ?=
assign :=
2021-02-13 01:34:18 +01:00
reference $ Q
2021-02-08 07:46:24 +01:00
reference-func $$
dump-makevars
))
;; There are five priority levels
;; 1. defined - in the script itself
;; 2. command-line
;; 3. makeflags - in the MAKEFLAGS environment variable
;; 4. env - specified in the environment
;; 5. built-in - one of the built-in macros
;; The lower priority level always win, unless the '-e' flag was set
;; If the '-e' flag is set level 1 doesn't override level 3 and 4.
2021-02-15 04:54:37 +01:00
(define %level-name '("unknown" "script" "command-line" "makeflags" "environment" "built-in"))
2021-02-10 15:28:32 +01:00
(define %ascii? #f)
2021-02-14 08:42:16 +01:00
(define %makevars (make-hash-table))
2021-02-10 15:28:32 +01:00
(define %elevate-environment? #f)
2021-02-13 01:34:18 +01:00
(define %strict #f)
2021-02-10 15:28:32 +01:00
(define %verbose? #t)
2021-02-08 07:46:24 +01:00
(define (debug spec . args)
2021-02-10 15:28:32 +01:00
(when %verbose?
2021-02-08 07:46:24 +01:00
(apply format (append (list #t spec) args))))
(define (split-at-equals str)
"Splits the string at the first equals sign, ignoring
later equals signs."
(let ((idx (string-index str #\=)))
(if (and idx (> idx 0))
(cons (substring str 0 idx)
(substring str (1+ idx)))
;; else
#f)))
(define (string-append-with-spaces lst)
"Appends the strings in lst, adding spaces in between."
(fold
(lambda (elem prev)
(string-append prev " " elem))
(car lst)
(cdr lst)))
(define (override? old-priority new-priority)
"The logic of whether which makemacro priority levels can override
others."
2021-02-10 15:28:32 +01:00
(if %elevate-environment?
2021-02-13 01:34:18 +01:00
(if (and (or (= old-priority 2) (= old-priority 3) (= old-priority 4))
2021-02-08 07:46:24 +01:00
(= new-priority 1))
#f
;; else
(<= new-priority old-priority))
;; else
(<= new-priority old-priority)))
(define* (makevars-set key
#:optional (new-val "") (new-priority 1))
"Maybe add key / val to %makevars hash table, if there is sufficient
priority."
;; Note that VAL can be either a string or a procedure. If it is a
;; procedure, it is converted into a promise to be evaluated later.
(let* ((val&priority (hash-ref %makevars key))
(old-val (if (pair? val&priority) (cdr val&priority) #f))
(old-priority (if (pair? val&priority) (cdr val&priority) #f)))
2021-02-15 04:54:37 +01:00
(when (or (not old-val)
(override? old-priority new-priority))
(if (procedure? new-val)
(hash-set! %makevars key (cons (delay new-val) new-priority))
(hash-set! %makevars key (cons new-val new-priority)))
(when %verbose? (print-makevar key))))
2021-02-08 07:46:24 +01:00
*unspecified*)
(define (makevars-add-keyvals keyvals)
"Adds any suitable macros passed in from the command line, which
here are expected to be a list of key / val string pairs."
(for-each
(lambda (entry)
(let ((key (car entry))
(val (cdr entry)))
(unless (or (string=? key "SHELL")
(string=? key "MAKEFLAGS"))
(makevars-set key val 2))))
keyvals))
(define (makevars-add-makeflags)
"Adds any suitable environment variables found in the MAKEFLAGS
environment variable to the macro store"
(let ((makeflags (getenv "MAKEFLAGS")))
(when makeflags
(for-each
(lambda (entry)
(let* ((keyval (split-at-equals entry))
(key (if keyval (car keyval) #f))
(val (if keyval (cdr keyval) #f)))
(unless (or (not (string? key))
(string=? key "SHELL")
(string=? key "MAKEFLAGS"))
(makevars-set key val 3))))
(string-split makeflags #\space)))))
(define (makevars-add-environment)
"Adds any suitable environment variables to the macro store, but not
the value of MAKEFLAGS or SHELL."
(for-each
(lambda (entry)
(let* ((keyval (split-at-equals entry))
(key (if keyval (car keyval) #f))
(val (if keyval (cdr keyval) #f)))
(unless (or (string=? key "SHELL")
(string=? key "MAKEFLAGS"))
(makevars-set key val 4))))
(environ)))
(define (makevars-add-builtins)
"Adds the default macros to the store"
(for-each
(lambda (keyval)
(makevars-set (car keyval) (cdr keyval) 5))
builtin-makevars))
2021-02-15 04:54:37 +01:00
(define (print-makevar key)
(let ((val (hash-ref %makevars key)))
(let ((keyval-string
(if (zero? (string-length (car val)))
(string-copy key)
(string-append key " " (right-arrow) " " (car val)))))
;; Replace any control characters in VAL, like newline or tab
(set! keyval-string
(string-fold
(lambda (c str)
(string-append str
(if (char<? c #\space)
(C0 c)
(string c))))
""
keyval-string))
;; Truncate
(if (> (string-length keyval-string) 60)
(if %ascii?
(set! keyval-string
(string-append (substring keyval-string 0 57) "..."))
(set! keyval-string
(string-append (substring keyval-string 0 59) "…"))))
(let* ((space (make-string (- 64 (string-length keyval-string))
#\space))
(priority (cdr val))
(source-string (list-ref '("unknown"
"script"
"command line"
"MAKEFLAGS"
"environment"
"built-in")
priority)))
(display "Var: ")
(display keyval-string)
(display space)
(display source-string)
(newline)))))
2021-02-08 07:46:24 +01:00
(define (dump-makevars)
"Write out a list of the current makevars."
(when (not (zero? (hash-count (const #t) %makevars)))
2021-02-10 15:28:32 +01:00
(display (underline))
2021-02-08 07:46:24 +01:00
(display "Makevars")
2021-02-10 15:28:32 +01:00
(display (default))
2021-02-08 07:46:24 +01:00
(newline)
(let ((keyvals
(sort
(hash-map->list cons %makevars)
(lambda (a b)
(string<? (car a) (car b))))))
(for-each
(lambda (keyval)
2021-02-10 15:28:32 +01:00
(let ((key (car keyval))
(val (cdr keyval)))
(let ((keyval-string
(if (zero? (string-length (car val)))
(string-copy key)
(string-append key " " (right-arrow) " " (car val)))))
;; Replace any control characters in VAL, like newline or tab
(set! keyval-string
(string-fold
(lambda (c str)
(string-append str
(if (char<? c #\space)
(C0 c)
(string c))))
""
keyval-string))
;; Truncate
(if (> (string-length keyval-string) 60)
(if %ascii?
(set! keyval-string
(string-append (substring keyval-string 0 57) "..."))
(set! keyval-string
(string-append (substring keyval-string 0 59) "…"))))
(let* ((space (make-string (- 64 (string-length keyval-string))
#\space))
(priority (cdr val))
(source-string (list-ref '("unknown"
"script"
"command line"
"MAKEFLAGS"
"environment"
"built-in")
priority)))
(display " ")
(display keyval-string)
(display space)
(display source-string)
(newline)))))
keyvals))))
2021-02-08 07:46:24 +01:00
(define (initialize-makevars keyvals
2021-02-10 15:28:32 +01:00
environment?
elevate-environment?
builtins?
2021-02-13 01:34:18 +01:00
strict?
verbosity
2021-02-10 15:28:32 +01:00
ascii?)
(set! %elevate-environment? elevate-environment?)
2021-02-14 08:42:16 +01:00
(hash-clear! %makevars)
2021-02-13 01:34:18 +01:00
(set! %strict strict?)
(set! %verbose? (= verbosity 3))
2021-02-10 15:28:32 +01:00
(set! %ascii? ascii?)
(when builtins?
2021-02-08 07:46:24 +01:00
(makevars-add-builtins))
2021-02-13 01:34:18 +01:00
(when (or environment? elevate-environment?)
2021-02-10 15:28:32 +01:00
(makevars-add-environment)
(makevars-add-makeflags))
2021-02-15 04:54:37 +01:00
(makevars-add-keyvals keyvals))
2021-02-08 07:46:24 +01:00
;; API
(define* (lazy-assign key #:optional (val ""))
"This procedure sets an entry in the %makevars hash table.
KEY must be a string or a thunk that evaluates to a string. Likewise
VAL.
If KEY is a thunk, it is immediately evaluated to a string to use as
the key in the hash table entry.
If VAL is a thunk, it is stored as a *promise* to be evaluated
later. The promise will be evaluated the first time this key is
referenced.
If VAL is not given, the empty string will be used."
2021-02-13 01:34:18 +01:00
(when (procedure? key)
(set! key (key)))
(unless (string? key)
(set! key (format #f "~a" key)))
2021-02-15 04:54:37 +01:00
(makevars-set key (delay val)))
2021-02-08 07:46:24 +01:00
(define-syntax ?=
(lambda (stx)
(syntax-case stx ()
((_ key val)
#'(lazy-assign (symbol->string (syntax->datum #'key)) val))
((_ key)
#'(lazy-assign (symbol->string (syntax->datum #'key)))))))
(define* (assign key #:optional (val ""))
"This procedure sets an entry in the %makevars hash table.
KEY must be a string or a thunk that evaluates to a string. Likewise
VAL.
If KEY and/or VAL is a thunk, it is immediately evaluated to a
string to use as the key in the hash table entry.
If VAL is not given, the empty string will be used."
2021-02-13 01:34:18 +01:00
(when (procedure? key)
(set! key (key)))
(unless (string? key)
(set! key (format #f "~a" key)))
(when (procedure? val)
(set! val (val)))
(unless (string? val)
(set! val (format #f "~a" val)))
2021-02-15 04:54:37 +01:00
(makevars-set key val))
2021-02-08 07:46:24 +01:00
(define-syntax :=
(lambda (stx)
(syntax-case stx ()
((_ key val)
#'(assign (symbol->string (syntax->datum #'key)) val))
((_ key)
#'(assign (symbol->string (syntax->datum #'key)))))))
2021-02-13 01:34:18 +01:00
(define* (reference key quoted? #:optional (transformer #f))
2021-02-08 07:46:24 +01:00
"Looks up KEY in the %makevars hash table. KEY may be a string
or a procedure that evaluates to a string.
If the value of the key
in the hash table was a *promise* it will be forced,
evaluated, and set to that result.
If no transformer is supplied, the looked up value will be
returned.
TRANSFORMER, if
supplied, should be a procedure of one string argument that returns a
string. If a transformer is supplied, it will be applied to every
space-separated token in the looked-up value."
(when (and (not (string? key))
(not (procedure? key)))
(bad-key-type "reference" (list key)))
(when (procedure? key)
(set! key (key))
(unless (string? key)
(bad-proc-output "reference" key)))
2021-02-13 01:34:18 +01:00
(when (not (string? key))
(set! key (format #t "~a" key)))
2021-02-08 07:46:24 +01:00
(let* ((val&priority (hash-ref %makevars key))
(val (if (pair? val&priority) (car val&priority) #f))
(priority (if (pair? val&priority) (cdr val&priority) #f)))
(if (not val)
2021-02-13 01:34:18 +01:00
(if %strict
2021-02-15 04:54:37 +01:00
(error (format #t "There is no makevar for key ~a~%~!" key))
2021-02-13 01:34:18 +01:00
;; else
(if quoted?
"\"\""
""))
2021-02-08 07:46:24 +01:00
;; else
(begin
2021-02-13 01:34:18 +01:00
(cond
((promise? val)
2021-02-08 07:46:24 +01:00
(set! val (force val))
2021-02-13 01:34:18 +01:00
(cond
((string? val)
;; noop
#t)
((procedure? val)
(set! val (val)))
(else
(set! val (format #f "~a" val)))))
((string? val)
;; noop
#f)
(else
(set! val (format #f "~a" val))))
(hash-set! %makevars key (cons val priority))
2021-02-15 04:54:37 +01:00
(when %verbose? (print-makevar key))
2021-02-08 07:46:24 +01:00
(when (procedure? transformer)
(set! val (string-append-with-spaces
(map transformer
(string-tokenize val)))))
2021-02-13 01:34:18 +01:00
(if quoted?
(string-append "\"" val "\"")
val)))))
2021-02-08 07:46:24 +01:00
(define-syntax $
(lambda (stx)
(syntax-case stx ()
2021-02-13 01:34:18 +01:00
((_ key transformer)
#'(reference (symbol->string (syntax->datum #'key)) #f transformer))
((_ key)
#'(reference (symbol->string (syntax->datum #'key)) #f)))))
(define-syntax Q
(lambda (stx)
(syntax-case stx ()
((_ key transformer)
#'(reference (symbol->string (syntax->datum #'key)) #t transformer))
2021-02-08 07:46:24 +01:00
((_ key)
2021-02-13 01:34:18 +01:00
#'(reference (symbol->string (syntax->datum #'key)) #t)))))
2021-02-08 07:46:24 +01:00
2021-02-10 17:14:50 +01:00
(define (reference-func key)
2021-02-08 07:46:24 +01:00
"Looks up KEY in the %makevars hash table. KEY shall be a string
or a procedure that evaluates to a string.
If the value of the key
in the hash table was a *promise*, a procedure will be returned that,
when called, will call that promise. If the value of the key is
a string, a procedure will be returned that, when called, returns
that string."
(when (and (not (string? key))
(not (procedure? key)))
(bad-key-type "reference" (list key)))
(when (procedure? key)
(set! key (key))
(unless (string? key)
(bad-proc-output "reference" key))
(let* ((val&priority (hash-ref %makevars key))
(val (if (pair? val&priority) (cdr val&priority) #f)))
(if (not val)
#f
;; else
(begin
(if (promise? val)
(lambda ()
(let ((VAL (force val)))
2021-02-10 15:28:32 +01:00
;; FIXME: put verbose print here?
2021-02-08 07:46:24 +01:00
VAL))
;; else
(lambda ()
val)))))))
(define-syntax $$
(lambda (stx)
(syntax-case stx ()
((_ key)
2021-02-10 17:14:50 +01:00
#'(reference-func (symbol->string (syntax->datum #'key)))))))
2021-02-13 01:34:18 +01:00