main shinobi / tests / shin-make
  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 "$@"