blob: 95c14699c047bdc6474fd91993894b0f8d3320ef (
plain)
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
|
#! /bin/sh
set -euf
## SYNOPSIS
# [debug=true] build compile SRCFILE DSTFILE
# [debug=true] build deps SRCFILE...
build() {
## Load macro definitions.
defmacro_pattern='## usage: \(.*\) -> \([^ ]\+\) \(.*\)'
script='s/^'"$defmacro_pattern"'$/\2_macro='"'"'\1'"'"'/p' \
setf defmacros '$(sed -n "$script" "$%s")' 0
eval "$defmacros"
## Dispatch.
case "$1" in
compile) build_compile "$2" "$3";;
deps) shift; build_deps "$@";;
*) echo "build: $1: bad command" >&2; return 23;;
esac
}
###
### macros
###
## usage: #@include \([0-9A-Za-z_]\+\) -> build_include \1 \2
build_include() {
if buildcache_has "#@include:$2"; then
printf '%da\\\n##include %s: already done\n' $1 $2
else
buildcache_add "#@include:$2"
cat<<EOF
$1a\\
# begin $2
$1r$(build_resolve $2)
$1a\\
# end $2
EOF
fi
}
## usage: #@info -> build_info \1
build_info() { cat<<EOF
$1a\\
# this file was generated by //ship/build\\
# build date: $(date -u --rfc-3339=s)\\
# git describe: $(git describe --always --dirty --abbrev=0)
EOF
}
###
### main subroutines
###
## usage: build_compile SRCFILE DSTFILE
build_compile() {
script='s/^'"$defmacro_pattern"'$/\2_macro/p' \
setf macro_names '$(sed -n "$script" "$%s")' 0
setf unexpanded_macros_pattern \
'$(make_unexpanded_macros_pattern $%s)' macro_names
script='
s/^'"$defmacro_pattern"'$/s:^ *\\([0-9]\\+\\) \1$:\2 \3:/p
$a\
t;s:^ *\\([0-9]\\+\\) .*:echo \\1p:
' \
setf input_parser '$(sed -n "$script" "$%s")' 0
SRCFILE="$1" setf src '$(cat "$%s")' SRCFILE
buildcache_initialize "$2"
while echo "$src" | grep -q "$unexpanded_macros_pattern"; do
setf sedgen '$(echo "$%s" | nl -b a -s \ | sed "$%s")' src input_parser
setf sedscript '$(eval "$%s")' sedgen
setf src '$(echo "$%s" | sed -n "$%s")' src sedscript
done
buildcache_finalize
echo "$src" > "$2"
chmod +x "$2"
}
## usage: build_deps SRCFILE...
# Print all the dependencies of SRCFILE... to stdout. (alphabetic order)
build_deps() {
while test $# -gt 0; do
deps="$(
for f; do
for d in $(sed -n 's:^'"$build_include_macro"'$:\1:p' "$f"); do
build_resolve $d
done
done
)"
set -- $deps
if test $# -gt 0; then
echo "$deps"
fi
done | sort | uniq
}
###
### misc utilities
###
## usage: build_resolve LIBNAME
build_resolve() {
echo "$BUILD_PATH" | tr : \\n |
xargs -I: printf '%s/%s\n' : "$1" |
xargs -I: ls -d : 2>/dev/null |
head -n 1 |
grep . ||
{
echo "build resolve: $1: library not found" >&2
return 23
}
}
## usage: make_unexpanded_macros_pattern BUILD_DIRECTIVES...
make_unexpanded_macros_pattern() {
echo "^\\($(
for macro; do
eval echo \"\$$macro\"
done |
tr \\n \| |
sed 's/|/\\|/'
)\\)$"
}
## usage: setf NAME FMT [ARG...]
setf() {
value_script="$(shift; printf "$@")"
eval "$1=$value_script"
if is_debug_mode; then
eval 'echo "[35m$1=\"$value_script\"[m"'
eval 'echo "'"\$$1"'"' | nl -b a
fi >&2
}
## usage: is_debug_mode
is_debug_mode() {
test "${debug-false}" = true
}
###
### buildcache utilities
###
## usage: buildcache_initialize DESTFILE
buildcache_initialize() {
buildcache="$1.buildcache"
cat /dev/null > "$buildcache"
}
## usage: buildcache_finalize
buildcache_finalize() {
if is_debug_mode; then
rm "$buildcache"
fi
}
## usage: buildcache_has BRE
# Check if buildcache contains a line matching BRE.
buildcache_has() {
grep -q "^$1\$" "$buildcache"
}
## usage: buildcache_add LINE
# Add LINE to buildcache.
buildcache_add() {
echo "$1" >> "$buildcache"
}
###
### main invocation
###
build "$@"
|