-
Notifications
You must be signed in to change notification settings - Fork 5
/
compile.sh
executable file
·143 lines (107 loc) · 3.5 KB
/
compile.sh
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
#!/usr/bin/env bash
# TODO: support nested [post-pass:] blocks
function main
{
exec >&2
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <input> <output> [compiler_flags...]"; exit 1
fi
local compiler="${SHELLSCRIPTLOADER_COMPILER:-$(dirname "$0")/contrib/shellscriptloader-0.1.1/compiler}"
if ! [[ -e "$compiler" ]]; then
echo "error: Compiler script \"$compiler\" does not exist. Specify path using SHELLSCRIPTLOADER_COMPILER"; exit 2
fi
local input="$1"; shift
local output="$1"; shift
local flags=""
while [[ $# -gt 0 ]]; do
flags="$flags $1"; shift
done
cp "$input" "$output"
echo "pre-pass: replace alias fn (bootstrap::finish -> loader_finish)"
sed -i 's/bootstrap::finish/loader_finish/' "$output"
local execline="gawk -f $compiler --$flags -o ${output}.out $output"
echo "$execline"
eval "$execline"
mv "${output}.out" "$output"
if [[ $? -eq 0 ]]; then
chmod +x "$output"
fi
IFS=$'\n'
local -i pass=0
# remove unused function definitions
while (( pass++ <= 5 ))
do
local fn_name unused_fn inside_fn
local -i line_no=0 unused_line_start=0
cp "$output" "${output}.min"
# shellcheck disable=2094
while read -r line; do
let line_no++
# check for function body close tag
if $inside_fn; then
if [[ "$line" == "}" ]]; then
inside_fn='false'
# remove function body
if [[ "$unused_fn" ]]; then
echo "post-pass #${pass}: Removing unused function '$unused_fn' (lines ${unused_line_start}-${line_no})"
sed -i "${unused_line_start},${line_no}s/.*//" "${output}.min"
unset unused_fn
fi
fi
continue
fi
# check for function body open statement
fn_name=$(sed -nr 's/^function ([^ ]+) ?\{?$/\1/p' <<< "$line")
if ! [[ "$fn_name" ]]; then
continue
fi
inside_fn='true'
# check for any occurrences of fn_name other than the definition
if ! grep -v "function $fn_name" "${output}.min" | egrep -q "( |trap .*|\(|\`|\||^)${fn_name}( |\)|\`|'|\||;|>|$)"; then
unused_fn="$fn_name"
unused_line_start=$line_no
fi
done < "$output"
mv "${output}.min" "$output"
done
cp "$output" "${output}.min"
# remove code blocks that depends on unused functions
# test '[post-pass:require-fn=log::defer]'
# ....
# test '[/post-pass]'
local -i line_no=0 block_start=0
local require_fn
while read -r line; do
let line_no++
if [[ "$line" == "test '[/post-pass:require-fn]'" ]]; then
if [[ $block_start -gt 0 ]]; then
echo "post-pass: Removing code block depending on '${require_fn}' (lines ${block_start}-${line_no})"
sed -i "${block_start},${line_no}s/.*//" "${output}.min"
let block_start=0
else
sed -i "${line_no}s/.*//" "${output}.min"
fi
continue
elif [[ $block_start -gt 0 ]]; then
continue
fi
require_fn=$(sed -nr 's/test .\[post-pass:require-fn=(.*)\]/\1/p' <<< "$line" | tr -d "'")
if ! [[ "$require_fn" ]]; then
continue
fi
# look for the function body
if egrep -q "function[ ]*$require_fn" "${output}.min"; then
sed -i "${line_no}s/.*//" "${output}.min"
else
let block_start=line_no
fi
done < "$output"
mv "${output}.min" "$output"
# Strip bootstrap
sed -r -i '/source[ ]+bootstrap.sh/d' "$output"
# Strip consecutive blank lines
# sed -i 'N; /^\n$/d; P; D' "$output"
# Strip blank lines
sed -i '/^$/d' "$output"
}
main "$@"