main deps
 1#!/bin/sh
 2# emit makefile dependencies for a list of C sources using the compiler's .d output. having a bunch of 
 3# autogenerated .d files and -include's is flaky and ugly, and might not work on all make 
 4# implementations. this way you can generate the deps once and ship the makefile. 
 5# it takes a list of files from stdin and prints to stdout. you might also want to append
 6# you CFLAGS/CPPFLAGS if that will change the deps.
 7#
 8# example:
 9#     find . -type f -name '*.c' | CPPFLAGS='-Iinclude' deps >> Makefile
10#
11
12set -eu
13
14cc=${CC:-cc}
15cppflags=${CPPFLAGS:-}
16cflags=${CFLAGS:-}
17tmpbase=${TMPDIR:-/tmp}
18
19cleanup() {
20	rm -rf "$tmpdir"
21}
22
23usage() {
24	printf 'usage: %s [file.c ...]\n' "${0##*/}" >&2
25	printf '       %s < file-list\n' "${0##*/}" >&2
26	exit 2
27}
28
29list() {
30	if [ "$#" -gt 0 ]; then
31		for src do
32			case $src in
33			*.c) printf '%s\n' "$src" ;;
34			*) printf '%s: not a .c file: %s\n' "${0##*/}" "$src" >&2; exit 2 ;;
35			esac
36		done
37		return
38	fi
39
40	while IFS= read -r src; do
41		[ -n "$src" ] || continue
42		case $src in
43		*.c) printf '%s\n' "$src" ;;
44		*) printf '%s: not a .c file: %s\n' "${0##*/}" "$src" >&2; exit 2 ;;
45		esac
46	done
47}
48
49if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
50	usage
51fi
52
53tmpdir=$(mktemp -d "$tmpbase/deps.XXXXXX")
54trap cleanup EXIT HUP INT TERM
55
56count=0
57srcs=$tmpdir/sources
58list "$@" > "$srcs"
59
60if [ ! -s "$srcs" ]; then
61	printf '%s: no .c files provided\n' "${0##*/}" >&2
62	exit 1
63fi
64
65printf '\n'
66printf '#AUTOGENERATED\n'
67
68while IFS= read -r src; do
69	dep=$tmpdir/$count.d
70	obj=${src%.c}.o
71	"$cc" $cppflags $cflags -MMD -MF "$dep" -MT "$obj" -E -o /dev/null "$src"
72	#flatten the block with one target: dep line per prereq
73	awk '
74	BEGIN {
75		rule = ""
76	}
77	{
78		line = $0
79		sub(/\\$/, "", line)
80		rule = rule line " "
81		if ($0 !~ /\\$/) {
82			sub(/^[[:space:]]*/, "", rule)
83			split(rule, parts, ":")
84			target = parts[1]
85			deps = substr(rule, length(target) + 2)
86			n = split(deps, items, /[[:space:]]+/)
87			for (i = 1; i <= n; ++i) {
88				if (items[i] == "")
89					continue
90				print target ": " items[i]
91			}
92			rule = ""
93		}
94	}
95	' "$dep"
96	count=$((count + 1))
97done < "$srcs"