diff options
Diffstat (limited to 'tests/t0055-forced-files.sh')
| -rwxr-xr-x | tests/t0055-forced-files.sh | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/tests/t0055-forced-files.sh b/tests/t0055-forced-files.sh new file mode 100755 index 0000000..454fa55 --- /dev/null +++ b/tests/t0055-forced-files.sh @@ -0,0 +1,242 @@ +#!/bin/sh +set -eu +base="${TMPDIR-/tmp}/mcpu-cpp-forced-files-$$" +mkdir -p "$base/project/src" "$base/project/user" "$base/project/sys" +trap 'rm -rf "$base"' EXIT HUP INT TERM + +cat > "$base/project/defs1.h" <<'EOT' +IMACROS1_BODY_MUST_NOT_APPEAR +#if CLI_VALUE != 9 +#error command-line macros were not applied before -imacros +#endif +#if __INCLUDE_LEVEL__ != 1 +#error -imacros must run at include level 1 +#endif +#define ORDER_VALUE 1 +#define FROM_IMACROS1 11 +#include "imacros-child.h" +#lang "as" +#define FROM_AS_LANG 13 +#endlang +EOT + +cat > "$base/project/imacros-child.h" <<'EOT' +IMACROS_CHILD_BODY_MUST_NOT_APPEAR +#if __INCLUDE_LEVEL__ != 2 +#error nested include from -imacros must run at include level 2 +#endif +#define FROM_IMACROS_CHILD 12 +EOT + +cat > "$base/project/defs2.h" <<'EOT' +IMACROS2_BODY_MUST_NOT_APPEAR +#if ORDER_VALUE != 1 +#error -imacros command-line order is broken +#endif +#if FROM_IMACROS_CHILD != 12 || FROM_AS_LANG != 13 +#error -imacros did not retain preprocessing state +#endif +#undef ORDER_VALUE +#define ORDER_VALUE 2 +EOT + +cat > "$base/project/a.h" <<'EOT' +#if ORDER_VALUE != 2 +#error all -imacros files must precede all -include files +#endif +INCLUDE_A_BODY +FORCED_LEVEL __INCLUDE_LEVEL__ +FORCED_BASE __BASE_FILE__ +#undef ORDER_VALUE +#define ORDER_VALUE 3 +EOT + +cat > "$base/project/b.h" <<'EOT' +#if ORDER_VALUE != 3 +#error -include command-line order is broken +#endif +INCLUDE_B_BODY +#undef ORDER_VALUE +#define ORDER_VALUE 4 +EOT + +cat > "$base/project/src/main.c" <<'EOT' +#if ORDER_VALUE != 4 +#error forced files did not complete before primary input +#endif +MAIN_BODY +MAIN_LEVEL __INCLUDE_LEVEL__ +FROM_IMACROS1 FROM_IMACROS_CHILD FROM_AS_LANG CLI_VALUE +EOT + +# Deliberately interleave -include and -imacros. The semantic order is all +# command-line -D/-U first, then all -imacros in their own argv order, then all +# -include in their own argv order, and only then the primary input. +( + cd "$base/project" + "$MCPU_CPP" --no-config \ + -include a.h -DCLI_VALUE=9 -imacros defs1.h \ + -include b.h -imacros defs2.h \ + src/main.c -o "$base/order.out" +) + +! grep 'IMACROS1_BODY_MUST_NOT_APPEAR' "$base/order.out" >/dev/null +! grep 'IMACROS2_BODY_MUST_NOT_APPEAR' "$base/order.out" >/dev/null +! grep 'IMACROS_CHILD_BODY_MUST_NOT_APPEAR' "$base/order.out" >/dev/null +! grep '^#lang ' "$base/order.out" >/dev/null +! grep '^#endlang' "$base/order.out" >/dev/null + +awk ' + /^INCLUDE_A_BODY$/ { a = NR } + /^INCLUDE_B_BODY$/ { b = NR } + /^MAIN_BODY$/ { c = NR } + END { exit !(a && a < b && b < c) } +' "$base/order.out" +grep '^11 12 13 9$' "$base/order.out" >/dev/null +grep '^FORCED_LEVEL 1$' "$base/order.out" >/dev/null +grep '^FORCED_BASE "src/main.c"$' "$base/order.out" >/dev/null +grep '^MAIN_LEVEL 0$' "$base/order.out" >/dev/null +# Each visible forced include returns to the primary file in the line-marker +# stack before another forced include or the primary body continues. +test `grep -c '^# 1 "src/main.c" 2$' "$base/order.out"` -eq 2 + +# A forced command-line file is searched from the current working directory, +# not from the physical directory of the primary input. +cat > "$base/project/cwd-first.h" <<'EOT' +CWD_FORCED_HEADER +EOT +cat > "$base/project/src/cwd-first.h" <<'EOT' +PRIMARY_DIRECTORY_MUST_NOT_WIN +EOT +cat > "$base/project/src/cwd-main.c" <<'EOT' +CWD_MAIN +EOT +( + cd "$base/project" + "$MCPU_CPP" --no-config -include cwd-first.h src/cwd-main.c \ + -o "$base/cwd.out" +) +grep '^CWD_FORCED_HEADER$' "$base/cwd.out" >/dev/null +! grep 'PRIMARY_DIRECTORY_MUST_NOT_WIN' "$base/cwd.out" >/dev/null + +# After CWD, the ordinary quoted include chain is used. A forced file found +# through -I retains its physical provenance, so quoted includes inside it are +# resolved relative to that file's own directory. +cat > "$base/project/user/forced-user.h" <<'EOT' +FORCED_USER_BEGIN +#include "forced-child.h" +FORCED_USER_END +EOT +cat > "$base/project/user/forced-child.h" <<'EOT' +FORCED_USER_CHILD +EOT +( + cd "$base/project" + "$MCPU_CPP" --no-config -I user -include forced-user.h src/cwd-main.c \ + -o "$base/user.out" +) +grep '^FORCED_USER_BEGIN$' "$base/user.out" >/dev/null +grep '^FORCED_USER_CHILD$' "$base/user.out" >/dev/null +grep '^FORCED_USER_END$' "$base/user.out" >/dev/null + +# Search-chain provenance of a forced file is retained for #include_next. +cat > "$base/project/user/wrapped.h" <<'EOT' +FORCED_WRAPPER +#include_next <wrapped.h> +EOT +cat > "$base/project/sys/wrapped.h" <<'EOT' +FORCED_INCLUDE_NEXT +EOT +( + cd "$base/project" + "$MCPU_CPP" --no-config -I user -isystem sys -include wrapped.h \ + src/cwd-main.c -o "$base/next.out" +) +grep '^FORCED_WRAPPER$' "$base/next.out" >/dev/null +grep '^FORCED_INCLUDE_NEXT$' "$base/next.out" >/dev/null + +# -dD does not make normal output from -imacros visible; macro state still +# reaches the primary input. +cat > "$base/project/only-macros.h" <<'EOT' +IMACROS_DD_BODY_MUST_NOT_APPEAR +#define DD_VALUE 77 +EOT +cat > "$base/project/src/dd-main.c" <<'EOT' +DD_VALUE +EOT +( + cd "$base/project" + "$MCPU_CPP" --no-config -dD -imacros only-macros.h src/dd-main.c \ + -o "$base/dd.out" +) +! grep 'IMACROS_DD_BODY_MUST_NOT_APPEAR' "$base/dd.out" >/dev/null +! grep '^#define DD_VALUE 77$' "$base/dd.out" >/dev/null +grep '^77$' "$base/dd.out" >/dev/null + +# Forced files participate in physical dependency tracking. The primary input +# remains the first dependency even though forced files are preprocessed first. +( + cd "$base/project" + "$MCPU_CPP" --no-config -M -DCLI_VALUE=9 \ + -imacros defs1.h -imacros defs2.h -include a.h -include b.h src/main.c \ + > "$base/deps.out" +) +grep '^main\.o:' "$base/deps.out" >/dev/null +first_dep=`sed 's/^[^:]*: //' "$base/deps.out" | awk '{print $1}'` +test "$first_dep" = 'src/main.c' || { + echo 'primary input is not the first forced-file dependency' >&2 + exit 1 +} +for f in defs1.h imacros-child.h defs2.h a.h b.h; do + grep -E "(^|[[:space:]])(\./)?$f([[:space:]]|$)" "$base/deps.out" >/dev/null || { + echo "forced dependency missing: $f" >&2 + exit 1 + } +done + +# A forced file found through -isystem has system dependency class and is +# therefore omitted by -MM while the primary user source remains. +cat > "$base/project/sys/system-forced.h" <<'EOT' +SYSTEM_FORCED_BODY +EOT +( + cd "$base/project" + "$MCPU_CPP" --no-config -MM -isystem sys -include system-forced.h \ + src/cwd-main.c > "$base/mm.out" +) +grep -E '(^|[[:space:]])src/cwd-main\.c([[:space:]]|$)' "$base/mm.out" >/dev/null +! grep -F "$base/project/sys/system-forced.h" "$base/mm.out" >/dev/null + +# Public help advertises both implemented forced-file options. +"$MCPU_CPP" --help > "$base/help" +grep '^ -imacros FILE ' "$base/help" >/dev/null +grep '^ -include FILE ' "$base/help" >/dev/null + +# Both option families require an argument. +for opt in -include -imacros; do + if "$MCPU_CPP" --no-config "$opt" > /dev/null 2>"$base/missing-arg.err"; then + echo "$opt accepted a missing argument" >&2 + exit 1 + fi + grep -- "argument missing after '$opt'" "$base/missing-arg.err" >/dev/null +done + +# Missing forced files are preprocessing errors for both option families. +for opt in -include -imacros; do + if ( + cd "$base/project" + "$MCPU_CPP" --no-config "$opt" no-such-forced-file.h src/cwd-main.c \ + -o "$base/missing.out" 2>"$base/missing.err" + ); then + echo "$opt accepted a missing forced file" >&2 + exit 1 + fi + grep -- "$opt cannot find file 'no-such-forced-file.h'" "$base/missing.err" >/dev/null +done + +# The same forced-file pipeline also applies when the primary source is stdin. +printf 'DD_VALUE\n' | ( + cd "$base/project" + "$MCPU_CPP" --no-config -imacros only-macros.h - +) > "$base/stdin.out" +grep '^77$' "$base/stdin.out" >/dev/null |
