pman/potato/make.scm

296 lines
11 KiB
Scheme
Raw Permalink Normal View History

2021-02-11 11:55:40 +01:00
(define-module (potato make)
2021-02-08 07:46:24 +01:00
#:use-module (srfi srfi-1)
#:use-module (ice-9 exceptions)
#:use-module (ice-9 optargs)
#:use-module (ice-9 getopt-long)
#:use-module (ice-9 receive)
2021-02-15 00:28:12 +01:00
#:use-module (system vm trace)
2021-02-08 07:46:24 +01:00
#:use-module (potato exceptions)
#:use-module (potato makevars)
#:use-module (potato rules)
2021-02-10 15:28:32 +01:00
#:use-module (potato text)
2021-02-08 07:46:24 +01:00
#:export (initialize
2021-02-13 01:34:18 +01:00
execute)
2021-02-08 07:46:24 +01:00
#:re-export (%suffix-rules
lazy-assign ?=
assign :=
2021-02-13 01:34:18 +01:00
reference $ Q
2021-02-08 07:46:24 +01:00
reference-func $$
target-rule :
suffix-rule ->
target-name $@
target-basename $*
2021-02-14 08:42:16 +01:00
newer-prerequisites $? $$?
prerequisites $^ $$^
2021-02-08 07:46:24 +01:00
primary-prerequisite $<
2021-02-13 01:34:18 +01:00
string-compose ~
silent-compose ~@
always-execute-compose ~+
ignore-error-compose ~-
install-alternate-system-driver
2021-02-08 07:46:24 +01:00
))
(define %version "1.0")
2021-02-13 01:34:18 +01:00
(define %debug-argv0 #f)
2021-02-08 07:46:24 +01:00
;; #:re-export (
;; lazy-assign ?=
;; assign :=
;; ref $
;; target-rule :
;; suffix-rule ->
;; compose ~
;; ))
;; Asynchronous events.
;; SIGHUP, SIGTERM, SIGINT and SIGQUIT remove the current target
;; unless that target is a directory or the target is a prerequisite
;; of .PRECIOUS or the -n, -q, or -p option was specified. This
;; deletion shall be reported to the error port, then the default for
;; that action will continue.
;; .SILENT
;; The utility shall write all commands to the standard output unless
;; the -s option was specified, the command is prefixed with +, or
;; .SILENT has the current target as a prerequisite or has no pre
;; requisites.
;; Nothing to be done
;; If make was invoked but found no work to do, it shall write a
;; message to standard output that no action was taken
;; File Touched
;; If the -t option was specified, make shall write to standard
;; output a message for each file that was touched.
;; Verbosity is 0 = silent, 1 = terse, 2 = default, 3 = verbose
(define %verbosity 2)
2021-02-08 07:46:24 +01:00
(define %opt-verbose #f)
(define %opt-ignore-errors #f)
(define %opt-continue-on-error #f)
(define %targets '())
(define %initialized #f)
(define (critical spec . args)
(apply format (append (list #t spec) args)))
(define (print spec . args)
(when (>= %verbosity 2)
2021-02-08 07:46:24 +01:00
(apply format (append (list #t spec) args))))
(define (debug spec . args)
(when (>= %verbosity 3)
2021-02-08 07:46:24 +01:00
(apply format (append (list #t spec) args))))
(define option-spec
2021-02-10 15:28:32 +01:00
'((help (single-char #\h) (value #f))
(version (single-char #\v) (value #f))
(verbosity (single-char #\V) (value #t))
2021-02-10 15:28:32 +01:00
(environment (single-char #\e) (value #f))
(elevate-environment (single-char #\E) (value #f))
(builtins (single-char #\b) (value #f))
(ignore-errors (value #f))
2021-02-08 07:46:24 +01:00
(continue-on-error (single-char #\k) (value #f))
2021-02-10 15:28:32 +01:00
(no-execution (single-char #\n) (value #f))
(ascii (single-char #\A) (value #f))
2021-02-13 01:34:18 +01:00
(strict (single-char #\S) (value #f))
2021-02-08 07:46:24 +01:00
))
(define (display-help-and-exit argv0)
2021-02-10 15:28:32 +01:00
(format #t "~A [-hvqVeEbn] [KEY=VALUE ...] [targets ...]~%" argv0)
(format #t " -h, --help print help and exit~%")
(format #t " -v, --version print version and exit~%")
(format #t " -V 0..3, --verbosity=0..3~%")
(format #t " set output level from 0=silent to 3=verbose~%")
2021-02-10 15:28:32 +01:00
(format #t " -e, --environment use environment variables~%")
(format #t " -E, --elevate-environment~%")
(format #t " use environment variables and let~%")
(format #t " them override script variables~%")
(format #t " -b, --builtins~%")
(format #t " include some common variables and suffix rules~%")
(format #t " --ignore-errors~%")
2021-02-13 01:34:18 +01:00
(format #t " ignore all errors~%")
2021-02-10 15:28:32 +01:00
(format #t " -k, --continue-on-error~%")
2021-02-13 01:34:18 +01:00
(format #t " after an error, keep building other targets~%")
2021-02-10 15:28:32 +01:00
(format #t " -n, --no-execution~%")
(format #t " only execute rules marked as 'always execute'~%")
(format #t " -a, --ascii~%")
(format #t " ASCII only output and no colors~%")
2021-02-13 01:34:18 +01:00
(format #t " -S, --strict~%")
(format #t " causes some behaviours to throw errors~%")
2021-02-08 07:46:24 +01:00
(exit 0))
(define (display-version-and-exit argv0)
(format #t "~a~%" argv0)
2021-02-11 11:55:40 +01:00
(format #t " using potato make~a~%" %version)
2021-02-08 07:46:24 +01:00
(exit 0))
(define (parse-macros lst)
"Search for list for strings of the form KEY=VAR and return a list
of pairs of KEY VAL"
(filter-map
(lambda (str)
2021-02-13 01:34:18 +01:00
(let ((tok (string-split str #\=)))
2021-02-08 07:46:24 +01:00
(cond
((= 1 (length tok))
#f)
((= 2 (length tok))
(cons (car tok) (cadr tok)))
(else
(invalid-macro "parse-macros" str)))))
lst))
(define (parse-targets lst)
"Search the list for strings that don't have equals signs, and
return them in a list."
(filter-map
(lambda (str)
(if (string-index str #\=)
#f
str))
lst))
(define* (initialize #:optional
2021-02-13 01:34:18 +01:00
(arguments #f))
2021-02-10 15:28:32 +01:00
"Set up the options, rules, and makevars. If ARGUMENTS
is not set, it will use options, makevars, and targets as
specified by the command line. If it is set, it is
expected to be a list of strings that are command-line
arguments."
2021-02-08 07:46:24 +01:00
2021-02-13 01:34:18 +01:00
;; If left unset, assume user want all the command line arguments.
(when (not arguments)
(set! arguments (command-line)))
;; If the user has set it to '(), expecting a null environment, add
;; back in a filename, which is required.
2021-02-08 07:46:24 +01:00
(when (null? arguments)
2021-02-13 01:34:18 +01:00
(set! arguments (list (car (program-arguments)))))
2021-02-08 07:46:24 +01:00
;; We start of with the --help and --version command-line arguments.
(let ((options (getopt-long arguments option-spec))
2021-02-10 15:28:32 +01:00
(%opt-builtins #f)
(%opt-environment #f)
2021-02-08 07:46:24 +01:00
(%opt-elevate-environment #f)
2021-02-10 15:28:32 +01:00
(%opt-no-errors #f)
(%opt-continue-on-error #f)
(%opt-no-execution #f)
2021-02-13 01:34:18 +01:00
(%opt-ascii #f)
(%opt-strict #f))
2021-02-08 07:46:24 +01:00
(when (option-ref options 'help #f)
(display-help-and-exit (car arguments)))
(when (option-ref options 'version #f)
(display-version-and-exit (car arguments)))
2021-02-10 15:28:32 +01:00
;; Then, we do --environment, because we need to know that
2021-02-08 07:46:24 +01:00
;; before we start parsing MAKEFLAGS
2021-02-10 15:28:32 +01:00
(set! %opt-environment
(option-ref options 'environment #f))
2021-02-08 07:46:24 +01:00
;; Parse MAKEFLAGS before the command-line, because we want
;; command-line options to override MAKEFLAGS options.
2021-02-10 15:28:32 +01:00
(when %opt-environment
2021-02-08 07:46:24 +01:00
(let ((mf (getenv "MAKEFLAGS")))
(when mf
(let ((tokens (string-tokenize mf)))
2021-02-14 08:42:16 +01:00
(when (member "silent" tokens)
(set! %verbosity 0))
2021-02-13 01:34:18 +01:00
(when (member "terse" tokens)
(set! %verbosity 1))
2021-02-08 07:46:24 +01:00
(when (member "verbose" tokens)
(set! %verbosity 3))
2021-02-10 15:28:32 +01:00
(when (member "builtins" tokens)
(set! %opt-builtins #t))
(when (member "ascii" tokens)
(set! %opt-ascii #t))
(when (member "ignore-errors" tokens)
(set! %opt-ignore-errors #t))
(when (member "continue-on-error" tokens)
(set! %opt-continue-on-error #t))
2021-02-13 01:34:18 +01:00
(when (member "strict" tokens)
(set! %opt-strict #t))
2021-02-10 15:28:32 +01:00
(when (member "no-execution" tokens)
(set! %opt-no-execution #t))))))
2021-02-08 07:46:24 +01:00
;; Now the bulk of the command-line options.
(when (option-ref options 'verbosity #f)
(let ((verbosity (string->number (option-ref options 'verbosity #f))))
(when verbosity
(set! %verbosity verbosity))))
2021-02-13 01:34:18 +01:00
(when (option-ref options 'builtins #f)
(set! %opt-builtins #t))
(when (option-ref options 'elevate-environment #f)
(set! %opt-elevate-environment #t))
(when (option-ref options 'ignore-errors #f)
(set! %opt-ignore-errors #t))
(when (option-ref options 'continue-on-error #f)
(set! %opt-continue-on-error #t))
(when (option-ref options 'no-execution #f)
(set! %opt-no-execution #t))
(when (option-ref options 'ascii #f)
(set! %opt-ascii #t))
(when (option-ref options 'strict #f)
(set! %opt-strict #t))
2021-02-08 07:46:24 +01:00
;; Now that all the options are set, we can set up
;; the build environment.
(let ((extra (option-ref options '() '())))
2021-02-10 15:28:32 +01:00
(initialize-text %opt-ascii)
2021-02-08 07:46:24 +01:00
(initialize-makevars (parse-macros extra)
2021-02-10 15:28:32 +01:00
%opt-environment
2021-02-08 07:46:24 +01:00
%opt-elevate-environment
2021-02-10 15:28:32 +01:00
%opt-builtins
2021-02-13 01:34:18 +01:00
%opt-strict
%verbosity
2021-02-10 15:28:32 +01:00
%opt-ascii)
2021-02-08 07:46:24 +01:00
;; The remaining command-line words are the build targets that
;; we're going to tackle.
(set! %targets (parse-targets extra))
2021-02-13 01:34:18 +01:00
(initialize-rules %targets
%opt-builtins
%opt-ignore-errors
%opt-continue-on-error
%opt-no-execution
%verbosity
2021-02-13 01:34:18 +01:00
%opt-ascii)
2021-02-08 07:46:24 +01:00
(set! %initialized #t)
%targets
)))
(define* (execute #:key (targets '()))
"This function runs build actions. TARGETS, if provided, is a list
of target names to be executed. If TARGETS is not provided, the
targets listed on the parsed command-line are used."
;; First, let's figure out what targets we're building.
(unless %initialized
(critical "The initialize procedure was not called in this build script.~%")
(critical "Using an empty environment.~%"))
(when (null? targets)
(set! targets %targets))
(when (null? targets)
2021-02-14 08:42:16 +01:00
(debug "No build target was explicitely specified.~%")
2021-02-08 07:46:24 +01:00
(let ((rule (first-target-rule-name)))
(if rule
(begin
2021-02-14 08:42:16 +01:00
(debug "Using first rule ~a~A~a as the build target.~%" (lquo) rule (rquo))
(set! targets (list rule)))
;; else
(debug "There are no target rules in the recipe.~%"))))
2021-02-08 07:46:24 +01:00
;; Build each target in order.
(when (not (null? targets))
(let loop ((target (car targets))
(rest (cdr targets)))
(if (not (build target))
(begin
2021-02-14 08:42:16 +01:00
(print "The recipe for “~A” has failed.~%" target)
#f)
2021-02-08 07:46:24 +01:00
;; else
(begin
(print "The recipe “~A” finished successfully.~%" target)
2021-02-08 07:46:24 +01:00
(if (not (null? rest))
2021-02-14 08:42:16 +01:00
(loop (car rest) (cdr rest))
2021-02-08 07:46:24 +01:00
2021-02-14 08:42:16 +01:00
;; True if all targets are built successfully.
#t))))))
2021-02-08 07:46:24 +01:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;