Clean up output
This commit is contained in:
parent
cbee22e2de
commit
67566c9478
149
CHEATSHEET.md
149
CHEATSHEET.md
@ -1,149 +0,0 @@
|
||||
# CHEATSHEET FOR POTATO MAKE
|
||||
|
||||
## Boilerplate
|
||||
|
||||
Add this at the top of your build script.
|
||||
|
||||
#!/usr/bin/env sh
|
||||
exec guile -s "$0" "$@"
|
||||
!#
|
||||
|
||||
(use-modules (potato make))
|
||||
(initialize)
|
||||
|
||||
Add this at the bottom of your build script
|
||||
|
||||
(execute)
|
||||
|
||||
The rules go in between `initialize` and `build`
|
||||
|
||||
## MAKEVARS
|
||||
|
||||
A hash table called `%makevars` has string keys. These procedures
|
||||
are syntax that add quotation marks around `key`, so you call them without the quotes on
|
||||
`key`. The returned value of `$` is a string, or an empty string on failure.
|
||||
|
||||
($ KEY) -> "VAL"
|
||||
|
||||
($ key [transformer])
|
||||
Look up `key` in the `%makevars` hash table and return the
|
||||
result as a string. If `key` is not found, return an empty
|
||||
string. If a string-to-string transformer procedure is
|
||||
provided, apply it to each space-separated token in the
|
||||
result.
|
||||
|
||||
(?= key val)
|
||||
Assign `val` to `key` in the `%makevars` hash table. If `val`
|
||||
is a procedure, assign its output to `key` the first time that
|
||||
`key` is referenced.
|
||||
|
||||
(:= key val)
|
||||
Assign `val` to `key` in the `%makevars` hash table. If `val`
|
||||
is a procedure, evaluate it and assign its output to `key`
|
||||
immediately.
|
||||
|
||||
## Rules
|
||||
|
||||
The *target rule* is for when the target, and the prerequisites, if any,
|
||||
have filenames or phony names.
|
||||
|
||||
(: target-name '(prereq-name-1 prereq-name-2 ...)
|
||||
recipe-1
|
||||
recipe-2
|
||||
...)
|
||||
|
||||
`target-name` is a string which is either a filename to be
|
||||
created or an phony name like "all" or "clean".
|
||||
|
||||
Recipe as a string to be evaluated by the system
|
||||
|
||||
(: "foo.o" '("foo.c")
|
||||
"cc -c foo.o")
|
||||
|
||||
Recipe as a procedure
|
||||
|
||||
(: "clean-foo" '()
|
||||
(lambda ()
|
||||
(delete-file "foo.o")))
|
||||
|
||||
Recipe as a procedure that returns #f to indicate failure
|
||||
|
||||
(: "recent" '()
|
||||
(lambda ()
|
||||
(if condition
|
||||
#t
|
||||
#f))))
|
||||
|
||||
Recipe as a procedure returning a string to be evaluated by the
|
||||
system
|
||||
|
||||
(: "foo.o" '("foo.c")
|
||||
(lambda ()
|
||||
(format #f "cc ~A -c foo.c" some-flags))
|
||||
|
||||
Recipe using recipe helper procedures, which create a string to
|
||||
be evaluated by the system
|
||||
|
||||
(: "foo.c" '("foo.c")
|
||||
(~ ($ CC) ($ CFLAGS) "-c" $<))
|
||||
|
||||
Recipe as a boolean to indicate pass or failure without doing any
|
||||
processing. For example, the rule below tells Potato Make that
|
||||
the file "foo.c" exists without actually testing for it.
|
||||
|
||||
(: "foo.c" '() #t)
|
||||
|
||||
If there is no recipe at all, it is shorthand for the recipe #t,
|
||||
indicating a recipe that always passes. This is used
|
||||
in prerequisite-only target rules, such as below, which passes
|
||||
so long as the prerequisites
|
||||
pass. These two rules are the same.
|
||||
|
||||
(: "all" '("foo.exe"))
|
||||
(: "all" '("foo.exe") #t)
|
||||
|
||||
Lastly, if the recipe is #f, this target will always fail.
|
||||
|
||||
(: "fail" '() #f)
|
||||
|
||||
The *suffix rule* is a generic rule to convert one source file to a
|
||||
target file, based on the filename extensions.
|
||||
|
||||
(-> ".c" ".o"
|
||||
(~ ($ CC) ($ CFLAGS) "-c" $< "-o" $@))
|
||||
|
||||
## Recipe Helpers
|
||||
|
||||
Concatenate elements with `~`. `~` inserts spaces between the
|
||||
elements.
|
||||
|
||||
Elements can be
|
||||
- strings
|
||||
- procedures that return strings
|
||||
- `%makevar` hash-table references
|
||||
- automatic variables
|
||||
- anything whose string representation as created by
|
||||
(format #f "~A" ...) make sense
|
||||
|
||||
Any procedures are applied lazily, when the rule is executed.
|
||||
|
||||
(~ "string" (lambda () "string") ($ KEY) $@ 100 )
|
||||
|
||||
Three versions of `~` with special effects
|
||||
(~- ...) ignores any errors
|
||||
(~@ ...) doesn't print recipe to console
|
||||
(~+ ...) runs even when `--no-execute` was chosen
|
||||
|
||||
## Automatic Variables
|
||||
|
||||
Recipes can contain the following automatic variables
|
||||
|
||||
$@ the target
|
||||
$* the target w/o a filename suffix
|
||||
$< the first prerequisite
|
||||
$^ the prerequisites, as a single space-separated string
|
||||
$$^ the prerequisites, as a scheme list of strings
|
||||
$? the prerequisites that are files newer than the target file
|
||||
as a single space-separated string
|
||||
$$? the prerequisites that are files newer than the target file
|
||||
as a scheme list of strings
|
510
NOTES.md
Normal file
510
NOTES.md
Normal file
@ -0,0 +1,510 @@
|
||||
# POTATO MAKE
|
||||
|
||||
A build tool written in Guile Scheme.
|
||||
|
||||
## Description
|
||||
|
||||
Potato Make is a scheme library that aims to simplify the task of
|
||||
maintaining, updating, and regenerating programs. It is inspired by
|
||||
the `make` utility in POSIX. With this library, you can write a
|
||||
build script in Guile Scheme.
|
||||
|
||||
Like POSIX `make`, these scripts update files that are derived from
|
||||
other files. The makefile script typically will describe how a file is
|
||||
built from shell commands, and it will describe the relationships
|
||||
between components to be built, so that they are built in order. In a
|
||||
typical script, the script will check the prerequisites to a target,
|
||||
and if the prerequisites are newer than the target, it will rebuild
|
||||
the target.
|
||||
|
||||
There are two types of rules that a makefile script can contain.
|
||||
|
||||
1. Target rules, which describe how a specific named file is to be
|
||||
built from prerequisites using a set of shell commands.
|
||||
2. Suffix rules, which generically describe how to convert files with
|
||||
one filename suffix into files with another filename suffix.
|
||||
|
||||
The makefile script will make use of a custom variable type which can
|
||||
be set either in the script, by environment variables, or in command
|
||||
line arguments. Let's call them *makevars*, to reduce confusion with
|
||||
standard scheme variables.
|
||||
|
||||
## Setting up the Scheme Script
|
||||
|
||||
To write a build script with this library, one needs to add the
|
||||
following boilerplate code at the top of an executable scheme script.
|
||||
Throughout this documentation we will presume that this scheme script
|
||||
is named `makefile.scm`; however, you may choose any name.
|
||||
|
||||
#!/usr/bin/env sh
|
||||
exec guile -s "$0" "$@"
|
||||
!#
|
||||
|
||||
(use-modules (potato make))
|
||||
(initialize)
|
||||
|
||||
This boilerplate loads the library functions and it parses the
|
||||
command-line arguments. The command-line arguments are the following,
|
||||
|
||||
makefile.scm [-hvqVeEbknB] [var=value...] [target_name...]
|
||||
-h, --help
|
||||
displays help
|
||||
-v, --version
|
||||
displays the version number of this script
|
||||
-V [0,1,2,3], --verbosity=[0,1,2,3]
|
||||
choose the verbosity of the output
|
||||
-e, --environment
|
||||
environment variables are converted to makevars
|
||||
-E, --elevate-environment
|
||||
environment variables are converted to makevars
|
||||
and will override makevars set in the script
|
||||
-b, --builtins
|
||||
adds some default makevars and suffix rules
|
||||
--ignore-errors
|
||||
keep building even if a command fails
|
||||
-k, --continue-on-error
|
||||
keep building some targets even if a command fails
|
||||
-n, --no-execute
|
||||
print rules, but only execute rules marked as
|
||||
'always execute'
|
||||
-a, --ascii
|
||||
use ASCII-only output and no colors
|
||||
-W, --warn
|
||||
enable warning messages
|
||||
|
||||
[var=value...]
|
||||
set the value of makevars
|
||||
[target_name...]
|
||||
Set one or more targets to be executed. If no target
|
||||
is specified, the first target found will be executed.
|
||||
|
||||
Note that in POSIX `make`, it, by default, adds in environment
|
||||
variables and built-in rules. With this library, these require
|
||||
command-line arguments to be enabled to pick up environment variables
|
||||
and built-in rules. This is to make this tool more appropriate for
|
||||
generating *reproducible builds*.
|
||||
|
||||
If you don't want `initialize` to parse the command line, you may call
|
||||
it with specific command line arguments, like the example below. The
|
||||
first string is the name of the script, and then any combination of
|
||||
flags, macro assignments and targets may follow.
|
||||
|
||||
(initialize '("makefile.scm" "--verbosity=3" "CC=gcc" "all"))
|
||||
|
||||
If you call initialize with an empty list as below, it will guess the
|
||||
script name from the command-line arguements, but, will ignore all
|
||||
other flags and options.
|
||||
|
||||
;; ignore all command line arguments except the script name
|
||||
(initialize '())
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Certain environment variables affect the execution of the makefile
|
||||
script.
|
||||
|
||||
`LANG` - affects the current locale
|
||||
|
||||
`MAKEFLAGS` - This will be parsed similar to command-line arguments.
|
||||
If it contains the single, space-separated letters 'e', 'f', 'i',
|
||||
'k', 'n', 'p', 'r', or 's', those options will be enabled as if
|
||||
set on the command line. If it contains strings of the form
|
||||
VAR=VALUE, it will set those makevars.
|
||||
|
||||
`SHELL` - The shell environment variable is always ignored.
|
||||
|
||||
All other environment variables, including those with null values,
|
||||
shall initialize makevars.
|
||||
|
||||
## Signals
|
||||
|
||||
`SIGHUP`, `SIGTERM`, `SIGINT`, and `SIGQUIT` shall interrupt any
|
||||
processing.
|
||||
|
||||
## Rules
|
||||
|
||||
The purpose of a makefile script is to run rules, which describe how
|
||||
programs act on prerequisites to create targets. There are two types
|
||||
of rules: target rules and suffix rules.
|
||||
|
||||
### Target Rules
|
||||
|
||||
Target rules are defined and manipulated with the following commands.
|
||||
|
||||
target-rule name [prerequisites] [commands...]
|
||||
: name [prerequisites] [commands ...]
|
||||
|
||||
`target-rule` (aka `:`) adds a target rule to the target rule
|
||||
list. There are 3 components
|
||||
|
||||
- NAME is a string that names the target. If this rule is being used
|
||||
to create a file, NAME is the name of the file to be output.
|
||||
|
||||
NAME can also be a predicate procedure that maps string->boolean.
|
||||
But if NAME is a procedure, this rule cannot be used at the
|
||||
top-level target of a build.
|
||||
- PREREQUISITES, if provided, is a list of strings or procedures of
|
||||
zero arguments that evaluate to strings. Each entry is the
|
||||
name of a target that needs to be exist before this target is
|
||||
attempted. It may be an empty list, indicating that there are no
|
||||
prerequisites.
|
||||
- COMMANDS, if provided, are recipes that will be executed that are
|
||||
intended to cause the target to be created. The recipe can be
|
||||
either a string or a procedure.
|
||||
|
||||
If the COMMAND recipe is a string, it will be passed to the `system`
|
||||
procedure for execution by the shell. If any call to system returns a
|
||||
non-zero return value, processing will end. (This behavior is modified
|
||||
by the `--ignore-errors` and `--continue-on-error` command-line
|
||||
arguments.)
|
||||
|
||||
If the COMMAND recipe is a procedure, it will be executed. If it
|
||||
returns `#f` or a non-zero integer, failure is assumed. If the
|
||||
COMMAND recipe returns a string, the resulting string is passed to
|
||||
`system` and is process as above.
|
||||
|
||||
If the COMMAND recipe is a pair, and the CAR of the pair is one of
|
||||
`'ignore-error`, `'silent`, or `'always-execute`, it will have the
|
||||
extra effect of ignoring errors, not printing the command line, or
|
||||
always executing even when the `--no-execution` option is enabled.
|
||||
The CDR must be a string or procedure as above.
|
||||
|
||||
There are a set of helper functions and variables that can be used to
|
||||
construct recipes.
|
||||
|
||||
string-compose element ...
|
||||
~ element ...
|
||||
ignore-error-compose element ...
|
||||
~- element ...
|
||||
silent-compose element ...
|
||||
~@ element ...
|
||||
always-execute-compose element ...
|
||||
~+ element ...
|
||||
|
||||
`string-compose` (aka `~`) takes as arguments one or more elements. It
|
||||
converts the elements to strings and concatenates the strings,
|
||||
appending spaces between them. The conversion to strings happens as if
|
||||
by `display`.
|
||||
|
||||
For elements that are procedures, they are executed and their result
|
||||
is used instead.
|
||||
|
||||
It is returned as a pair, where the `car` is the symbol `'default`.
|
||||
That symbol is interpreted by the builder.
|
||||
|
||||
`ignore-error-compose` (aka `~-`) is like string-compose but returns a
|
||||
pair with the first argument of `'ignore-error`. When passed as a
|
||||
recipe, it causes the recipe not to end execution, even if an error is
|
||||
signaled.
|
||||
|
||||
`silent-compose` (aka `~@`) is like string-compose, but, it does not
|
||||
print the resulting string to the output port, except with verbose output.
|
||||
|
||||
`always-execute-compose` (aka `~+`) is like compose, but, it forces
|
||||
the line to always be executed, even if the `--no-execution` option
|
||||
was chosen.
|
||||
|
||||
target-name
|
||||
$@
|
||||
|
||||
`target-name` (aka `$@`) is a global variable. If called from within the
|
||||
context of a recipe, it contains as a string the name of the target.
|
||||
target-name is not thread safe.
|
||||
|
||||
newer-prerequisites
|
||||
$?
|
||||
|
||||
`newer-prerequisites` (aka `$?`) returns the list of prerequisites that
|
||||
are newer than the target.
|
||||
|
||||
primary-prerequisite
|
||||
$<
|
||||
|
||||
`primary-prerequisite` (aka `$<`) returns the first prerequisite.
|
||||
|
||||
target-basename
|
||||
$*
|
||||
|
||||
`target-basename` (aka `$*`) returns the target with the suffix elided.
|
||||
|
||||
prerequisites
|
||||
$^
|
||||
|
||||
`prerequisites` (aka `$^`) return all the prerequisites.
|
||||
|
||||
%target-rule-list`
|
||||
|
||||
`%target-rule-list` is list of targets rules encountered in the build
|
||||
script in the order in which they were listed, converted into an
|
||||
internal format.
|
||||
|
||||
Here are some example target rules that with recipes meant to be
|
||||
executed by `system`.
|
||||
|
||||
(: "foo.o" '("foo.c" "foo.h")
|
||||
(~ "cc -o" $@ $<))
|
||||
|
||||
(: "clean" '()
|
||||
"rm *.o"
|
||||
"rm *~")
|
||||
|
||||
Target rules may take advantage of makevars.
|
||||
|
||||
(: "foo.o" '("foo.c" "foo.h")
|
||||
(~ ($ CC) ($ CFLAGS) "-o" $@ $<))
|
||||
|
||||
Target rules may also have recipes that execute scheme code
|
||||
|
||||
(: "clean" '()
|
||||
(lambda ()
|
||||
(delete-file "foo.o")))
|
||||
|
||||
### Suffix Rules
|
||||
|
||||
Unlike target rules which are for one specific target and may have
|
||||
multiple prerequisites, suffix rules describe how to create a target
|
||||
from a single prerequisite with the assumption that they have the same
|
||||
basename and differ only in the filename suffixes. The are applied to
|
||||
implicit prerequisites to other rules, or to explicit prerequisites to
|
||||
other rules that have no target rules defined.
|
||||
|
||||
For example, one could have a suffix rule to convert a `*.c` file into
|
||||
a `*.o` file. The syntax for suffix rules are similar to target rules
|
||||
above.
|
||||
|
||||
suffix-rule source-suffix target-suffix [commands...]
|
||||
-> source-suffix target-suffix [commands ...]
|
||||
|
||||
`suffix-rule` (aka `->` or `→`) adds a suffix rule to the suffix rule
|
||||
list. There are 3 components
|
||||
|
||||
- SOURCE-SUFFIX is a string that names the filename suffix of the file
|
||||
used to create the target. Commonly, this string begins with a
|
||||
period.
|
||||
|
||||
SOURCE-SUFFIX can also be a conversion procedure that takes
|
||||
in a target name string and converts it into a source name string.
|
||||
|
||||
- TARGET-SUFFIX, is a string that is the filename suffix of the file
|
||||
to be created. The TARGET-SUFFIX could be an empty string,
|
||||
indicating that the target is just the basename with no suffix.
|
||||
|
||||
TARGET-SUFFIX can also be a predicate procedure that takes in a
|
||||
potential target name string and returns `#t` or `#f` if the target
|
||||
name string should be processed with this suffix rule.
|
||||
|
||||
- COMMANDS, if provided, are recipes that will be executed that are
|
||||
intended to cause the target to be created. The recipe can be
|
||||
either a string or a procedure.
|
||||
|
||||
If the COMMAND recipe is a string, it will be passed to the `system`
|
||||
procedure for execution by the shell. If any call to system returns a
|
||||
non-zero return value, ending processing.
|
||||
|
||||
If the COMMAND recipe is a procedure, it will be executed. If it
|
||||
returns #f or a non-zero integer, failure is assumed. If the COMMAND
|
||||
recipe returns a string, the resulting string is passed to `system`
|
||||
and is process as above.
|
||||
|
||||
%suffix-rule-list
|
||||
|
||||
`%suffix-rule-list` is list of suffix rules encountered in the build
|
||||
script in the order in which they were listed, converted into an
|
||||
internal format.
|
||||
|
||||
Example suffix rules are
|
||||
|
||||
(-> ".c" ".o"
|
||||
(~ ($ CC) ($ CFLAGS) "-c" "-o" $@ $<))
|
||||
|
||||
(-> ".sh" ""
|
||||
(~ "cp" $< $@)
|
||||
(~ "chmod a+x" $@))
|
||||
|
||||
|
||||
## makevars
|
||||
|
||||
Makefile scripts may take advantage of a special variable type
|
||||
called a makevar. In scheme terms, makevars are entries in a
|
||||
`%makevars` hash table that have special accessor syntax.
|
||||
|
||||
- The makevar names -- the keys -- are strings.
|
||||
- The makevar values are either strings or procedures that take no
|
||||
arguments that return strings.
|
||||
|
||||
There are five ways a makevar can be initialized.
|
||||
|
||||
1. Set directly in the script using the `?=` or `:=` syntax.
|
||||
2. Set in command-line arguments
|
||||
3. Extracted from the `MAKEFLAGS` environment variable
|
||||
4. Generated from the environment variables
|
||||
5. Or be one of the predefined variables built into this library
|
||||
|
||||
There is a priority to makevars. The variables from category five
|
||||
above are set first, then four, then three, etc. Each lower category
|
||||
may overwrite variables set in the higher category.
|
||||
|
||||
This priority is modified by the `-e` command-line argument. If `-e`
|
||||
is set, category 1 variables *do not* override variables from categories
|
||||
2, 3, and 4. They *do* override variables set in category 5.
|
||||
|
||||
The library provides the following procedures for makevars
|
||||
|
||||
lazy-assign key [val]
|
||||
|
||||
> `lazy-assign` sets a entry in the makevars hash table. KEY must be
|
||||
> a string or a thunk that evaluates to a string. Likewise VAL must
|
||||
> be a string or a thunk that evaluates to a string.
|
||||
|
||||
> 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.
|
||||
|
||||
?= key [val]
|
||||
|
||||
> This is a syntax version of lazy-assign where KEY should be a string
|
||||
> without quotes, e.g.
|
||||
|
||||
(?= foo "bar") ==> (lazy-assign "foo" "bar")
|
||||
|
||||
assign key [val]
|
||||
|
||||
> `assign` is the same as `lazy-assign` above, except that if VAL is a
|
||||
> thunk it is immediately evaluated to a string and that string is
|
||||
> used as the value in the hash table entry.
|
||||
|
||||
:= key [val]
|
||||
|
||||
> This is a syntax version of `assign` where KEY should be a string
|
||||
> without quotes, e.g.
|
||||
|
||||
(:= foo "bar") ==> (assign "foo" "bar")
|
||||
|
||||
reference key [transformer]
|
||||
|
||||
> `reference` looks up KEY in the `%makevar` hash table. If it is
|
||||
> found, VALUE is returned as a string.
|
||||
|
||||
> *IMPORTANT!* If it is not found, an empty string is returned. This
|
||||
> is because it is a common practice in makefiles to use makevars that
|
||||
> may or may not be defined by environment variables. With verbose output,
|
||||
> a warning will be printed when a key cannot be found.
|
||||
|
||||
> If the value was stored using `lazy-assign` and is a *promise*, this
|
||||
> procedure is *forced* to return a string. Also, the value in the
|
||||
> hash table is updated to this string.
|
||||
|
||||
> The optional `transfomer` should be a function the takes a string
|
||||
> and returns a string. It will be applied to every space-separated
|
||||
> word in the value.
|
||||
|
||||
$ key
|
||||
|
||||
> This is a syntax version of `reference`, where KEY should be a
|
||||
> string without quotes, e.g.
|
||||
|
||||
($ key) ==> (reference "key")
|
||||
|
||||
reference-func key
|
||||
|
||||
> `reference-func` returns a procedure of zero arguments that will,
|
||||
> when called, look up a key as described in `reference` above.
|
||||
|
||||
$$ key
|
||||
|
||||
> This is a syntax version of reference-func, where KEY should be a
|
||||
> string without quotes, e.g.
|
||||
|
||||
($$ key) ==> (reference-func "key")
|
||||
|
||||
%makevars
|
||||
|
||||
> This is the hash table. You are not meant to access it directly,
|
||||
> but, with the functions above. If you do use it directly, the VALUE
|
||||
> is a cons where the CAR is string or promise and the CDR is private
|
||||
> data.
|
||||
|
||||
## The build algorithm
|
||||
|
||||
The initial target is given on the command line. If no target was
|
||||
given on the command line, the first entry in the target list is used.
|
||||
|
||||
For each top-level target, create a n-ary tree of prerequisites. If a
|
||||
target doesn't have an explicit rule, but has a suffix that appears in
|
||||
one or more suffix rules, it searches for possible prerequisites that
|
||||
would fulfill a suffix rule. Continue until the tree is populated.
|
||||
|
||||
Then for each node, try to compute timestamps for each target, if they
|
||||
exist.
|
||||
|
||||
Mark as 'skip' each node that is a real file that is older than the
|
||||
parent file.
|
||||
|
||||
In a depth-first search, build each node unless the node target is
|
||||
older than the parent.
|
||||
|
||||
If a build recipe fails...
|
||||
If '--ignore-errors', mark current node as 'skip', then keep going.
|
||||
If '--continue-on-error', mark all siblings as 'skip', and mark the direct ancestors 'skip', keep
|
||||
going.
|
||||
Else, quit.
|
||||
|
||||
If we're not quit, once reaching the end, start with the next
|
||||
top-level target (which only happens is multiple targets are given in
|
||||
the command line).
|
||||
|
||||
## Built-in rules and makevars
|
||||
|
||||
If the `--builtins` option is given, there are some builtin suffix rules
|
||||
and *makevars* that are present by default. These include the following.
|
||||
You can add more builtins by updating the potato/builtins.scm file.
|
||||
|
||||
MAKE=make
|
||||
AR=ar
|
||||
ARFLAGS=-rv
|
||||
YACC=yacc
|
||||
YFLAGS=
|
||||
LEX=lex
|
||||
LFLAGS=
|
||||
LDFLAGS=
|
||||
CC=gcc
|
||||
CFLAGS=-g -O2
|
||||
FC=gfortran
|
||||
FFLAGS=-g -O2
|
||||
|
||||
(-> ".c" ".o"
|
||||
(~ ($ CC) ($ CFLAGS) "-c" $<)))
|
||||
|
||||
(-> ".f90" ".o"
|
||||
(~ ($ FC) ($ FFLAGS) "-c" $<))
|
||||
|
||||
(-> ".y" ".o"
|
||||
(~ ($ YACC) ($ YFLAGS) $<)
|
||||
(~ ($ CC) ($ CFLAGS) "-c y.tab.c")
|
||||
"rm -f y.tab.c"
|
||||
(~ "mv y.tab.o" $@))
|
||||
|
||||
(-> ".l" ".o"
|
||||
(~ ($ LEX) ($ LFLAGS) $<)
|
||||
(~ ($ CC) ($ CFLAGS) "-c lex.yy.c")
|
||||
"rm -f lex.yy.c"
|
||||
(~ "mv lex.yy.o" $@))
|
||||
|
||||
(-> ".y" ".c"
|
||||
(~ ($ YACC) ($ YFLAGS) $<)
|
||||
(~ "mv y.tab.c" $@))
|
||||
|
||||
(-> ".l" ".c"
|
||||
(~ ($ LEX) ($ LFLAGS) $<)
|
||||
(~ "mv lex.yy.c" $@))
|
||||
|
||||
## Debug commands
|
||||
|
||||
These commands modify how rules are interpreted or executed
|
||||
|
||||
FIXME
|
535
README.md
535
README.md
@ -1,40 +1,13 @@
|
||||
# POTATO MAKE
|
||||
|
||||
A build tool written in Guile Scheme.
|
||||
|
||||
## Description
|
||||
# CHEATSHEET FOR POTATO MAKE
|
||||
|
||||
Potato Make is a scheme library that aims to simplify the task of
|
||||
maintaining, updating, and regenerating programs. It is inspired by
|
||||
the `make` utility in POSIX. With this library, you can write a
|
||||
build script in Guile Scheme.
|
||||
|
||||
Like POSIX `make`, these scripts update files that are derived from
|
||||
other files. The makefile script typically will describe how a file is
|
||||
built from shell commands, and it will describe the relationships
|
||||
between components to be built, so that they are built in order. In a
|
||||
typical script, the script will check the prerequisites to a target,
|
||||
and if the prerequisites are newer than the target, it will rebuild
|
||||
the target.
|
||||
## Boilerplate
|
||||
|
||||
There are two types of rules that a makefile script can contain.
|
||||
|
||||
1. Target rules, which describe how a specific named file is to be
|
||||
built from prerequisites using a set of shell commands.
|
||||
2. Suffix rules, which generically describe how to convert files with
|
||||
one filename suffix into files with another filename suffix.
|
||||
|
||||
The makefile script will make use of a custom variable type which can
|
||||
be set either in the script, by environment variables, or in command
|
||||
line arguments. Let's call them *makevars*, to reduce confusion with
|
||||
standard scheme variables.
|
||||
|
||||
## Setting up the Scheme Script
|
||||
|
||||
To write a build script with this library, one needs to add the
|
||||
following boilerplate code at the top of an executable scheme script.
|
||||
Throughout this documentation we will presume that this scheme script
|
||||
is named `makefile.scm`; however, you may choose any name.
|
||||
Add this at the top of your build script.
|
||||
|
||||
#!/usr/bin/env sh
|
||||
exec guile -s "$0" "$@"
|
||||
@ -43,10 +16,16 @@ is named `makefile.scm`; however, you may choose any name.
|
||||
(use-modules (potato make))
|
||||
(initialize)
|
||||
|
||||
Add this at the bottom of your build script
|
||||
|
||||
(execute)
|
||||
|
||||
The rules go in between `initialize` and `build`
|
||||
|
||||
This boilerplate loads the library functions and it parses the
|
||||
command-line arguments. The command-line arguments are the following,
|
||||
|
||||
makefile.scm [-hvqVeEbknB] [var=value...] [target_name...]
|
||||
<your-script-name> [-hvqVeEbknB] [var=value...] [target_name...]
|
||||
-h, --help
|
||||
displays help
|
||||
-v, --version
|
||||
@ -60,16 +39,16 @@ command-line arguments. The command-line arguments are the following,
|
||||
and will override makevars set in the script
|
||||
-b, --builtins
|
||||
adds some default makevars and suffix rules
|
||||
--ignore-errors
|
||||
--ignore-errors [NOT IMPLEMENTED YET]
|
||||
keep building even if a command fails
|
||||
-k, --continue-on-error
|
||||
-k, --continue-on-error [NOT IMPLEMENTED YET]
|
||||
keep building some targets even if a command fails
|
||||
-n, --no-execute
|
||||
-n, --no-execute [NOT IMPLEMENTED YET]
|
||||
print rules, but only execute rules marked as
|
||||
'always execute'
|
||||
-a, --ascii
|
||||
use ASCII-only output and no colors
|
||||
-W, --warn
|
||||
-W, --warn [NOT IMPLEMENTED YET]
|
||||
enable warning messages
|
||||
|
||||
[var=value...]
|
||||
@ -78,433 +57,133 @@ command-line arguments. The command-line arguments are the following,
|
||||
Set one or more targets to be executed. If no target
|
||||
is specified, the first target found will be executed.
|
||||
|
||||
Note that in POSIX `make`, it, by default, adds in environment
|
||||
variables and built-in rules. With this library, these require
|
||||
command-line arguments to be enabled to pick up environment variables
|
||||
and built-in rules. This is to make this tool more appropriate for
|
||||
generating *reproducible builds*.
|
||||
## MAKEVARS
|
||||
|
||||
If you don't want `initialize` to parse the command line, you may call
|
||||
it with specific command line arguments, like the example below. The
|
||||
first string is the name of the script, and then any combination of
|
||||
flags, macro assignments and targets may follow.
|
||||
A hash table called `%makevars` has string keys. These procedures
|
||||
are syntax that add quotation marks around `key`, so you call them without the quotes on
|
||||
`key`. The returned value of `$` is a string, or an empty string on failure.
|
||||
|
||||
(initialize '("makefile.scm" "--verbosity=3" "CC=gcc" "all"))
|
||||
($ KEY) -> "VAL"
|
||||
|
||||
If you call initialize with an empty list as below, it will guess the
|
||||
script name from the command-line arguements, but, will ignore all
|
||||
other flags and options.
|
||||
($ key [transformer])
|
||||
Look up `key` in the `%makevars` hash table and return the
|
||||
result as a string. If `key` is not found, return an empty
|
||||
string. If a string-to-string transformer procedure is
|
||||
provided, apply it to each space-separated token in the
|
||||
result.
|
||||
|
||||
;; ignore all command line arguments except the script name
|
||||
(initialize '())
|
||||
(?= key val)
|
||||
Assign `val` to `key` in the `%makevars` hash table. If `val`
|
||||
is a procedure, assign its output to `key` the first time that
|
||||
`key` is referenced.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Certain environment variables affect the execution of the makefile
|
||||
script.
|
||||
|
||||
`LANG` - affects the current locale
|
||||
|
||||
`MAKEFLAGS` - This will be parsed similar to command-line arguments.
|
||||
If it contains the single, space-separated letters 'e', 'f', 'i',
|
||||
'k', 'n', 'p', 'r', or 's', those options will be enabled as if
|
||||
set on the command line. If it contains strings of the form
|
||||
VAR=VALUE, it will set those makevars.
|
||||
|
||||
`SHELL` - The shell environment variable is always ignored.
|
||||
|
||||
All other environment variables, including those with null values,
|
||||
shall initialize makevars.
|
||||
|
||||
## Signals
|
||||
|
||||
`SIGHUP`, `SIGTERM`, `SIGINT`, and `SIGQUIT` shall interrupt any
|
||||
processing.
|
||||
(:= key val)
|
||||
Assign `val` to `key` in the `%makevars` hash table. If `val`
|
||||
is a procedure, evaluate it and assign its output to `key`
|
||||
immediately.
|
||||
|
||||
## Rules
|
||||
|
||||
The purpose of a makefile script is to run rules, which describe how
|
||||
programs act on prerequisites to create targets. There are two types
|
||||
of rules: target rules and suffix rules.
|
||||
The *target rule* is for when the target, and the prerequisites, if any,
|
||||
have filenames or phony names.
|
||||
|
||||
### Target Rules
|
||||
(: target-name '(prereq-name-1 prereq-name-2 ...)
|
||||
recipe-1
|
||||
recipe-2
|
||||
...)
|
||||
|
||||
Target rules are defined and manipulated with the following commands.
|
||||
`target-name` is a string which is either a filename to be
|
||||
created or an phony name like "all" or "clean".
|
||||
|
||||
target-rule name [prerequisites] [commands...]
|
||||
: name [prerequisites] [commands ...]
|
||||
Recipe as a string to be evaluated by the system
|
||||
|
||||
`target-rule` (aka `:`) adds a target rule to the target rule
|
||||
list. There are 3 components
|
||||
(: "foo.o" '("foo.c")
|
||||
"cc -c foo.o")
|
||||
|
||||
- NAME is a string that names the target. If this rule is being used
|
||||
to create a file, NAME is the name of the file to be output.
|
||||
Recipe as a procedure
|
||||
|
||||
NAME can also be a predicate procedure that maps string->boolean.
|
||||
But if NAME is a procedure, this rule cannot be used at the
|
||||
top-level target of a build.
|
||||
- PREREQUISITES, if provided, is a list of strings or procedures of
|
||||
zero arguments that evaluate to strings. Each entry is the
|
||||
name of a target that needs to be exist before this target is
|
||||
attempted. It may be an empty list, indicating that there are no
|
||||
prerequisites.
|
||||
- COMMANDS, if provided, are recipes that will be executed that are
|
||||
intended to cause the target to be created. The recipe can be
|
||||
either a string or a procedure.
|
||||
(: "clean-foo" '()
|
||||
(lambda ()
|
||||
(delete-file "foo.o")))
|
||||
|
||||
If the COMMAND recipe is a string, it will be passed to the `system`
|
||||
procedure for execution by the shell. If any call to system returns a
|
||||
non-zero return value, processing will end. (This behavior is modified
|
||||
by the `--ignore-errors` and `--continue-on-error` command-line
|
||||
arguments.)
|
||||
Recipe as a procedure that returns #f to indicate failure
|
||||
|
||||
If the COMMAND recipe is a procedure, it will be executed. If it
|
||||
returns `#f` or a non-zero integer, failure is assumed. If the
|
||||
COMMAND recipe returns a string, the resulting string is passed to
|
||||
`system` and is process as above.
|
||||
(: "recent" '()
|
||||
(lambda ()
|
||||
(if condition
|
||||
#t
|
||||
#f))))
|
||||
|
||||
If the COMMAND recipe is a pair, and the CAR of the pair is one of
|
||||
`'ignore-error`, `'silent`, or `'always-execute`, it will have the
|
||||
extra effect of ignoring errors, not printing the command line, or
|
||||
always executing even when the `--no-execution` option is enabled.
|
||||
The CDR must be a string or procedure as above.
|
||||
Recipe as a procedure returning a string to be evaluated by the
|
||||
system
|
||||
|
||||
There are a set of helper functions and variables that can be used to
|
||||
construct recipes.
|
||||
(: "foo.o" '("foo.c")
|
||||
(lambda ()
|
||||
(format #f "cc ~A -c foo.c" some-flags))
|
||||
|
||||
string-compose element ...
|
||||
~ element ...
|
||||
ignore-error-compose element ...
|
||||
~- element ...
|
||||
silent-compose element ...
|
||||
~@ element ...
|
||||
always-execute-compose element ...
|
||||
~+ element ...
|
||||
Recipe using recipe helper procedures, which create a string to
|
||||
be evaluated by the system
|
||||
|
||||
`string-compose` (aka `~`) takes as arguments one or more elements. It
|
||||
converts the elements to strings and concatenates the strings,
|
||||
appending spaces between them. The conversion to strings happens as if
|
||||
by `display`.
|
||||
(: "foo.c" '("foo.c")
|
||||
(~ ($ CC) ($ CFLAGS) "-c" $<))
|
||||
|
||||
For elements that are procedures, they are executed and their result
|
||||
is used instead.
|
||||
Recipe as a boolean to indicate pass or failure without doing any
|
||||
processing. For example, the rule below tells Potato Make that
|
||||
the file "foo.c" exists without actually testing for it.
|
||||
|
||||
It is returned as a pair, where the `car` is the symbol `'default`.
|
||||
That symbol is interpreted by the builder.
|
||||
(: "foo.c" '() #t)
|
||||
|
||||
`ignore-error-compose` (aka `~-`) is like string-compose but returns a
|
||||
pair with the first argument of `'ignore-error`. When passed as a
|
||||
recipe, it causes the recipe not to end execution, even if an error is
|
||||
signaled.
|
||||
If there is no recipe at all, it is shorthand for the recipe #t,
|
||||
indicating a recipe that always passes. This is used
|
||||
in prerequisite-only target rules, such as below, which passes
|
||||
so long as the prerequisites
|
||||
pass. These two rules are the same.
|
||||
|
||||
`silent-compose` (aka `~@`) is like string-compose, but, it does not
|
||||
print the resulting string to the output port, except with verbose output.
|
||||
(: "all" '("foo.exe"))
|
||||
(: "all" '("foo.exe") #t)
|
||||
|
||||
`always-execute-compose` (aka `~+`) is like compose, but, it forces
|
||||
the line to always be executed, even if the `--no-execution` option
|
||||
was chosen.
|
||||
Lastly, if the recipe is #f, this target will always fail.
|
||||
|
||||
target-name
|
||||
$@
|
||||
(: "fail" '() #f)
|
||||
|
||||
`target-name` (aka `$@`) is a global variable. If called from within the
|
||||
context of a recipe, it contains as a string the name of the target.
|
||||
target-name is not thread safe.
|
||||
The *suffix rule* is a generic rule to convert one source file to a
|
||||
target file, based on the filename extensions.
|
||||
|
||||
newer-prerequisites
|
||||
$?
|
||||
(-> ".c" ".o"
|
||||
(~ ($ CC) ($ CFLAGS) "-c" $< "-o" $@))
|
||||
|
||||
`newer-prerequisites` (aka `$?`) returns the list of prerequisites that
|
||||
are newer than the target.
|
||||
## Recipe Helpers
|
||||
|
||||
primary-prerequisite
|
||||
$<
|
||||
Concatenate elements with `~`. `~` inserts spaces between the
|
||||
elements.
|
||||
|
||||
`primary-prerequisite` (aka `$<`) returns the first prerequisite.
|
||||
Elements can be
|
||||
- strings
|
||||
- procedures that return strings
|
||||
- `%makevar` hash-table references
|
||||
- automatic variables
|
||||
- anything whose string representation as created by
|
||||
(format #f "~A" ...) make sense
|
||||
|
||||
target-basename
|
||||
$*
|
||||
Any procedures are applied lazily, when the rule is executed.
|
||||
|
||||
`target-basename` (aka `$*`) returns the target with the suffix elided.
|
||||
(~ "string" (lambda () "string") ($ KEY) $@ 100 )
|
||||
|
||||
prerequisites
|
||||
$^
|
||||
Three versions of `~` with special effects
|
||||
(~- ...) ignores any errors
|
||||
(~@ ...) doesn't print recipe to console
|
||||
(~+ ...) runs even when `--no-execute` was chosen
|
||||
|
||||
`prerequisites` (aka `$^`) return all the prerequisites.
|
||||
## Automatic Variables
|
||||
|
||||
%target-rule-list`
|
||||
Recipes can contain the following automatic variables
|
||||
|
||||
`%target-rule-list` is list of targets rules encountered in the build
|
||||
script in the order in which they were listed, converted into an
|
||||
internal format.
|
||||
|
||||
Here are some example target rules that with recipes meant to be
|
||||
executed by `system`.
|
||||
|
||||
(: "foo.o" '("foo.c" "foo.h")
|
||||
(~ "cc -o" $@ $<))
|
||||
|
||||
(: "clean" '()
|
||||
"rm *.o"
|
||||
"rm *~")
|
||||
|
||||
Target rules may take advantage of makevars.
|
||||
|
||||
(: "foo.o" '("foo.c" "foo.h")
|
||||
(~ ($ CC) ($ CFLAGS) "-o" $@ $<))
|
||||
|
||||
Target rules may also have recipes that execute scheme code
|
||||
|
||||
(: "clean" '()
|
||||
(lambda ()
|
||||
(delete-file "foo.o")))
|
||||
|
||||
### Suffix Rules
|
||||
|
||||
Unlike target rules which are for one specific target and may have
|
||||
multiple prerequisites, suffix rules describe how to create a target
|
||||
from a single prerequisite with the assumption that they have the same
|
||||
basename and differ only in the filename suffixes. The are applied to
|
||||
implicit prerequisites to other rules, or to explicit prerequisites to
|
||||
other rules that have no target rules defined.
|
||||
|
||||
For example, one could have a suffix rule to convert a `*.c` file into
|
||||
a `*.o` file. The syntax for suffix rules are similar to target rules
|
||||
above.
|
||||
|
||||
suffix-rule source-suffix target-suffix [commands...]
|
||||
-> source-suffix target-suffix [commands ...]
|
||||
|
||||
`suffix-rule` (aka `->` or `→`) adds a suffix rule to the suffix rule
|
||||
list. There are 3 components
|
||||
|
||||
- SOURCE-SUFFIX is a string that names the filename suffix of the file
|
||||
used to create the target. Commonly, this string begins with a
|
||||
period.
|
||||
|
||||
SOURCE-SUFFIX can also be a conversion procedure that takes
|
||||
in a target name string and converts it into a source name string.
|
||||
|
||||
- TARGET-SUFFIX, is a string that is the filename suffix of the file
|
||||
to be created. The TARGET-SUFFIX could be an empty string,
|
||||
indicating that the target is just the basename with no suffix.
|
||||
|
||||
TARGET-SUFFIX can also be a predicate procedure that takes in a
|
||||
potential target name string and returns `#t` or `#f` if the target
|
||||
name string should be processed with this suffix rule.
|
||||
|
||||
- COMMANDS, if provided, are recipes that will be executed that are
|
||||
intended to cause the target to be created. The recipe can be
|
||||
either a string or a procedure.
|
||||
|
||||
If the COMMAND recipe is a string, it will be passed to the `system`
|
||||
procedure for execution by the shell. If any call to system returns a
|
||||
non-zero return value, ending processing.
|
||||
|
||||
If the COMMAND recipe is a procedure, it will be executed. If it
|
||||
returns #f or a non-zero integer, failure is assumed. If the COMMAND
|
||||
recipe returns a string, the resulting string is passed to `system`
|
||||
and is process as above.
|
||||
|
||||
%suffix-rule-list
|
||||
|
||||
`%suffix-rule-list` is list of suffix rules encountered in the build
|
||||
script in the order in which they were listed, converted into an
|
||||
internal format.
|
||||
|
||||
Example suffix rules are
|
||||
|
||||
(-> ".c" ".o"
|
||||
(~ ($ CC) ($ CFLAGS) "-c" "-o" $@ $<))
|
||||
|
||||
(-> ".sh" ""
|
||||
(~ "cp" $< $@)
|
||||
(~ "chmod a+x" $@))
|
||||
|
||||
|
||||
## makevars
|
||||
|
||||
Makefile scripts may take advantage of a special variable type
|
||||
called a makevar. In scheme terms, makevars are entries in a
|
||||
`%makevars` hash table that have special accessor syntax.
|
||||
|
||||
- The makevar names -- the keys -- are strings.
|
||||
- The makevar values are either strings or procedures that take no
|
||||
arguments that return strings.
|
||||
|
||||
There are five ways a makevar can be initialized.
|
||||
|
||||
1. Set directly in the script using the `?=` or `:=` syntax.
|
||||
2. Set in command-line arguments
|
||||
3. Extracted from the `MAKEFLAGS` environment variable
|
||||
4. Generated from the environment variables
|
||||
5. Or be one of the predefined variables built into this library
|
||||
|
||||
There is a priority to makevars. The variables from category five
|
||||
above are set first, then four, then three, etc. Each lower category
|
||||
may overwrite variables set in the higher category.
|
||||
|
||||
This priority is modified by the `-e` command-line argument. If `-e`
|
||||
is set, category 1 variables *do not* override variables from categories
|
||||
2, 3, and 4. They *do* override variables set in category 5.
|
||||
|
||||
The library provides the following procedures for makevars
|
||||
|
||||
lazy-assign key [val]
|
||||
|
||||
> `lazy-assign` sets a entry in the makevars hash table. KEY must be
|
||||
> a string or a thunk that evaluates to a string. Likewise VAL must
|
||||
> be a string or a thunk that evaluates to a string.
|
||||
|
||||
> 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.
|
||||
|
||||
?= key [val]
|
||||
|
||||
> This is a syntax version of lazy-assign where KEY should be a string
|
||||
> without quotes, e.g.
|
||||
|
||||
(?= foo "bar") ==> (lazy-assign "foo" "bar")
|
||||
|
||||
assign key [val]
|
||||
|
||||
> `assign` is the same as `lazy-assign` above, except that if VAL is a
|
||||
> thunk it is immediately evaluated to a string and that string is
|
||||
> used as the value in the hash table entry.
|
||||
|
||||
:= key [val]
|
||||
|
||||
> This is a syntax version of `assign` where KEY should be a string
|
||||
> without quotes, e.g.
|
||||
|
||||
(:= foo "bar") ==> (assign "foo" "bar")
|
||||
|
||||
reference key [transformer]
|
||||
|
||||
> `reference` looks up KEY in the `%makevar` hash table. If it is
|
||||
> found, VALUE is returned as a string.
|
||||
|
||||
> *IMPORTANT!* If it is not found, an empty string is returned. This
|
||||
> is because it is a common practice in makefiles to use makevars that
|
||||
> may or may not be defined by environment variables. With verbose output,
|
||||
> a warning will be printed when a key cannot be found.
|
||||
|
||||
> If the value was stored using `lazy-assign` and is a *promise*, this
|
||||
> procedure is *forced* to return a string. Also, the value in the
|
||||
> hash table is updated to this string.
|
||||
|
||||
> The optional `transfomer` should be a function the takes a string
|
||||
> and returns a string. It will be applied to every space-separated
|
||||
> word in the value.
|
||||
|
||||
$ key
|
||||
|
||||
> This is a syntax version of `reference`, where KEY should be a
|
||||
> string without quotes, e.g.
|
||||
|
||||
($ key) ==> (reference "key")
|
||||
|
||||
reference-func key
|
||||
|
||||
> `reference-func` returns a procedure of zero arguments that will,
|
||||
> when called, look up a key as described in `reference` above.
|
||||
|
||||
$$ key
|
||||
|
||||
> This is a syntax version of reference-func, where KEY should be a
|
||||
> string without quotes, e.g.
|
||||
|
||||
($$ key) ==> (reference-func "key")
|
||||
|
||||
%makevars
|
||||
|
||||
> This is the hash table. You are not meant to access it directly,
|
||||
> but, with the functions above. If you do use it directly, the VALUE
|
||||
> is a cons where the CAR is string or promise and the CDR is private
|
||||
> data.
|
||||
|
||||
## The build algorithm
|
||||
|
||||
The initial target is given on the command line. If no target was
|
||||
given on the command line, the first entry in the target list is used.
|
||||
|
||||
For each top-level target, create a n-ary tree of prerequisites. If a
|
||||
target doesn't have an explicit rule, but has a suffix that appears in
|
||||
one or more suffix rules, it searches for possible prerequisites that
|
||||
would fulfill a suffix rule. Continue until the tree is populated.
|
||||
|
||||
Then for each node, try to compute timestamps for each target, if they
|
||||
exist.
|
||||
|
||||
Mark as 'skip' each node that is a real file that is older than the
|
||||
parent file.
|
||||
|
||||
In a depth-first search, build each node unless the node target is
|
||||
older than the parent.
|
||||
|
||||
If a build recipe fails...
|
||||
If '--ignore-errors', mark current node as 'skip', then keep going.
|
||||
If '--continue-on-error', mark all siblings as 'skip', and mark the direct ancestors 'skip', keep
|
||||
going.
|
||||
Else, quit.
|
||||
|
||||
If we're not quit, once reaching the end, start with the next
|
||||
top-level target (which only happens is multiple targets are given in
|
||||
the command line).
|
||||
|
||||
## Built-in rules and makevars
|
||||
|
||||
If the `--builtins` option is given, there are some builtin suffix rules
|
||||
and *makevars* that are present by default. These include the following.
|
||||
You can add more builtins by updating the potato/builtins.scm file.
|
||||
|
||||
MAKE=make
|
||||
AR=ar
|
||||
ARFLAGS=-rv
|
||||
YACC=yacc
|
||||
YFLAGS=
|
||||
LEX=lex
|
||||
LFLAGS=
|
||||
LDFLAGS=
|
||||
CC=gcc
|
||||
CFLAGS=-g -O2
|
||||
FC=gfortran
|
||||
FFLAGS=-g -O2
|
||||
|
||||
(-> ".c" ".o"
|
||||
(~ ($ CC) ($ CFLAGS) "-c" $<)))
|
||||
|
||||
(-> ".f90" ".o"
|
||||
(~ ($ FC) ($ FFLAGS) "-c" $<))
|
||||
|
||||
(-> ".y" ".o"
|
||||
(~ ($ YACC) ($ YFLAGS) $<)
|
||||
(~ ($ CC) ($ CFLAGS) "-c y.tab.c")
|
||||
"rm -f y.tab.c"
|
||||
(~ "mv y.tab.o" $@))
|
||||
|
||||
(-> ".l" ".o"
|
||||
(~ ($ LEX) ($ LFLAGS) $<)
|
||||
(~ ($ CC) ($ CFLAGS) "-c lex.yy.c")
|
||||
"rm -f lex.yy.c"
|
||||
(~ "mv lex.yy.o" $@))
|
||||
|
||||
(-> ".y" ".c"
|
||||
(~ ($ YACC) ($ YFLAGS) $<)
|
||||
(~ "mv y.tab.c" $@))
|
||||
|
||||
(-> ".l" ".c"
|
||||
(~ ($ LEX) ($ LFLAGS) $<)
|
||||
(~ "mv lex.yy.c" $@))
|
||||
|
||||
## Debug commands
|
||||
|
||||
These commands modify how rules are interpreted or executed
|
||||
|
||||
FIXME
|
||||
$@ the target
|
||||
$* the target w/o a filename suffix
|
||||
$< the first prerequisite
|
||||
$^ the prerequisites, as a single space-separated string
|
||||
$$^ the prerequisites, as a scheme list of strings
|
||||
$? the prerequisites that are files newer than the target file
|
||||
as a single space-separated string
|
||||
$$? the prerequisites that are files newer than the target file
|
||||
as a scheme list of strings
|
||||
|
15
examples/simple.scm
Executable file
15
examples/simple.scm
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env sh
|
||||
exec guile -s "$0" "$@"
|
||||
!#
|
||||
|
||||
(use-modules (potato make))
|
||||
(initialize)
|
||||
(:= CC "gcc")
|
||||
(:= CFLAGS "-g -O2")
|
||||
|
||||
(: "all" '("foo"))
|
||||
(: "foo" '("foo.o" "bar.o")
|
||||
(~ ($ CC) "-o" $@ $^))
|
||||
(-> ".c" ".o"
|
||||
(~ ($ CC) "-c" $<))
|
||||
(execute)
|
@ -24,6 +24,8 @@
|
||||
;; 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.
|
||||
|
||||
(define %level-name '("unknown" "script" "command-line" "makeflags" "environment" "built-in"))
|
||||
|
||||
(define %ascii? #f)
|
||||
(define %makevars (make-hash-table))
|
||||
(define %elevate-environment? #f)
|
||||
@ -73,11 +75,12 @@ priority."
|
||||
(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)))
|
||||
(if (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 (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))))
|
||||
*unspecified*)
|
||||
|
||||
(define (makevars-add-keyvals keyvals)
|
||||
@ -128,6 +131,45 @@ the value of MAKEFLAGS or SHELL."
|
||||
(makevars-set (car keyval) (cdr keyval) 5))
|
||||
builtin-makevars))
|
||||
|
||||
(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)))))
|
||||
|
||||
(define (dump-makevars)
|
||||
"Write out a list of the current makevars."
|
||||
(when (not (zero? (hash-count (const #t) %makevars)))
|
||||
@ -199,9 +241,7 @@ the value of MAKEFLAGS or SHELL."
|
||||
(when (or environment? elevate-environment?)
|
||||
(makevars-add-environment)
|
||||
(makevars-add-makeflags))
|
||||
(makevars-add-keyvals keyvals)
|
||||
(when %verbose?
|
||||
(dump-makevars)))
|
||||
(makevars-add-keyvals keyvals))
|
||||
|
||||
;; API
|
||||
(define* (lazy-assign key #:optional (val ""))
|
||||
@ -218,9 +258,7 @@ referenced.
|
||||
(set! key (key)))
|
||||
(unless (string? key)
|
||||
(set! key (format #f "~a" key)))
|
||||
(makevars-set key (delay val))
|
||||
(when %verbose?
|
||||
(format #t "~A=~A~%" key val)))
|
||||
(makevars-set key (delay val)))
|
||||
|
||||
(define-syntax ?=
|
||||
(lambda (stx)
|
||||
@ -245,9 +283,7 @@ string to use as the key in the hash table entry.
|
||||
(set! val (val)))
|
||||
(unless (string? val)
|
||||
(set! val (format #f "~a" val)))
|
||||
(makevars-set key val)
|
||||
(when %verbose?
|
||||
(format #t "~A=~A~%" key val)))
|
||||
(makevars-set key val))
|
||||
|
||||
(define-syntax :=
|
||||
(lambda (stx)
|
||||
@ -283,7 +319,7 @@ space-separated token in the looked-up value."
|
||||
(priority (if (pair? val&priority) (cdr val&priority) #f)))
|
||||
(if (not val)
|
||||
(if %strict
|
||||
(error (format #t "There is no makevar for key ~a~%" key))
|
||||
(error (format #t "There is no makevar for key ~a~%~!" key))
|
||||
;; else
|
||||
(if quoted?
|
||||
"\"\""
|
||||
@ -307,8 +343,7 @@ space-separated token in the looked-up value."
|
||||
(else
|
||||
(set! val (format #f "~a" val))))
|
||||
(hash-set! %makevars key (cons val priority))
|
||||
(when %verbose?
|
||||
(format #t "~A=~A~%" key val))
|
||||
(when %verbose? (print-makevar key))
|
||||
(when (procedure? transformer)
|
||||
(set! val (string-append-with-spaces
|
||||
(map transformer
|
||||
|
198
potato/rules.scm
198
potato/rules.scm
@ -165,12 +165,10 @@ installs it as the system driver. Returns the old system driver."
|
||||
(define* (target-rule name #:optional (prerequisites '()) #:rest recipes)
|
||||
"Register a new target rule"
|
||||
|
||||
(format #t "BLAMMO!! ~S~%" recipes)
|
||||
|
||||
(when (>= %verbosity 0)
|
||||
(when (>= %verbosity 3)
|
||||
(if (null? prerequisites)
|
||||
(format #t "Defining target rule: ~a~A~a~%" (lquo) name (rquo))
|
||||
(format #t "Defining target rule: ~a~A~a ~A ~A~%" (lquo) name (rquo) (left-arrow) prerequisites)))
|
||||
(format #t "Target rule: ~a~A~a~%~!" (lquo) name (rquo))
|
||||
(format #t "Target rule: ~a~A~a ~A ~A~%~!" (lquo) name (rquo) (left-arrow) prerequisites)))
|
||||
|
||||
;; Empty recipes is shorthand for a recipe that always passes.
|
||||
(when (null? recipes)
|
||||
@ -226,8 +224,10 @@ installs it as the system driver. Returns the old system driver."
|
||||
"Register a suffix rule"
|
||||
|
||||
;; FIXME: Typecheck
|
||||
(when (>= %verbosity 0)
|
||||
(format #t "Defining suffix rule: ~A ~A ~A~%" source (right-arrow) target))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "Suffix rule: ~a~A~a ~A ~a~A~a~%~!"
|
||||
(lquo) source (rquo) (right-arrow) (lquo) target (rquo)))
|
||||
|
||||
;; If any recipes are raw strings, we need to make them into
|
||||
;; (cons 'default string)
|
||||
(let ((recipes2
|
||||
@ -376,6 +376,11 @@ installs it as the system driver. Returns the old system driver."
|
||||
#t
|
||||
#f))
|
||||
|
||||
(define (has-children? node)
|
||||
(if (null? (node-get-children node))
|
||||
#f
|
||||
#t))
|
||||
|
||||
(define (get-parent node)
|
||||
(node-get-parent node))
|
||||
|
||||
@ -398,13 +403,16 @@ installs it as the system driver. Returns the old system driver."
|
||||
#t
|
||||
#f)))))
|
||||
|
||||
(define (node-depth-string node)
|
||||
(define (node-depth node)
|
||||
(let loop ((depth 0)
|
||||
(cur node))
|
||||
(if (has-parent? cur)
|
||||
(loop (1+ depth) (get-parent cur))
|
||||
;;
|
||||
(make-string (* 2 depth) #\space))))
|
||||
depth)))
|
||||
|
||||
(define (node-depth-string node)
|
||||
(make-string (* 2 (node-depth node)) #\space))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; AUTOMATIC VARIABLES
|
||||
@ -485,6 +493,7 @@ installs it as the system driver. Returns the old system driver."
|
||||
;; and target rules
|
||||
|
||||
(define (add-builtins)
|
||||
#|
|
||||
(-> ".c" ""
|
||||
(~ ($ CC) ($ CFLAGS) ($ LDFLAGS) "-o" $@ $<))
|
||||
(-> ".f" ""
|
||||
@ -492,6 +501,7 @@ installs it as the system driver. Returns the old system driver."
|
||||
(-> ".sh" ""
|
||||
(~ "cp" $< $@)
|
||||
(~ "chmod a+x" $< $@))
|
||||
|#
|
||||
(-> ".c" ".o"
|
||||
(~ ($ CC) ($ CFLAGS) "-c" $<))
|
||||
(-> ".f" ".o"
|
||||
@ -576,8 +586,8 @@ runs them one-by-one, quitting on the first success."
|
||||
|
||||
(when (>= %verbosity 3)
|
||||
(if (passed? node)
|
||||
(format #t "PASS: ~a~%" (node-get-name node))
|
||||
(format #t "FAIL: ~a~%" (node-get-name node))))
|
||||
(format #t "PASS: ~a~%~!" (node-get-name node))
|
||||
(format #t "FAIL: ~a~%~!" (node-get-name node))))
|
||||
(node-get-status node)))
|
||||
|
||||
(define (run-recipes! node recipes)
|
||||
@ -612,10 +622,10 @@ failure condition happens, mark the node as having failed."
|
||||
|
||||
((string? recipe)
|
||||
(when (= %verbosity 1)
|
||||
(format #t "~a~%" (node-get-name node)))
|
||||
(format #t "~a~%~!" (node-get-name node)))
|
||||
(when (or (and (= %verbosity 2) (not (eq? 'silent opt)))
|
||||
(= %verbosity 3))
|
||||
(format #t "~A~%" recipe))
|
||||
(format #t "~A~%~!" recipe))
|
||||
(let ((retval (%system-proc recipe)))
|
||||
(if (zero? retval)
|
||||
(set-pass! node)
|
||||
@ -628,10 +638,10 @@ failure condition happens, mark the node as having failed."
|
||||
;; processed by system.
|
||||
((string? retval)
|
||||
(when (= %verbosity 1)
|
||||
(format #t "~a~%" (node-get-name node)))
|
||||
(format #t "~a~%~!" (node-get-name node)))
|
||||
(when (or (and (= %verbosity 2) (not (eq? 'silent opt)))
|
||||
(= %verbosity 3))
|
||||
(format #t "~A~%" retval))
|
||||
(format #t "~A~%~!" retval))
|
||||
(let ((retval2 (%system-proc retval)))
|
||||
(if (zero? retval2)
|
||||
(set-pass! node)
|
||||
@ -721,6 +731,8 @@ file exists."
|
||||
|
||||
(define (create-node name parent)
|
||||
"Constructs a tree of nodes, with name as the root node."
|
||||
(when (and (node? parent) (> (node-depth parent) 30))
|
||||
(error "Stack overflow"))
|
||||
(let ((node (make-node name parent 'undetermined)))
|
||||
(node-set-children! node '())
|
||||
(node-set-rule-type! node 'default)
|
||||
@ -787,8 +799,8 @@ file exists."
|
||||
;; FIXME: First matching rule has highest priority? Or is last better?
|
||||
(node-set-rules! node (reverse (node-get-rules node)))
|
||||
(node-set-children! node (reverse (node-get-children node)))
|
||||
;;(format #t "matching suffix rules ~S~%" (node-get-rules node))
|
||||
;;(format #t "matching children rules ~S~%" (node-get-children node))
|
||||
;;(format #t "matching suffix rules ~S~%~!" (node-get-rules node))
|
||||
;;(format #t "matching children rules ~S~%~!" (node-get-children node))
|
||||
|
||||
;; And node is ready to go
|
||||
node))
|
||||
@ -798,112 +810,134 @@ file exists."
|
||||
This is where the magic happens."
|
||||
(let ((tree (create-node root #f)))
|
||||
(let ((node tree))
|
||||
(format #t "~ABegin building target ~a~A~a.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ABegin building target ~a~A~a.~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)))
|
||||
(while #t
|
||||
(format #t "~AConsidering target ~a~A~a.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~AConsidering target ~a~A~a.~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)))
|
||||
(if (undetermined? node)
|
||||
(begin
|
||||
(format #t "~ATarget file ~a~A~a is undetermined.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(unless (node-get-mtime node)
|
||||
(format #t "~AFile ~a~A~a does not exist.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a is undetermined.~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(unless (node-get-mtime node)
|
||||
(format #t "~AFile ~a~A~a does not exist.~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))))
|
||||
(if (children-complete? node)
|
||||
(begin
|
||||
(format #t "~AFinished prerequisites of target file ~a~A~a.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(when (and (>= %verbosity 3) (has-children? node))
|
||||
(format #t "~AFinished prerequisites of target file ~a~A~a.~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)))
|
||||
(if (children-passed? node)
|
||||
(begin
|
||||
(format #t "~AThe prerequisites of target file ~a~A~a have passed.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(when (and (>= %verbosity 3) (has-children? node))
|
||||
(format #t "~AThe prerequisites of target file ~a~A~a have passed.~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)))
|
||||
(if (up-to-date? node)
|
||||
(begin
|
||||
(when (node-get-mtime node)
|
||||
(format #t "~ATarget file ~a~A~a is up to date.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a is up to date.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))))
|
||||
(set-pass! node))
|
||||
;; else, not up to date
|
||||
(begin
|
||||
(format #t "~ATarget file ~a~A~a is not up to date.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a is not up to date.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
(cond
|
||||
((using-target-rule? node)
|
||||
(format #t "~ATarget file ~a~A~a has a target rule.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a has a target rule.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
(run-target-rule! node))
|
||||
((using-suffix-rules? node)
|
||||
(format #t "~ATarget file ~a~A~a has a suffix rule.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a has a suffix rule.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
(run-suffix-rules! node))
|
||||
((using-default-rule? node)
|
||||
(format #t "~ATarget file ~a~A~a is using the default rule.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a is using the default rule.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
(run-default-rule! node))
|
||||
(else
|
||||
(error "bad rules")))
|
||||
|
||||
(if (passed? node)
|
||||
(format #t "~A[PASS] target file ~a~A~a.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))
|
||||
(format #t "~A[FAIL] target file ~a~A~a.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))))))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a has passed.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a has failed.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))))))
|
||||
;; else, children have failed
|
||||
(begin
|
||||
(format #t "~AThe prerequisites of target file ~a~A~a have failed.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~AThe prerequisites of target file ~a~A~a have failed.~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)))
|
||||
(set-fail! node))))
|
||||
;; else, children aren't complete
|
||||
(begin
|
||||
(format #t "~AThe prerequisites of target file ~a~A~a are incomplete.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~AThe prerequisites of target file ~a~A~a are incomplete.~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)))
|
||||
(let ((next (get-next-child node)))
|
||||
(format #t "~ANew node ~a~A~a ~a ~a~A~a.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)
|
||||
(right-arrow)
|
||||
(lquo) (node-get-name next) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ADescending node ~a~A~a ~a ~a~A~a.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)
|
||||
(right-arrow)
|
||||
(lquo) (node-get-name next) (rquo)))
|
||||
(set! node (get-next-child node))
|
||||
(format #t "~ATarget is now ~a~A~a.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
)))
|
||||
))))
|
||||
;; else, this node is determined
|
||||
(begin
|
||||
(if (passed? node)
|
||||
(format #t "~ATarget file ~a~A~a is passed.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo))
|
||||
(format #t "~ATarget file ~a~A~a has failed.~%"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)))
|
||||
(when (>= %verbosity 2)
|
||||
(format #t "~A~a~A~a: ~Apass~A~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)
|
||||
(green) (default)))
|
||||
(when (>= %verbosity 2)
|
||||
(format #t "~A~a~A~a: ~Afail~A~%~!"
|
||||
(node-depth-string node) (lquo) (node-get-name node) (rquo)
|
||||
(red) (default))))
|
||||
(if (has-parent? node)
|
||||
(begin
|
||||
(format #t "~ANew node ~a~A~a ~a ~a~A~a.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)
|
||||
(right-arrow)
|
||||
(lquo) (node-get-name (node-get-parent node)) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~AAscending node ~a~A~a ~a ~a~A~a.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)
|
||||
(right-arrow)
|
||||
(lquo) (node-get-name (node-get-parent node)) (rquo)))
|
||||
|
||||
(set! node (get-parent node)))
|
||||
;; else, there is no parent to this node
|
||||
(begin
|
||||
(format #t "~ATarget file ~a~A~a has no parent.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))
|
||||
(when (>= %verbosity 3)
|
||||
(format #t "~ATarget file ~a~A~a has no parent.~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
(if (passed? node)
|
||||
(format #t "~A[COMPLETE] [PASS] target file ~a~A~a.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo))
|
||||
(format #t "~A[COMPLETE] [FAIL] target file ~a~A~a.~%"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)))
|
||||
(when (>= %verbosity 1)
|
||||
(format #t "~A~a~A~a: ~Acomplete~A~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)
|
||||
(green) (default)))
|
||||
(when (>= %verbosity 1)
|
||||
(format #t "~A~a~A~a: ~Acomplete~A~%~!"
|
||||
(node-depth-string node)
|
||||
(lquo) (node-get-name node) (rquo)
|
||||
(red) (default))))
|
||||
(break)))))))
|
||||
;; Return the command output of the root node
|
||||
(passed? tree)))
|
||||
|
@ -5,7 +5,7 @@
|
||||
left-arrow
|
||||
ellipses
|
||||
C0
|
||||
red
|
||||
red green
|
||||
lquo
|
||||
rquo
|
||||
initialize-text))
|
||||
|
Loading…
Reference in New Issue
Block a user