Merge branch 'master' into search-parent-dirs
* master: Fixed markdown errors in README - code samples now correctly formatted. Fix use of config.sh in example log.py, minimal/do: don't use ansi colour codes if $TERM is blank or 'dumb' Use named constants for terminal control codes. redo-sh: keep testing even after finding a 'good' shell. redo-sh.do: hide warning output from 'which' in some shells. redo-sh.do: wrap long lines. Handle .do files that start with "#!/" to specify an explicit interpreter. minimal/do: don't print an error on exit if we don't build anything. bash completions: also mark 'do' as a completable command. bash completions: work correctly when $cur is an empty string. bash completions: call redo-targets for a more complete list. bash completions: work correctly with subdirs, ie. 'redo t/<tab>' Sample bash completion rules for redo targets. minimal/do: faster deletion of stamp files. minimal/do: delete .tmp files if a build fails. minimal/do: use ".did" stamp files instead of empty target files. minimal/do: use posix shell features instead of dirname/basename. Automatically select a good shell instead of relying on /bin/sh. Conflicts: t/clean.do
This commit is contained in:
commit
e98696caef
20 changed files with 525 additions and 89 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@ t/LD
|
||||||
t/[yb]ellow
|
t/[yb]ellow
|
||||||
t/hello
|
t/hello
|
||||||
t/*.o
|
t/*.o
|
||||||
|
/redo-sh
|
||||||
|
|
|
||||||
37
README.md
37
README.md
|
|
@ -69,6 +69,7 @@ it'll be very, very fast.)
|
||||||
The easiest way to show it is with an example.
|
The easiest way to show it is with an example.
|
||||||
|
|
||||||
Create a file called default.o.do:
|
Create a file called default.o.do:
|
||||||
|
|
||||||
redo-ifchange $1.c
|
redo-ifchange $1.c
|
||||||
gcc -MD -MF $3.deps.tmp -c -o $3 $1.c
|
gcc -MD -MF $3.deps.tmp -c -o $3 $1.c
|
||||||
DEPS=$(sed -e "s/^$3://" -e 's/\\//g' <$3.deps.tmp)
|
DEPS=$(sed -e "s/^$3://" -e 's/\\//g' <$3.deps.tmp)
|
||||||
|
|
@ -76,6 +77,7 @@ Create a file called default.o.do:
|
||||||
redo-ifchange $DEPS
|
redo-ifchange $DEPS
|
||||||
|
|
||||||
Create a file called myprog.do:
|
Create a file called myprog.do:
|
||||||
|
|
||||||
DEPS="a.o b.o"
|
DEPS="a.o b.o"
|
||||||
redo-ifchange $DEPS
|
redo-ifchange $DEPS
|
||||||
gcc -o $3 $DEPS
|
gcc -o $3 $DEPS
|
||||||
|
|
@ -84,30 +86,36 @@ Of course, you'll also have to create `a.c` and `b.c`, the C language
|
||||||
source files that you want to build to create your application.
|
source files that you want to build to create your application.
|
||||||
|
|
||||||
In a.c:
|
In a.c:
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "b.h"
|
#include "b.h"
|
||||||
|
|
||||||
int main() { printf(bstr); }
|
int main() { printf(bstr); }
|
||||||
|
|
||||||
In b.h:
|
In b.h:
|
||||||
|
|
||||||
extern char *bstr;
|
extern char *bstr;
|
||||||
|
|
||||||
In b.c:
|
In b.c:
|
||||||
char *bstr = "hello, world!\n";
|
char *bstr = "hello, world!\n";
|
||||||
|
|
||||||
Now you simply run:
|
Now you simply run:
|
||||||
|
|
||||||
$ redo myprog
|
$ redo myprog
|
||||||
|
|
||||||
And it says:
|
And it says:
|
||||||
|
|
||||||
redo myprog
|
redo myprog
|
||||||
redo a.o
|
redo a.o
|
||||||
redo b.o
|
redo b.o
|
||||||
|
|
||||||
Now try this:
|
Now try this:
|
||||||
|
|
||||||
$ touch b.h
|
$ touch b.h
|
||||||
$ redo myprog
|
$ redo myprog
|
||||||
|
|
||||||
Sure enough, it says:
|
Sure enough, it says:
|
||||||
|
|
||||||
redo myprog
|
redo myprog
|
||||||
redo a.o
|
redo a.o
|
||||||
|
|
||||||
|
|
@ -437,15 +445,28 @@ changed, you can run `redo-ifchange target` instead.
|
||||||
|
|
||||||
# Can my .do files be written in a language other than sh?
|
# Can my .do files be written in a language other than sh?
|
||||||
|
|
||||||
FIXME: Not presently. In theory, we could support starting your .do files
|
Yes. If the first line of your .do file starts with the
|
||||||
with the magic "#!/" sequence (eg. #!/usr/bin/python) and then using that
|
magic "#!/" sequence (eg. `#!/usr/bin/python`), then redo
|
||||||
shell to run your .do script. But that opens new problems, like figuring
|
will execute your script using that particular interpreter.
|
||||||
out what is the equivalent of the `redo-ifchange` command in python. Do you
|
|
||||||
just run it in a subprocess? That might be unnecessarily slow. And so on.
|
|
||||||
|
|
||||||
Right now, `redo` explicitly runs `sh filename.do`. The main reasons for
|
Note that this is slightly different from normal Unix
|
||||||
this are to make the #!/ line optional, and so you don't have to remember to
|
execution semantics: redo never execs your script directly;
|
||||||
`chmod +x` your .do files.
|
it only looks for the "#!/" line. The main reason for this
|
||||||
|
is so that your .do scripts don't have to be marked
|
||||||
|
executable (chmod +x). Executable .do scripts would
|
||||||
|
suggest to users that they should run them directly, and
|
||||||
|
they shouldn't; .do scripts should always be executed
|
||||||
|
inside an instance of redo, so that dependencies can be
|
||||||
|
tracked correctly.
|
||||||
|
|
||||||
|
WARNING: If your .do script *is* written in Unix sh, we
|
||||||
|
recommend *not* including the `#!/bin/sh` line. That's
|
||||||
|
because there are many variations of /bin/sh, and not all
|
||||||
|
of them are POSIX compliant. redo tries pretty hard to
|
||||||
|
find a good default shell that will be "as POSIXy as
|
||||||
|
possible," and if you override it using #!/bin/sh, you lose
|
||||||
|
this benefit and you'll have to worry more about
|
||||||
|
portability.
|
||||||
|
|
||||||
|
|
||||||
# Can a single .do script generate multiple outputs?
|
# Can a single .do script generate multiple outputs?
|
||||||
|
|
|
||||||
2
_all.do
Normal file
2
_all.do
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
redo-ifchange redo-sh
|
||||||
|
redo-ifchange Documentation/all
|
||||||
2
all.do
2
all.do
|
|
@ -1,2 +1,2 @@
|
||||||
redo-ifchange Documentation/all
|
redo-ifchange _all
|
||||||
echo "Nothing much to do. Try 'redo t/all' or 'redo test'" >&2
|
echo "Nothing much to do. Try 'redo t/all' or 'redo test'" >&2
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,9 @@ class BuildJob:
|
||||||
if vars.VERBOSE: argv[1] += 'v'
|
if vars.VERBOSE: argv[1] += 'v'
|
||||||
if vars.XTRACE: argv[1] += 'x'
|
if vars.XTRACE: argv[1] += 'x'
|
||||||
if vars.VERBOSE or vars.XTRACE: log_('\n')
|
if vars.VERBOSE or vars.XTRACE: log_('\n')
|
||||||
|
firstline = open(os.path.join(dodir, dofile)).readline().strip()
|
||||||
|
if firstline.startswith('#!/'):
|
||||||
|
argv[0:2] = firstline[2:].split(' ')
|
||||||
log('%s\n' % _nice(t))
|
log('%s\n' % _nice(t))
|
||||||
self.dodir = dodir
|
self.dodir = dodir
|
||||||
self.basename = basename
|
self.basename = basename
|
||||||
|
|
@ -216,7 +219,8 @@ class BuildJob:
|
||||||
after_t = _try_stat(t)
|
after_t = _try_stat(t)
|
||||||
st1 = os.fstat(f.fileno())
|
st1 = os.fstat(f.fileno())
|
||||||
st2 = _try_stat(self.tmpname2)
|
st2 = _try_stat(self.tmpname2)
|
||||||
if after_t != before_t and not stat.S_ISDIR(after_t.st_mode):
|
if (after_t and after_t != before_t and
|
||||||
|
not stat.S_ISDIR(after_t.st_mode)):
|
||||||
err('%s modified %s directly!\n' % (self.argv[2], t))
|
err('%s modified %s directly!\n' % (self.argv[2], t))
|
||||||
err('...you should update $3 (a temp file) or stdout, not $1.\n')
|
err('...you should update $3 (a temp file) or stdout, not $1.\n')
|
||||||
rv = 206
|
rv = 206
|
||||||
|
|
|
||||||
2
clean.do
2
clean.do
|
|
@ -1,3 +1,4 @@
|
||||||
|
rm -rf t/.redo redo-sh
|
||||||
if [ -e .do_built ]; then
|
if [ -e .do_built ]; then
|
||||||
while read x; do
|
while read x; do
|
||||||
rm -f "$x"
|
rm -f "$x"
|
||||||
|
|
@ -6,5 +7,4 @@ fi
|
||||||
[ -z "$DO_BUILT" ] && rm -rf .do_built .do_built.dir
|
[ -z "$DO_BUILT" ] && rm -rf .do_built .do_built.dir
|
||||||
redo t/clean Documentation/clean
|
redo t/clean Documentation/clean
|
||||||
rm -f *~ .*~ */*~ */.*~ *.pyc install.wrapper
|
rm -f *~ .*~ */*~ */.*~ *.pyc install.wrapper
|
||||||
rm -rf t/.redo
|
|
||||||
find . -name '*.tmp' -exec rm -fv {} \;
|
find . -name '*.tmp' -exec rm -fv {} \;
|
||||||
|
|
|
||||||
40
contrib/bash_completion.d/redo
Normal file
40
contrib/bash_completion.d/redo
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
__find_redo_targets()
|
||||||
|
{
|
||||||
|
local IFS=$'\n:'
|
||||||
|
for d in . ../.. $PATH; do
|
||||||
|
if [ -x "$d/redo-targets" ]; then
|
||||||
|
( cd "$d" && echo "$PWD/redo-targets" )
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
__redo_targets=$(__find_redo_targets)
|
||||||
|
|
||||||
|
|
||||||
|
__redo_completions()
|
||||||
|
{
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
local IFS=$'\n'
|
||||||
|
local targets=$(
|
||||||
|
# targets already known to redo
|
||||||
|
[ -x "$__redo_targets" ] &&
|
||||||
|
"$__redo_targets" |
|
||||||
|
while read name; do
|
||||||
|
rest=${name#$cur}
|
||||||
|
[ "$cur$rest" != "$name" ] && continue
|
||||||
|
name2="$cur${rest%%/*}"
|
||||||
|
[ -e "$name2/." ] || echo "$name2"
|
||||||
|
done
|
||||||
|
|
||||||
|
# targets named explicitly by .do files
|
||||||
|
compgen -o default "$cur" |
|
||||||
|
while read name; do
|
||||||
|
local don=${name%.do} def=${name#default.}
|
||||||
|
[ "$don" = "$name" -o "$def" != "$name" ] && continue
|
||||||
|
echo "${name%.do}"
|
||||||
|
done
|
||||||
|
)
|
||||||
|
COMPREPLY=($(compgen -W "$targets" "$cur"))
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F __redo_completions -o plusdirs -o filenames redo do
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
exec >&2
|
exec >&2
|
||||||
redo-ifchange Documentation/all
|
redo-ifchange _all
|
||||||
|
|
||||||
: ${INSTALL:=install}
|
: ${INSTALL:=install}
|
||||||
: ${DESTDIR:=}
|
: ${DESTDIR:=}
|
||||||
|
|
@ -28,6 +28,10 @@ for d in *.py; do
|
||||||
done
|
done
|
||||||
python -mcompileall $LIBDIR
|
python -mcompileall $LIBDIR
|
||||||
|
|
||||||
|
# It's important for the file to actually be named 'sh'. Some shells (like
|
||||||
|
# bash and zsh) only go into POSIX-compatible mode if they have that name.
|
||||||
|
cp -R redo-sh/sh $LIBDIR/sh
|
||||||
|
|
||||||
# binaries
|
# binaries
|
||||||
for dd in redo*.py; do
|
for dd in redo*.py; do
|
||||||
d=$(basename $dd .py)
|
d=$(basename $dd .py)
|
||||||
|
|
|
||||||
44
log.py
44
log.py
|
|
@ -1,6 +1,22 @@
|
||||||
import sys, os
|
import sys, os
|
||||||
import vars
|
import vars
|
||||||
|
|
||||||
|
# By default, no output colouring.
|
||||||
|
RED = ""
|
||||||
|
GREEN = ""
|
||||||
|
YELLOW = ""
|
||||||
|
BOLD = ""
|
||||||
|
PLAIN = ""
|
||||||
|
|
||||||
|
if sys.stderr.isatty() and (os.environ.get('TERM') or 'dumb') != 'dumb':
|
||||||
|
# ...use ANSI formatting codes.
|
||||||
|
RED = "\x1b[31m"
|
||||||
|
GREEN = "\x1b[32m"
|
||||||
|
YELLOW = "\x1b[33m"
|
||||||
|
BOLD = "\x1b[1m"
|
||||||
|
PLAIN = "\x1b[m"
|
||||||
|
|
||||||
|
|
||||||
def log_(s):
|
def log_(s):
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
if vars.DEBUG_PIDS:
|
if vars.DEBUG_PIDS:
|
||||||
|
|
@ -10,30 +26,14 @@ def log_(s):
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|
||||||
def _clog(s):
|
def log(s):
|
||||||
log_('\x1b[32mredo %s\x1b[1m%s\x1b[m' % (vars.DEPTH, s))
|
log_(''.join([GREEN, "redo ", vars.DEPTH, BOLD, s, PLAIN]))
|
||||||
def _bwlog(s):
|
|
||||||
log_('redo %s%s' % (vars.DEPTH, s))
|
|
||||||
|
|
||||||
def _cerr(s):
|
def err(s):
|
||||||
log_('\x1b[31mredo: %s\x1b[1m%s\x1b[m' % (vars.DEPTH, s))
|
log_(''.join([RED, "redo ", vars.DEPTH, BOLD, s, PLAIN]))
|
||||||
def _bwerr(s):
|
|
||||||
log_('redo: %s%s' % (vars.DEPTH, s))
|
|
||||||
|
|
||||||
def _cwarn(s):
|
def warn(s):
|
||||||
log_('\x1b[33mredo: %s\x1b[1m%s\x1b[m' % (vars.DEPTH, s))
|
log_(''.join([YELLOW, "redo ", vars.DEPTH, BOLD, s, PLAIN]))
|
||||||
def _bwwarn(s):
|
|
||||||
log_('redo: %s%s' % (vars.DEPTH, s))
|
|
||||||
|
|
||||||
|
|
||||||
if os.isatty(2):
|
|
||||||
log = _clog
|
|
||||||
err = _cerr
|
|
||||||
warn = _cwarn
|
|
||||||
else:
|
|
||||||
log = _bwlog
|
|
||||||
err = _bwerr
|
|
||||||
warn = _bwwarn
|
|
||||||
|
|
||||||
|
|
||||||
def debug(s):
|
def debug(s):
|
||||||
|
|
|
||||||
130
minimal/do
130
minimal/do
|
|
@ -6,18 +6,39 @@
|
||||||
# The author disclaims copyright to this source file and hereby places it in
|
# The author disclaims copyright to this source file and hereby places it in
|
||||||
# the public domain. (2010 12 14)
|
# the public domain. (2010 12 14)
|
||||||
#
|
#
|
||||||
export REDO="$(cd "$(dirname "$0")" && echo "$PWD/$(basename "$0")")"
|
|
||||||
|
|
||||||
|
# By default, no output coloring.
|
||||||
|
GREEN=""
|
||||||
|
BOLD=""
|
||||||
|
PLAIN=""
|
||||||
|
|
||||||
|
if [ -n "$TERM" -a "$TERM" != "dumb" ] && tty <&2 >/dev/null 2>&1; then
|
||||||
|
GREEN="$(printf '\033[32m')"
|
||||||
|
BOLD="$(printf '\033[1m')"
|
||||||
|
PLAIN="$(printf '\033[m')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_dirsplit()
|
||||||
|
{
|
||||||
|
base=${1##*/}
|
||||||
|
dir=${1%$base}
|
||||||
|
}
|
||||||
|
|
||||||
|
_dirsplit "$0"
|
||||||
|
export REDO=$(cd "${dir:-.}" && echo "$PWD/$base")
|
||||||
|
|
||||||
|
DO_TOP=
|
||||||
if [ -z "$DO_BUILT" ]; then
|
if [ -z "$DO_BUILT" ]; then
|
||||||
export DO_BUILT="$PWD/.do_built"
|
DO_TOP=1
|
||||||
if [ -e "$DO_BUILT" ]; then
|
export DO_BUILT=$PWD/.do_built
|
||||||
echo "Removing previously built files..." >&2
|
: >>"$DO_BUILT"
|
||||||
sort -u "$DO_BUILT" | tee "$DO_BUILT.new" |
|
echo "Removing previously built files..." >&2
|
||||||
while read f; do rm -f "$f"; done
|
sort -u "$DO_BUILT" | tee "$DO_BUILT.new" |
|
||||||
mv "$DO_BUILT.new" "$DO_BUILT"
|
while read f; do printf "%s\0%s.did\0" "$f" "$f"; done |
|
||||||
fi
|
xargs -0 rm -f 2>/dev/null
|
||||||
DO_PATH="$DO_BUILT.dir"
|
mv "$DO_BUILT.new" "$DO_BUILT"
|
||||||
export PATH="$DO_PATH:$PATH"
|
DO_PATH=$DO_BUILT.dir
|
||||||
|
export PATH=$DO_PATH:$PATH
|
||||||
rm -rf "$DO_PATH"
|
rm -rf "$DO_PATH"
|
||||||
mkdir "$DO_PATH"
|
mkdir "$DO_PATH"
|
||||||
for d in redo redo-ifchange; do
|
for d in redo redo-ifchange; do
|
||||||
|
|
@ -30,56 +51,63 @@ if [ -z "$DO_BUILT" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
_dirsplit()
|
_find_dofile()
|
||||||
{
|
{
|
||||||
OLDIFS="$IFS"
|
DOFILE=default.$1.do
|
||||||
IFS=/
|
while :; do
|
||||||
set -- $1
|
DOFILE=default.${DOFILE#default.*.}
|
||||||
IFS="$OLDIFS"
|
[ -e "$DOFILE" -o "$DOFILE" = default.do ] && break
|
||||||
dir=""
|
|
||||||
while [ $# -gt 1 ]; do
|
|
||||||
dir="$dir$1/"
|
|
||||||
shift
|
|
||||||
done
|
done
|
||||||
base="$1"
|
EXT=${DOFILE#default}
|
||||||
|
EXT=${EXT%.do}
|
||||||
|
BASE=${1%$EXT}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_run_dofile()
|
||||||
|
{
|
||||||
|
export DO_DEPTH="$DO_DEPTH "
|
||||||
|
export REDO_TARGET=$PWD/$TARGET
|
||||||
|
set -e
|
||||||
|
read line1 <"$PWD/$DOFILE"
|
||||||
|
cmd=${line1#"#!/"}
|
||||||
|
if [ "$cmd" != "$line1" ]; then
|
||||||
|
/$cmd "$PWD/$DOFILE" "$@" >"$TARGET.tmp2"
|
||||||
|
else
|
||||||
|
. "$PWD/$DOFILE" >"$TARGET.tmp2"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_do()
|
_do()
|
||||||
{
|
{
|
||||||
DIR="$1"
|
DIR=$1
|
||||||
TARGET="$2"
|
TARGET=$2
|
||||||
if [ ! -e "$TARGET" ]; then
|
if [ ! -e "$TARGET" ] || [ -e "$TARGET/." -a ! -e "$TARGET.did" ]; then
|
||||||
printf '\033[32mdo %s\033[1m%s\033[m\n' \
|
printf '%sdo %s%s%s%s\n' \
|
||||||
"$DO_DEPTH" "$DIR$TARGET" >&2
|
"$GREEN" "$DO_DEPTH" "$BOLD" "$DIR$TARGET" "$PLAIN" >&2
|
||||||
echo "$PWD/$TARGET" >>"$DO_BUILT"
|
echo "$PWD/$TARGET" >>"$DO_BUILT"
|
||||||
dof=".$TARGET"
|
DOFILE=$TARGET.do
|
||||||
DOFILE="$TARGET.do"
|
BASE=$TARGET
|
||||||
BASE="$TARGET"
|
EXT=
|
||||||
EXT=""
|
[ -e "$TARGET.do" ] || _find_dofile "$TARGET"
|
||||||
while [ ! -e "$DOFILE" ]; do
|
if [ ! -e "$DOFILE" ]; then
|
||||||
dof2=$(echo "$dof" | sed 's/\.[^\.]*//')
|
echo "do: $TARGET: no .do file" >&2
|
||||||
[ "$dof" = "$dof2" ] && break
|
return 1
|
||||||
dof="$dof2"
|
fi
|
||||||
DOFILE="default$dof.do"
|
: >>"$TARGET.did"
|
||||||
BASE="$(basename "$TARGET" "$dof")"
|
( _run_dofile "$BASE" "$EXT" "$TARGET.tmp" )
|
||||||
EXT="$dof"
|
RV=$?
|
||||||
done
|
if [ $RV != 0 ]; then
|
||||||
set "$BASE" "$EXT" "$TARGET.tmp"
|
|
||||||
RV=
|
|
||||||
(
|
|
||||||
export DO_DEPTH="$DO_DEPTH "
|
|
||||||
export REDO_TARGET="$PWD/$TARGET"
|
|
||||||
set -e
|
|
||||||
. "$PWD/$DOFILE" >"$TARGET.tmp"
|
|
||||||
) || RV="$?"
|
|
||||||
[ -z "$RV" ] && mv "$TARGET.tmp" "$TARGET" 2>/dev/null
|
|
||||||
: >>"$TARGET"
|
|
||||||
if [ -n "$RV" ]; then
|
|
||||||
printf "do: %s%s\n" "$DO_DEPTH" \
|
printf "do: %s%s\n" "$DO_DEPTH" \
|
||||||
"$DIR$TARGET: got exit code $RV" >&2
|
"$DIR$TARGET: got exit code $RV" >&2
|
||||||
|
rm -f "$TARGET.tmp" "$TARGET.tmp2"
|
||||||
return $RV
|
return $RV
|
||||||
fi
|
fi
|
||||||
|
mv "$TARGET.tmp" "$TARGET" 2>/dev/null ||
|
||||||
|
! test -s "$TARGET.tmp2" ||
|
||||||
|
mv "$TARGET.tmp2" "$TARGET" 2>/dev/null
|
||||||
|
rm -f "$TARGET.tmp2"
|
||||||
else
|
else
|
||||||
echo "do $DO_DEPTH$TARGET exists." >&2
|
echo "do $DO_DEPTH$TARGET exists." >&2
|
||||||
fi
|
fi
|
||||||
|
|
@ -101,3 +129,9 @@ if [ -n "$*" ]; then
|
||||||
else
|
else
|
||||||
redo all
|
redo all
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DO_TOP" ]; then
|
||||||
|
echo "Removing stamp files..." >&2
|
||||||
|
while read f; do printf "%s.did\0" "$f"; done <"$DO_BUILT" |
|
||||||
|
xargs -0 rm -f 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
|
||||||
46
redo-sh.do
Normal file
46
redo-sh.do
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
exec >&2
|
||||||
|
redo-ifchange t/shelltest.od
|
||||||
|
|
||||||
|
rm -rf $1.new $1/sh
|
||||||
|
mkdir $1.new
|
||||||
|
|
||||||
|
GOOD=
|
||||||
|
WARN=
|
||||||
|
|
||||||
|
for sh in dash sh ash ksh ksh88 ksh93 pdksh bash zsh busybox; do
|
||||||
|
printf "Testing %s... " "$sh"
|
||||||
|
FOUND=`which $sh 2>/dev/null` || { echo "missing"; continue; }
|
||||||
|
|
||||||
|
# It's important for the file to actually be named 'sh'. Some
|
||||||
|
# shells (like bash and zsh) only go into POSIX-compatible mode if
|
||||||
|
# they have that name. If they're not in POSIX-compatible mode,
|
||||||
|
# they'll fail the test.
|
||||||
|
rm -f $1.new/sh
|
||||||
|
ln -s $FOUND $1.new/sh
|
||||||
|
|
||||||
|
set +e
|
||||||
|
( cd t && ../$1.new/sh shelltest.od >/dev/null 2>&1 )
|
||||||
|
RV=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
case $RV in
|
||||||
|
0) echo "good"; [ -n "$GOOD" ] || GOOD=$FOUND ;;
|
||||||
|
42) echo "warnings"; [ -n "$WARN" ] || WARN=$FOUND ;;
|
||||||
|
*) echo "failed" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -rf $1 $1.new $3
|
||||||
|
|
||||||
|
if [ -n "$GOOD" ]; then
|
||||||
|
echo "Selected perfect shell: $GOOD"
|
||||||
|
mkdir $3
|
||||||
|
ln -s $GOOD $3/sh
|
||||||
|
elif [ -n "$WARN" ]; then
|
||||||
|
echo "Selected mostly good shell: $WARN"
|
||||||
|
mkdir $3
|
||||||
|
ln -s $WARN $3/sh
|
||||||
|
else
|
||||||
|
echo "No good shells found! Maybe install dash, bash, or zsh."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
3
t/.gitignore
vendored
3
t/.gitignore
vendored
|
|
@ -15,3 +15,6 @@ test2.args
|
||||||
/ifcreate[12].log
|
/ifcreate[12].log
|
||||||
/ifcreate[12].dep
|
/ifcreate[12].dep
|
||||||
/ifcreate[12]
|
/ifcreate[12]
|
||||||
|
/broken
|
||||||
|
/shellfile
|
||||||
|
/nonshelltest
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
redo example/clean curse/clean deps/clean "space dir/clean" stamp/clean \
|
redo example/clean curse/clean deps/clean "space dir/clean" stamp/clean \
|
||||||
defaults-flat/clean defaults-nested/clean
|
defaults-flat/clean defaults-nested/clean
|
||||||
rm -f mode1 makedir.log chdir1 deltest2 \
|
rm -f broken nonshelltest shellfile mode1 makedir.log chdir1 deltest2 \
|
||||||
hello [by]ellow *.o *~ .*~ *.log CC LD passfail silence silence.do \
|
hello [by]ellow *.o *~ .*~ *.log CC LD passfail silence silence.do \
|
||||||
touch1 touch1.do always1 ifcreate[12].dep ifcreate[12]
|
touch1 touch1.do always1 ifcreate[12].dep ifcreate[12]
|
||||||
rm -rf makedir
|
rm -rf makedir
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ redo-ifchange config.sh
|
||||||
exec >$3
|
exec >$3
|
||||||
cat <<-EOF
|
cat <<-EOF
|
||||||
redo-ifchange \$1.c
|
redo-ifchange \$1.c
|
||||||
gcc -MD -MF \$3.deps.tmp -o \$3 -c \$1.c
|
gcc $CFLAGS -MD -MF \$3.deps.tmp -o \$3 -c \$1.c
|
||||||
DEPS=\$(sed -e "s/^\$3://" -e 's/\\\\//g' <\$3.deps.tmp)
|
DEPS=\$(sed -e "s/^\$3://" -e 's/\\\\//g' <\$3.deps.tmp)
|
||||||
rm -f \$3.deps.tmp
|
rm -f \$3.deps.tmp
|
||||||
redo-ifchange \$DEPS
|
redo-ifchange \$DEPS
|
||||||
|
|
|
||||||
3
t/nonshelltest.do
Normal file
3
t/nonshelltest.do
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
$a="perly";
|
||||||
|
print "hello $a world\n";
|
||||||
8
t/shelltest.do
Normal file
8
t/shelltest.do
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
set +e
|
||||||
|
( . ./shelltest.od )
|
||||||
|
RV=$?
|
||||||
|
case $RV in
|
||||||
|
0) exit 0 ;;
|
||||||
|
42) exit 0 ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
262
t/shelltest.od
Normal file
262
t/shelltest.od
Normal file
|
|
@ -0,0 +1,262 @@
|
||||||
|
#
|
||||||
|
# Most of these tests were inspired by:
|
||||||
|
# http://www.gnu.org/software/hello/manual/autoconf/Shell-Substitutions.html
|
||||||
|
#
|
||||||
|
exec >&2
|
||||||
|
set +e
|
||||||
|
FAIL=
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo " failed: $1"
|
||||||
|
FAIL=41
|
||||||
|
}
|
||||||
|
warn()
|
||||||
|
{
|
||||||
|
echo " warning: $1"
|
||||||
|
[ -n "$FAIL" ] || FAIL=42
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
name=foo.o.o
|
||||||
|
ext=.o
|
||||||
|
[ "${name#foo.o}" = ".o" ] || fail 3
|
||||||
|
|
||||||
|
|
||||||
|
spacey="this has * and spaces"
|
||||||
|
case $spacey in
|
||||||
|
*) spaceout=$name$spacey ;;
|
||||||
|
esac
|
||||||
|
[ "$spaceout" = "$name$spacey" ] || fail 4
|
||||||
|
|
||||||
|
|
||||||
|
n() { echo "$#$@"; }
|
||||||
|
f=" - "
|
||||||
|
out=$(n - ""$f"" -)
|
||||||
|
[ "$out" = "5- - -" ] || warn 5
|
||||||
|
|
||||||
|
|
||||||
|
n1() { echo $#; }
|
||||||
|
n2() { n1 "$@"; }
|
||||||
|
t1=$(n1)
|
||||||
|
t2=$(n2)
|
||||||
|
[ "$t1" = "0" ] || fail 6
|
||||||
|
[ "$t2" = "0" ] || fail 7
|
||||||
|
|
||||||
|
|
||||||
|
n1() { for i in "$@"; do echo $i; done; }
|
||||||
|
n2() { for i in ${1+"$@"}; do echo $i; done; }
|
||||||
|
t1=$(n1 "Hello World" "!")
|
||||||
|
t2=$(n2 "Hello World" "!")
|
||||||
|
WANT="Hello World
|
||||||
|
!"
|
||||||
|
[ "$t1" = "$WANT" ] || fail 8
|
||||||
|
[ "$t2" = "$WANT" ] || fail 9
|
||||||
|
|
||||||
|
|
||||||
|
n() { echo ${10}; }
|
||||||
|
t1=$(n 1 2 3 4 5 6 7 8 9 xx yy)
|
||||||
|
[ "$t1" = "xx" ] || fail 10
|
||||||
|
|
||||||
|
|
||||||
|
chicken1=`echo " $spacey" | sed s/a/./g`
|
||||||
|
chicken2="`echo " $spacey" | sed s/a/./g`"
|
||||||
|
chicken3=$(echo " $spacey" | sed s/a/./g)
|
||||||
|
chicken4="$(echo " $spacey" | sed s/a/./g)"
|
||||||
|
[ "$chicken1" = " this h.s * .nd sp.ces" ] || fail 11
|
||||||
|
[ "$chicken2" = " this h.s * .nd sp.ces" ] || fail 12
|
||||||
|
[ "$chicken3" = " this h.s * .nd sp.ces" ] || fail 13
|
||||||
|
[ "$chicken4" = " this h.s * .nd sp.ces" ] || fail 14
|
||||||
|
|
||||||
|
|
||||||
|
f1=
|
||||||
|
f2=goo
|
||||||
|
g1=
|
||||||
|
g2=goo
|
||||||
|
out=$(echo ${f1:-foo} ${f2:-foo} ${g1:=foo} ${g2:=foo})
|
||||||
|
: ${f1:-roo} ${f2:-roo} ${g1:=roo} ${g2:=roo}
|
||||||
|
[ "$out" = "foo goo foo goo" ] || fail 16
|
||||||
|
[ "$f1$f2$g1$g2" = "gooroogoo" ] || fail 17
|
||||||
|
|
||||||
|
|
||||||
|
unset a
|
||||||
|
t1=$(echo ${a-b c})
|
||||||
|
t2=$(echo ${a-'b c'})
|
||||||
|
t3=$(echo "${a-b c}")
|
||||||
|
t4=$(echo "${a-"b c"}")
|
||||||
|
t5=$(cat <<EOF
|
||||||
|
${a-b c}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
[ "$t1" = "b c" ] || fail 21
|
||||||
|
[ "$t2" = "b c" ] || fail 22
|
||||||
|
[ "$t3" = "b c" ] || fail 23
|
||||||
|
[ "$t4" = "b c" ] || fail 24
|
||||||
|
[ "$t5" = "b c" ] || fail 25
|
||||||
|
|
||||||
|
|
||||||
|
unset a
|
||||||
|
t1=$(echo ${a=b c})
|
||||||
|
t2=$(echo ${a='b c'})
|
||||||
|
t3=$(echo "${a=b c}")
|
||||||
|
t4=$(echo "${a="b c"}")
|
||||||
|
t5=$(cat <<EOF
|
||||||
|
${a=b c}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
[ "$t1" = "b c" ] || fail 31
|
||||||
|
[ "$t2" = "b c" ] || warn 32
|
||||||
|
[ "$t3" = "b c" ] || fail 33
|
||||||
|
[ "$t4" = "b c" ] || fail 34
|
||||||
|
[ "$t5" = "b c" ] || fail 35
|
||||||
|
|
||||||
|
|
||||||
|
unset foo
|
||||||
|
foo=${foo='}'} # unconfuse syntax highlighting: '
|
||||||
|
[ "$foo" = "}" ] || fail 41
|
||||||
|
foo=${foo='}'} # unconfuse syntax highlighting: '
|
||||||
|
[ "$foo" = "}" ] || fail 42
|
||||||
|
|
||||||
|
|
||||||
|
default="yu,yaa"
|
||||||
|
unset var
|
||||||
|
: ${var="$default"}
|
||||||
|
t1=$(cat <<EOF
|
||||||
|
$var
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
[ "$t1" = "yu,yaa" ] || fail 43
|
||||||
|
|
||||||
|
|
||||||
|
default="a b c"
|
||||||
|
unset list1 list2
|
||||||
|
: ${list1="$default"} ${list2=$default}
|
||||||
|
t1=$(for c in $list1; do echo $c; done)
|
||||||
|
t2=$(for c in $list2; do echo $c; done)
|
||||||
|
WANT="a
|
||||||
|
b
|
||||||
|
c"
|
||||||
|
[ "$t1" = "$WANT" ] || fail 44
|
||||||
|
[ "$t2" = "$WANT" ] || fail 45
|
||||||
|
|
||||||
|
|
||||||
|
var='a a b b'
|
||||||
|
t1=${#var}
|
||||||
|
t2=${var#a* }
|
||||||
|
t3=${var##a* }
|
||||||
|
t4=${var%b*}
|
||||||
|
t5=${var%%b*}
|
||||||
|
[ "$t1" = "7" ] || fail 51
|
||||||
|
[ "$t2" = "a b b" ] || fail 52
|
||||||
|
[ "$t3" = "b" ] || fail 53
|
||||||
|
[ "$t4" = "a a b " ] || fail 54
|
||||||
|
[ "$t5" = "a a " ] || fail 55
|
||||||
|
|
||||||
|
|
||||||
|
in="a
|
||||||
|
b
|
||||||
|
"
|
||||||
|
t1=$(echo "$in" | tr a A)
|
||||||
|
t2="$(echo "$in" | tr a A)"
|
||||||
|
[ "$t1" = "A
|
||||||
|
b" ] || exit 57
|
||||||
|
[ "$t2" = "A
|
||||||
|
b" ] || exit 58
|
||||||
|
|
||||||
|
|
||||||
|
echo "`printf 'foo\r\n'` bar" >broken
|
||||||
|
echo "`printf 'foo\r\n'`"" bar" | diff -q - broken || fail 59
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# This one is too obnoxious. dash and ash pass the test, but nothing else
|
||||||
|
# does, and this case is just too dumb to care about. Just don't do that!
|
||||||
|
#
|
||||||
|
#t=`echo $(case x in x) echo hello;; esac)`
|
||||||
|
#[ "$t" = "hello" ] || fail 60
|
||||||
|
|
||||||
|
|
||||||
|
x=5
|
||||||
|
t1=$(($x + 4))
|
||||||
|
t2=$(echo $(( 010 + 0x10 )))
|
||||||
|
[ "$t1" = "9" ] || fail 61
|
||||||
|
[ "$t2" = "24" ] || fail 62
|
||||||
|
|
||||||
|
|
||||||
|
t=$(echo hello ^ cat)
|
||||||
|
[ "$t" = "hello ^ cat" ] || fail 65
|
||||||
|
|
||||||
|
|
||||||
|
t1=$(for d in this-glob-does-*-not-exist; do echo "$d"; done)
|
||||||
|
t2=$(for d in this-glob-does-*-not-exist; do echo "$d"; done)
|
||||||
|
|
||||||
|
|
||||||
|
# http://www.gnu.org/software/hello/manual/autoconf/Assignments.html
|
||||||
|
false || foo=bar; [ "$?" = 0 ] || fail 71
|
||||||
|
foo=`exit 1`; [ "$?" != 0 ] || fail 72
|
||||||
|
|
||||||
|
|
||||||
|
# http://www.gnu.org/software/hello/manual/autoconf/Shell-Functions.html
|
||||||
|
f1() { echo 1; }
|
||||||
|
f2(){ echo 2;}
|
||||||
|
f3()(echo 3)
|
||||||
|
f4()if true; then echo 4; fi
|
||||||
|
f5() ( exit 5 )
|
||||||
|
[ "$(f1)" = 1 ] || fail 81
|
||||||
|
[ "$(f2)" = 2 ] || fail 82
|
||||||
|
[ "$(f3)" = 3 ] || fail 83
|
||||||
|
[ "$(f4)" = 4 ] || fail 84
|
||||||
|
f5 && fail 85
|
||||||
|
f6() (
|
||||||
|
f6b() { return 1; }
|
||||||
|
set -e
|
||||||
|
f6b
|
||||||
|
fail 86
|
||||||
|
)
|
||||||
|
f6
|
||||||
|
f7() { :; }; f7=; f7 || fail 87
|
||||||
|
a=
|
||||||
|
f8() { echo $a; };
|
||||||
|
t8=$(a=1 f8)
|
||||||
|
[ "$t8" = "1" ] || fail 88
|
||||||
|
|
||||||
|
|
||||||
|
# http://www.gnu.org/software/hello/manual/autoconf/Limitations-of-Builtins.html
|
||||||
|
. /dev/null || fail 90
|
||||||
|
(! : | :) && fail 91 || true
|
||||||
|
(! { :; }) && fail 92 || true
|
||||||
|
t3=none
|
||||||
|
case frog.c in
|
||||||
|
(*.c) t3=c ;;
|
||||||
|
(*) t3=all ;;
|
||||||
|
esac
|
||||||
|
[ "$t3" = "c" ] || fail 93
|
||||||
|
t4=$(echo '\n' | wc -l)
|
||||||
|
[ "$t4" -eq 1 ] || warn 94
|
||||||
|
f5() {
|
||||||
|
for arg; do
|
||||||
|
echo $arg
|
||||||
|
done
|
||||||
|
}
|
||||||
|
t5=$(f5 a=5 b c)
|
||||||
|
[ "$t5" = "a=5
|
||||||
|
b
|
||||||
|
c" ] || fail 95
|
||||||
|
t6=$(printf -- '%d %d' 5 6)
|
||||||
|
[ "$t6" = "5 6" ] || fail 96
|
||||||
|
echo 'word\ game stuff' >shellfile
|
||||||
|
read t7a t7b <shellfile
|
||||||
|
read -r t8a t8b <shellfile
|
||||||
|
[ "$t7a" = 'word game' ] || fail 97
|
||||||
|
[ "$t7b" = 'stuff' ] || fail 97
|
||||||
|
[ "$t8a" = 'word\' ] || fail 98
|
||||||
|
[ "$t8b" = 'game stuff' ] || fail 98
|
||||||
|
test -e shellfile || fail 99
|
||||||
|
[ "-a" = "-a" ] || fail 100
|
||||||
|
[ "-a" != "-b" ] || fail 101
|
||||||
|
[ "-a" -a "!" ] || fail 102
|
||||||
|
(unset foo && unset foo && unset foo) || fail 103
|
||||||
|
|
||||||
|
# http://www.gnu.org/software/hello/manual/autoconf/Limitations-of-Usual-Tools.html
|
||||||
|
rm -f || fail 110
|
||||||
|
|
||||||
|
exit $FAIL
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
redo-ifchange all
|
redo-ifchange all
|
||||||
./hello >&2
|
./hello >&2
|
||||||
redo deltest deltest2 test.args test2.args passfailtest chdirtest \
|
redo nonshelltest shelltest \
|
||||||
|
deltest deltest2 test.args test2.args passfailtest chdirtest \
|
||||||
curse/test deps/test "space dir/test" modetest makedir2 \
|
curse/test deps/test "space dir/test" modetest makedir2 \
|
||||||
silencetest touchtest stamp/test alwaystest ifcreate-test
|
silencetest touchtest stamp/test alwaystest ifcreate-test
|
||||||
|
|
|
||||||
2
test.do
2
test.do
|
|
@ -1,2 +1,2 @@
|
||||||
redo-ifchange Documentation/all
|
redo-ifchange _all
|
||||||
redo t/test
|
redo t/test
|
||||||
|
|
|
||||||
13
vars_init.py
13
vars_init.py
|
|
@ -5,10 +5,17 @@ def init(targets):
|
||||||
# toplevel call to redo
|
# toplevel call to redo
|
||||||
exenames = [os.path.abspath(sys.argv[0]),
|
exenames = [os.path.abspath(sys.argv[0]),
|
||||||
os.path.realpath(sys.argv[0])]
|
os.path.realpath(sys.argv[0])]
|
||||||
if exenames[0] == exenames[1]:
|
|
||||||
exenames = [exenames[0]]
|
|
||||||
dirnames = [os.path.dirname(p) for p in exenames]
|
dirnames = [os.path.dirname(p) for p in exenames]
|
||||||
os.environ['PATH'] = ':'.join(dirnames) + ':' + os.environ['PATH']
|
trynames = ([os.path.abspath(p+'/../lib/redo') for p in dirnames] +
|
||||||
|
[p+'/redo-sh' for p in dirnames] +
|
||||||
|
dirnames)
|
||||||
|
seen = {}
|
||||||
|
dirs = []
|
||||||
|
for k in trynames:
|
||||||
|
if not seen.get(k) and os.path.exists('%s/.' % k):
|
||||||
|
seen[k] = 1
|
||||||
|
dirs.append(k)
|
||||||
|
os.environ['PATH'] = ':'.join(dirs) + ':' + os.environ['PATH']
|
||||||
os.environ['REDO'] = os.path.abspath(sys.argv[0])
|
os.environ['REDO'] = os.path.abspath(sys.argv[0])
|
||||||
|
|
||||||
if not os.environ.get('REDO_BASE'):
|
if not os.environ.get('REDO_BASE'):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue