#!/bin/sh set -eu base="${TMPDIR-/tmp}/mcpu-cpp-deps-$$" mkdir -p "$base/user" "$base/sys" "$base/cfg-user" "$base/cfg-sys" "$base/after" "$base/cfg-after" trap 'rm -rf "$base"' EXIT HUP INT TERM cat > "$base/config.conf" < "$base/main.c" <<'EOT' #include "local.h" #include "local-alias.h" #include #include #include "sys-explicit.h" #include #include #include #include #include SHOULD_NOT_APPEAR_IN_DEP_OUTPUT EOT cat > "$base/local.h" <<'EOT' #pragma once #line 100 "logical-local.h" LOCAL_BODY EOT ln "$base/local.h" "$base/local-alias.h" cat > "$base/user/user.h" <<'EOT' USER_HEADER EOT cat > "$base/cfg-user/cfg-user.h" <<'EOT' CONFIG_USER_HEADER EOT cat > "$base/user/shared.h" <<'EOT' SHARED_USER_HEADER EOT cat > "$base/user/transitive-only.h" <<'EOT' TRANSITIVE_USER_HEADER EOT cat > "$base/sys/sys-explicit.h" <<'EOT' #include #include #include "sys-child.h" EOT cat > "$base/sys/sys-child.h" <<'EOT' SYSTEM_CHILD EOT cat > "$base/cfg-sys/sys-config.h" <<'EOT' SYSTEM_CONFIG EOT cat > "$base/after/after-explicit.h" <<'EOT' AFTER_EXPLICIT EOT cat > "$base/cfg-after/after-config.h" <<'EOT' AFTER_CONFIG EOT cat > "$base/user/same.h" <<'EOT' USER_WRAPPER #include_next EOT cat > "$base/sys/same.h" <<'EOT' SYSTEM_WRAPPED EOT "$MCPU_CPP" --config-file "$base/config.conf" \ -I "$base/user" -isystem "$base/sys" -idirafter "$base/after" \ -M "$base/main.c" > "$base/M.out" # -M emits only a make rule and includes every physical dependency. grep "^main\\.o:" "$base/M.out" >/dev/null ! grep 'SHOULD_NOT_APPEAR_IN_DEP_OUTPUT' "$base/M.out" >/dev/null for f in \ "$base/main.c" "$base/local.h" "$base/user/user.h" \ "$base/cfg-user/cfg-user.h" "$base/sys/sys-explicit.h" \ "$base/user/transitive-only.h" "$base/user/shared.h" \ "$base/sys/sys-child.h" "$base/cfg-sys/sys-config.h" \ "$base/after/after-explicit.h" "$base/cfg-after/after-config.h" \ "$base/user/same.h" "$base/sys/same.h" do grep -F "$f" "$base/M.out" >/dev/null || { echo "-M omitted dependency: $f" >&2 exit 1 } done # local-alias.h is a hard link to local.h and must not create a duplicate # physical dependency. The logical #line name is not a dependency either. ! grep -F "$base/local-alias.h" "$base/M.out" >/dev/null ! grep -F 'logical-local.h' "$base/M.out" >/dev/null "$MCPU_CPP" --config-file "$base/config.conf" \ -I "$base/user" -isystem "$base/sys" -idirafter "$base/after" \ -MM "$base/main.c" > "$base/MM.out" grep "^main\\.o:" "$base/MM.out" >/dev/null for f in "$base/main.c" "$base/local.h" "$base/user/user.h" \ "$base/cfg-user/cfg-user.h" "$base/user/same.h" "$base/user/shared.h" do grep -F "$f" "$base/MM.out" >/dev/null || { echo "-MM omitted user dependency: $f" >&2 exit 1 } done # Quoting style does not define system-ness. sys-explicit.h was included # with quotes but found in -isystem, and all descendants reachable only from # that system header are excluded. -idirafter/config AFTER are system class. for f in "$base/sys/sys-explicit.h" "$base/user/transitive-only.h" \ "$base/sys/sys-child.h" "$base/cfg-sys/sys-config.h" \ "$base/after/after-explicit.h" "$base/cfg-after/after-config.h" \ "$base/sys/same.h" do if grep -F "$f" "$base/MM.out" >/dev/null; then echo "-MM kept system dependency: $f" >&2 exit 1 fi done # The same physical shared.h is first reached transitively from a system # header, then directly from main. The direct user inclusion makes it a user # dependency, so -MM must retain it (checked above). # Normal mcpu-cpp output-file syntax remains usable for dependency output. "$MCPU_CPP" --config-file "$base/config.conf" -I "$base/user" \ -isystem "$base/sys" -idirafter "$base/after" \ -MM "$base/main.c" -o "$base/deps.mk" test -s "$base/deps.mk" grep '^main\.o:' "$base/deps.mk" >/dev/null # stdin follows GNU cpp's useful default target convention. printf 'int x;\n' | "$MCPU_CPP" --no-config -M > "$base/stdin.dep" grep '^-: -$' "$base/stdin.dep" >/dev/null