summaryrefslogtreecommitdiff
path: root/tests/t0055-forced-files.sh
diff options
context:
space:
mode:
authorkx <kx@radix-linux.su>2026-10-01 12:02:28 +0300
committerkx <kx@radix-linux.su>2026-10-01 12:02:28 +0300
commitba1b04d64bdfaf377915b22f77520216ebe47674 (patch)
tree044343035c28b2b04ca9725f26e9b7c39342d19c /tests/t0055-forced-files.sh
parent124b140798456778e2a96851cc7268aa9a726698 (diff)
downloadmcpu-cpp-1.0.2.tar.xz
Version 1.0.21.0.2
Diffstat (limited to 'tests/t0055-forced-files.sh')
-rwxr-xr-xtests/t0055-forced-files.sh242
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