forked from jecxjo/redo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
redo
executable file
·213 lines (210 loc) · 6.06 KB
/
redo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/bin/bash
#@+leo-ver=5-thin
#@+node:caminhante.20241004145243.1: * @file redo
#@@first
#@@language shell
#@@tabwidth -2
# An implementation of djb's redo, written in bash shell
# See cr.yp.to/redo.html for more about redo
# This work is placed in the Public Domain
PROGNAME="$(basename "$0")"
PARALLELJOBS=${PARALLELJOBS:=4}
#@+others
#@+node:caminhante.20241004145644.1: ** Msg
function Msg {
local level="$1: "
shift
case "$level" in
info) level=
esac
echo "${level}$*" 1>&2
case "$level" in
abort) exit 111 ;;
esac
}
#@+node:caminhante.20241007003617.1: ** Basename
function Basename {
local bn="${1%%.*}"
echo "${bn##*/}"
}
#@+node:caminhante.20241004145740.1: ** BasenamePath
function BasenamePath {
echo "${1%%.*}"
}
#@+node:caminhante.20241004145733.1: ** Extension
function Extension {
echo "${1#*.}"
}
#@+node:caminhante.20241004145728.1: ** TargetChangesFile
function TargetChangesFile {
echo "${METADIR}/${1}/md5.sum"
}
#@+node:caminhante.20241004145721.1: ** TargetCreatesFile
function TargetCreatesFile {
echo "${METADIR}/${1}/exists.txt"
}
#@+node:caminhante.20241004145710.1: ** AddIfChange
function AddIfChange {
sed -i "\%${1}$% d" "$(TargetChangesFile "$2")"
md5sum "$1" >> "$(TargetChangesFile "$2")"
}
#@+node:caminhante.20241004145705.1: ** AddIfCreate
function AddIfCreate {
sed -i "\%${1}$% d" "$(targetCreatesFile "$2")"
echo "$1" >> "$(targetCreatesFile "$2")"
}
#@+node:caminhante.20241005225255.1: ** DependencySum
function DependencySum {
sed -r "\%${1}$% !d; s%(^.{32}) ${1}%\1%" "$(TargetChangesFile "$2")"
}
#@+node:caminhante.20241004181939.1: ** ClearMetadata
function ClearMetadata {
echo -n > "$(TargetChangesFile "$1")"
echo -n > "$(TargetCreatesFile "$1")"
}
#@+node:caminhante.20241004185656.1: ** InitializeMetadata
function InitializeMetadata {
mkdir -p "${METADIR}/${1}"
touch "$(TargetChangesFile "$1")" "$(TargetCreatesFile "$1")"
}
#@+node:caminhante.20241004145659.1: ** TargetChanged
function TargetChanged {
! md5sum --quiet --check "$(TargetChangesFile "$1")" >/dev/null 2>/dev/null || \
! grep -Eq "${1}$" "$(TargetChangesFile "$1")"
}
#@+node:caminhante.20241004145655.1: ** TargetCantCreate
function TargetCantCreate {
! xargs -a "$(TargetCreatesFile "$1")" -r -I% \[ ! -e "%" \]
}
#@+node:caminhante.20241004213342.1: ** TargetDependencies
function TargetDependencies {
[ -f "$(TargetChangesFile "$1")" ] && \
sed -r 's/^.{32} //' "$(TargetChangesFile "$1")"
}
#@+node:caminhante.20241004214728.1: ** IsTarget
function IsTarget {
local target="$1"
local doPath="$(DoPath "$target")"
[ ! -z "$doPath" ]
}
#@+node:caminhante.20241004174234.1: ** DoPath
function DoPath {
if [ -e "${1}.do" ]; then
echo "${1}.do"
elif [ -e "$(dirname "$1")/default.$(Extension "$1").do" ]; then
echo "$(dirname "$1")/default.$(Extension "$1").do"
elif [ -e "${BUILDDIR}/default.$(Extension "$1").do" ]; then
echo "${BUILDDIR}/default.$(Extension "$1").do"
else
echo ""
fi
}
#@+node:caminhante.20241004201517.1: ** Rebuild
function Rebuild {
local target="$1"
local doPath="$2"
local tmp="${target}--redoing"
local RC=111
mkdir -p "$(dirname "$target")"
Msg "info" "Redoing $target"
ClearMetadata "$target"
AddIfChange "$doPath" "$target"
export BUILDDIR METADIR
if [ -x "$doPath" ]; then
REDO_TARGET="$target" "$doPath" "$(Basename "$target")" "$(BasenamePath "$target")" "$tmp" > "$tmp"
else
REDO_TARGET="$target" sh "$doPath" "$(Basename "$target")" "$(BasenamePath "$target")" "$tmp" > "$tmp"
fi
RC=$?
if [ $RC -ne 0 ]; then
Msg "error" "Dofile exited with a non-zero exit code: $RC"
rm -f "$tmp" "$target"
exit $RC
elif [ -s "$tmp" ]; then
mv "$tmp" "$target"
true
else
rm -f "$tmp" "$target"
false
fi
}
#@+node:caminhante.20241004200445.1: ** Redo
function Redo {
local file="$1"
local doPath="$(DoPath "$file")"
local isTarget=true
[ -z "$doPath" ] && isTarget=false
if $isTarget; then
InitializeMetadata "$file"
{ flock -x 3
if TargetCantCreate "$file"; then
Msg "error" "The following files should be created by a dofile but already exist:"
xargs -a "$(TargetCreatesFile "$file")" -r -I% find "%" 2>/dev/null
exit 111
fi
TargetDependencies "$file" | ( while read dep; do
if IsTarget "$dep" && [ "$dep" != "$file" ]; then
# local oldSum="$(DependencySum "$dep" "$file")"
# Msg "debug" "** oldsum $dep: $oldSum"
REDO_TARGET="$file" Redo "$dep" &
# local actualSum="$(md5sum "$dep" | sed -r 's/(^.{32}).*/\1/')"
# Msg "debug" "** actual $dep: $actualSum"
# if [ "$oldSum" != "$actualSum" ]; then
# Msg "debug" "** dependency $dep changed"
# fi
while [ $(jobs -p | wc -l) -ge $PARALLELJOBS ]; do
Msg "info" "waiting for $PARALLELJOBS processes"
wait -n
done
fi
done; wait; )
false
if TargetChanged "$file"; then
Rebuild "$file" "$doPath" && AddIfChange "$file" "$file"
true
fi
} 3>>"$(TargetCreatesFile "$file")"
else
if [ ! -e "$file" ]; then
Msg "abort" "No dofile found for $file"
else
AddIfChange "$file" "$REDO_TARGET"
true
fi
fi
}
#@-others
case "$PROGNAME" in
redo-ifchange)
[ -z "$REDO_TARGET" ] && Msg "abort" "REDO_TARGET not set"
[ -z "$BUILDDIR" ] && Msg "abort" "BUILDDIR not set"
[ -z "$METADIR" ] && Msg "abort" "METADIR not set"
for dep; do
cd "$BUILDDIR"
dep="$(readlink -m "$dep")"
Redo "$dep" && AddIfChange "$dep" "$REDO_TARGET"
done
;;
redo-ifcreate)
[ -z "$REDO_TARGET" ] && Msg "abort" "REDO_TARGET not set"
[ -z "$BUILDDIR" ] && Msg "abort" "BUILDDIR not set"
[ -z "$METADIR" ] && Msg "abort" "METADIR not set"
for arg; do
cd "$BUILDDIR"
arg="$(readlink -m "$arg")"
[ -e "$dep" ] && Msg "abort" "$dep exists but should be created by a dofile"
Redo "$dep" && AddIfCreate "$dep" "$REDO_TARGET"
done
;;
redo)
for arg; do
arg="$(readlink -m "$arg")"
REDO_TARGET="$arg"
BUILDDIR="$(dirname "$arg")"
METADIR="${BUILDDIR}/.redo"
cd "$BUILDDIR"
Redo "$arg"
done
;;
esac
#@-leo