Don't crash on targets in directories that don't exist yet.

The reason we'd crash is that we tried to pre-create a file called
$target.redo.tmp, which wouldn't work because the directory containing
$target didn't exist.

We now try to generate a smarter filename by using the innermost directory
of target that *does* exist.  It's a little messy, but the idea is to make
sure we won't have to rename() across a filesystem boundary if, for example,
there's a mounted filesystem in the middle of the hierarchy somewhere.
This commit is contained in:
Avery Pennarun 2011-03-22 22:09:56 -07:00
commit 2efbbc26b9
3 changed files with 47 additions and 16 deletions

View file

@ -71,8 +71,13 @@ class BuildJob:
def __init__(self, t, sf, lock, shouldbuildfunc, donefunc): def __init__(self, t, sf, lock, shouldbuildfunc, donefunc):
self.t = t # original target name, not relative to vars.BASE self.t = t # original target name, not relative to vars.BASE
self.sf = sf self.sf = sf
self.tmpname1 = '%s.redo1.tmp' % t tmpbase = t
self.tmpname2 = '%s.redo2.tmp' % t while not os.path.isdir(os.path.dirname(tmpbase) or '.'):
ofs = tmpbase.rfind('/')
assert(ofs >= 0)
tmpbase = tmpbase[:ofs] + '__' + tmpbase[ofs+1:]
self.tmpname1 = '%s.redo1.tmp' % tmpbase
self.tmpname2 = '%s.redo2.tmp' % tmpbase
self.lock = lock self.lock = lock
self.shouldbuildfunc = shouldbuildfunc self.shouldbuildfunc = shouldbuildfunc
self.donefunc = donefunc self.donefunc = donefunc
@ -141,7 +146,8 @@ class BuildJob:
dofile, dofile,
basename, # target name (no extension) basename, # target name (no extension)
ext, # extension (if any), including leading dot ext, # extension (if any), including leading dot
os.path.join(basedir, os.path.basename(self.tmpname2)) # temp output file name # temp output file name
state.relpath(os.path.abspath(self.tmpname2), dodir),
] ]
if vars.VERBOSE: argv[1] += 'v' if vars.VERBOSE: argv[1] += 'v'
if vars.XTRACE: argv[1] += 'x' if vars.XTRACE: argv[1] += 'x'

View file

@ -1,7 +1,7 @@
rm -rf t/.redo redo-sh 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" [ -d "$x" ] || rm -f "$x"
done <.do_built done <.do_built
fi fi
[ -z "$DO_BUILT" ] && rm -rf .do_built .do_built.dir [ -z "$DO_BUILT" ] && rm -rf .do_built .do_built.dir

View file

@ -24,6 +24,13 @@ _dirsplit()
dir=${1%$base} dir=${1%$base}
} }
dirname()
(
_dirsplit "$1"
dir=${dir%/}
echo "${dir:-.}"
)
_dirsplit "$0" _dirsplit "$0"
export REDO=$(cd "${dir:-.}" && echo "$PWD/$base") export REDO=$(cd "${dir:-.}" && echo "$PWD/$base")
@ -73,6 +80,7 @@ _find_dofile()
[ -e "$dofile" ] && break [ -e "$dofile" ] && break
[ "$PWD" = "/" ] && break [ "$PWD" = "/" ] && break
target=${PWD##*/}/$target target=${PWD##*/}/$target
tmp=${PWD##*/}/$tmp
prefix=${PWD##*/}/$prefix prefix=${PWD##*/}/$prefix
cd .. cd ..
done done
@ -89,17 +97,17 @@ _run_dofile()
read line1 <"$PWD/$dofile" read line1 <"$PWD/$dofile"
cmd=${line1#"#!/"} cmd=${line1#"#!/"}
if [ "$cmd" != "$line1" ]; then if [ "$cmd" != "$line1" ]; then
/$cmd "$PWD/$dofile" "$@" >"$target.tmp2" /$cmd "$PWD/$dofile" "$@" >"$tmp.tmp2"
else else
:; . "$PWD/$dofile" >"$target.tmp2" :; . "$PWD/$dofile" >"$tmp.tmp2"
fi fi
} }
_do() _do()
{ {
local dir=$1 target=$2 local dir=$1 target=$2 tmp=$3
if [ ! -e "$target" ] || [ -d "$target/." -a ! -e "$target.did" ]; then if [ ! -e "$target" ] || [ -d "$target" -a ! -e "$target.did" ]; then
printf '%sdo %s%s%s%s\n' \ printf '%sdo %s%s%s%s\n' \
"$green" "$DO_DEPTH" "$bold" "$dir$target" "$plain" >&2 "$green" "$DO_DEPTH" "$bold" "$dir$target" "$plain" >&2
echo "$PWD/$target" >>"$DO_BUILT" echo "$PWD/$target" >>"$DO_BUILT"
@ -111,30 +119,47 @@ _do()
echo "do: $target: no .do file" >&2 echo "do: $target: no .do file" >&2
return 1 return 1
fi fi
[ ! -e "$DO_BUILT" ] || : >>"$target.did" [ ! -e "$DO_BUILT" ] || [ ! -d "$(dirname "$target")" ] ||
( _run_dofile "$base" "$ext" "$target.tmp" ) : >>"$target.did"
( _run_dofile "$base" "$ext" "$tmp.tmp" )
rv=$? rv=$?
if [ $rv != 0 ]; then if [ $rv != 0 ]; 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" rm -f "$tmp.tmp" "$tmp.tmp2"
return $rv return $rv
fi fi
mv "$target.tmp" "$target" 2>/dev/null || mv "$tmp.tmp" "$target" 2>/dev/null ||
! test -s "$target.tmp2" || ! test -s "$tmp.tmp2" ||
mv "$target.tmp2" "$target" 2>/dev/null mv "$tmp.tmp2" "$target" 2>/dev/null
rm -f "$target.tmp2" rm -f "$tmp.tmp2"
else else
echo "do $DO_DEPTH$target exists." >&2 echo "do $DO_DEPTH$target exists." >&2
fi fi
} }
# Make corrections for directories that don't actually exist yet.
_dir_shovel()
{
local dir base
xdir=$1 xbase=$2 xbasetmp=$2
while [ ! -d "$xdir" -a -n "$xdir" ]; do
_dirsplit "${xdir%/}"
xbasetmp=${base}__$xbase
xdir=$dir xbase=$base/$xbase
echo "xbasetmp='$xbasetmp'" >&2
done
}
redo() redo()
{ {
for i in "$@"; do for i in "$@"; do
_dirsplit "$i" _dirsplit "$i"
( cd "$dir" && _do "$dir" "$base" ) || return 1 _dir_shovel "$dir" "$base"
dir=$xdir base=$xbase basetmp=$xbasetmp
( cd "$dir" && _do "$dir" "$base" "$basetmp" ) || return 1
done done
} }