1#!/bin/sh
2
3set -u
4
5self_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
6repo_dir=$(CDPATH= cd -- "$self_dir/.." && pwd)
7shin_bin=${SHIN:-"$repo_dir/shin"}
8ninja_bin=${NINJA:-ninja}
9
10makefile=
11makefile_set=0
12workdir=
13dryrun=0
14jobs=
15targets=
16vars=
17pass_flags=
18
19lie() {
20 printf '%s\n' 'GNU Make 4.4'
21}
22
23notgts() {
24 printf '%s\n' "${0##*/}: *** No targets. Stop." >&2
25 return 2
26}
27
28probemk() {
29 content=$(cat -- "$makefile" 2>/dev/null || true)
30
31 case $content in
32 *'$(info $(MAKE))'*)
33 printf '%s\n' "$0"
34 return 0
35 ;;
36 esac
37 case $content in
38 *'$(info $(SHELL))'*)
39 printf '%s\n' /bin/sh
40 return 0
41 ;;
42 esac
43 case $content in
44 *'$(info $(.FEATURES))'*)
45 printf '%s\n' 'order-only target-specific second-expansion'
46 return 0
47 ;;
48 esac
49 case $content in
50 *'$(info AR=$(AR))'*|*'$(info CC=$(CC))'*)
51 case $content in
52 *'$(info AR=$(AR))'*) printf '%s\n' 'AR=ar' ;;
53 esac
54 case $content in
55 *'$(info CC=$(CC))'*) printf '%s\n' 'CC=cc' ;;
56 esac
57 return 0
58 ;;
59 esac
60 if [ ! -s "$makefile" ]; then
61 notgts
62 return $?
63 fi
64 return 1
65}
66
67default_target() {
68 sed -n 's/^default //p' build.ninja 2>/dev/null | sed -n '1p'
69}
70
71uptodate_target() {
72 if [ -n "$targets" ]; then
73 printf '%s\n' "$targets" | sed -n '1p'
74 else
75 default_target
76 fi
77}
78
79delayed_only() {
80 printf '%s\n' "$1" | while IFS= read -r line; do
81 case $line in
82 '') continue ;;
83 '['*'] '*) line=${line#*] } ;;
84 esac
85 case $line in
86 *' # __shin_info__')
87 :
88 ;;
89 *)
90 return 1
91 ;;
92 esac
93 done
94}
95
96nowork_only() {
97 [ "$1" = "ninja: no work to do." ]
98}
99
100rewrite_ninja_error() {
101 line=$1
102 case $line in
103 "ninja: error: '"*", needed by '"*", missing and no known rule to make it")
104 rest=${line#"ninja: error: '"}
105 target=${rest%%"', needed by '"*}
106 rest=${rest#*"', needed by '"}
107 needer=${rest%%"', missing and no known rule to make it"}
108 printf "%s: *** No rule to make target '%s', needed by '%s'. Stop.\n" \
109 "${0##*/}" "$target" "$needer"
110 return 0
111 ;;
112 "ninja: error: unknown target '"*"'")
113 target=${line#"ninja: error: unknown target '"}
114 target=${target%"'"}
115 printf "%s: *** No rule to make target '%s'. Stop.\n" \
116 "${0##*/}" "$target"
117 return 0
118 ;;
119 esac
120 return 1
121}
122
123while [ "$#" -gt 0 ]; do
124 case "$1" in
125 -v|--version)
126 lie
127 exit 0
128 ;;
129 -s)
130 shift
131 ;;
132 -f)
133 makefile=$2
134 makefile_set=1
135 shift 2
136 ;;
137 -sf|-fs)
138 makefile=$2
139 makefile_set=1
140 shift 2
141 ;;
142 -C)
143 workdir=$2
144 shift 2
145 ;;
146 -j)
147 jobs=$2
148 shift 2
149 ;;
150 -j*)
151 jobs=${1#-j}
152 shift
153 ;;
154 -n)
155 dryrun=1
156 shift
157 ;;
158 -e)
159 pass_flags=${pass_flags}${pass_flags:+'
160'}$1
161 shift
162 ;;
163 --no-print-directory|--warn-undefined-variables|-r|-R|-k|-B|-i|-q|-t)
164 shift
165 ;;
166 --)
167 shift
168 while [ "$#" -gt 0 ]; do
169 targets=${targets}${targets:+'
170'}$1
171 shift
172 done
173 break
174 ;;
175 -*)
176 shift
177 ;;
178 *)
179 case $1 in
180 *=*)
181 vars=${vars}${vars:+'
182'}$1
183 ;;
184 *)
185 targets=${targets}${targets:+'
186'}$1
187 ;;
188 esac
189 shift
190 ;;
191 esac
192done
193
194if [ -n "${workdir}" ]; then
195 cd -- "$workdir" || exit 2
196fi
197
198if [ -z "$makefile" ]; then
199 for cand in GNUmakefile makefile Makefile; do
200 if [ -f "$cand" ]; then
201 makefile=$cand
202 break
203 fi
204 done
205 if [ -z "$makefile" ]; then
206 makefile=Makefile
207 fi
208fi
209
210probemk
211probe_rc=$?
212if [ "$probe_rc" -eq 0 ]; then
213 exit 0
214fi
215if [ "$probe_rc" -eq 2 ]; then
216 exit 2
217fi
218
219if [ "$makefile_set" -eq 1 ]; then
220 set -- "$shin_bin" -f "$makefile"
221else
222 set -- "$shin_bin"
223fi
224if [ -n "$pass_flags" ]; then
225 oldifs=$IFS
226 IFS='
227'
228 for flag in $pass_flags; do
229 set -- "$@" "$flag"
230 done
231 IFS=$oldifs
232fi
233if [ -n "$vars" ]; then
234 oldifs=$IFS
235 IFS='
236'
237 for var in $vars; do
238 set -- "$@" "$var"
239 done
240 IFS=$oldifs
241fi
242if [ -n "$targets" ]; then
243 oldifs=$IFS
244 IFS='
245'
246 for target in $targets; do
247 set -- "$@" "$target"
248 done
249 IFS=$oldifs
250fi
251
252if SHIN_PROGNAME="${0##*/}" "$@"; then
253 :
254else
255 rc=$?
256 exit 2
257fi
258
259set -- "$ninja_bin" --quiet -f build.ninja
260if [ -n "${jobs}" ]; then
261 set -- "$@" -j "$jobs"
262fi
263if [ "$dryrun" -ne 0 ]; then
264 set -- "$@" -v -n
265fi
266if [ -n "$targets" ]; then
267 oldifs=$IFS
268 IFS='
269'
270 for target in $targets; do
271 set -- "$@" "$target"
272 done
273 IFS=$oldifs
274fi
275
276if [ "$dryrun" -eq 0 ]; then
277 probe_cmd="$ninja_bin -f build.ninja"
278 if [ -n "${jobs}" ]; then
279 probe_cmd="$probe_cmd -j $jobs"
280 fi
281 oldifs=$IFS
282 IFS='
283'
284 for target in $targets; do
285 probe_cmd="$probe_cmd '$target'"
286 done
287 IFS=$oldifs
288 probe_out=$(eval "$probe_cmd -v -n" 2>&1)
289 probe_status=$?
290 if [ "$probe_status" -ne 0 ]; then
291 if ! rewrite_ninja_error "$probe_out"; then
292 printf '%s' "$probe_out"
293 fi
294 exit 2
295 fi
296 if nowork_only "$probe_out"; then
297 target=$(uptodate_target)
298 if [ -n "$target" ]; then
299 printf "%s: '%s' is up to date.\n" "${0##*/}" "$target"
300 fi
301 exit 0
302 fi
303 if delayed_only "$probe_out"; then
304 run_out=$("$@" 2>&1)
305 run_status=$?
306 if [ -n "$run_out" ]; then
307 printf '%s\n' "$run_out"
308 fi
309 if [ "$run_status" -ne 0 ]; then
310 exit 2
311 fi
312 target=$(uptodate_target)
313 if [ -n "$target" ]; then
314 printf "%s: '%s' is up to date.\n" "${0##*/}" "$target"
315 fi
316 exit 0
317 fi
318fi
319
320NINJA_STATUS= exec "$@"