Criterion-by-criterion pass over the 24 requirement files for a
minimal-but-complete contract set. Sixteen criteria that carried no
contract a user relies on are deleted (over-enumeration, restatements
of another file's contract, an unobservable same-source-twice rule that
duplicate detection collapses, untestable "may" disclaimers). Seventeen
instance-enumerations become one owned property each - source-extension
lists, kept-flag and link-flag lists, per-shape repeats, the MSVC /link
tail folded into "family-defined link-stage flag sets" - with the lists
retained as subject-matter references and a representative scenario
declared to cover them. Every generalization was checked to imply each
instance it replaces.
About thirty-five acceptance criteria that a real test already guarded
had no Given-When-Then scenario; these are written down now, modeled on
what the tests actually assert, and the implementing tests are tagged.
The genuine coverage gaps are closed with new tests: exact-exit-code
propagation, all-dropped and understood-but-empty exit codes, the
forced-preload rejection (the long-open mode-selection gap), a Fortran
entry, a masquerade launcher recorded under a compiler name, duplicate
output order, header/real-entry dedup, response-file source
participation, output-field path formatting, and reporting-failure
tolerance in wrapper mode. Coverage: 24/24 implemented requirements.
Found by the /requirements skill audit (dogfooding 1038d916).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.3 KiB
title, status
| title | status |
|---|---|
| Handle compiler env vars that contain flags | implemented |
Intent
Compiler environment variables often carry not just the compiler path but a simple trailing flag or two. This is a GNU Make convention and shows up in configure scripts, Makefiles, and Dockerfiles:
CC="gcc -std=c11"CXX="clang++ -stdlib=libc++"CC="/usr/local/bin/gcc -m32"
When the user runs bear -- make with a value of this shape, Bear's
wrapper mode registers a wrapper for the real program and rewrites the
env var so the build still receives the flags.
Anything more elaborate -- flags with embedded whitespace, shell
quoting, metacharacters, command substitutions -- belongs in CFLAGS /
CXXFLAGS / LDFLAGS. Bear splits the env var value on whitespace
and goes no further. The man page points users at CFLAGS for
anything that does not fit that shape.
Acceptance criteria
- Wrapper mode splits a compiler env var into
(program, flags)on whitespace before resolution. - The program token, whether a bare name or an absolute path, follows
the ordinary wrapper-mode resolution (masquerade handling of the
token included, see
interception-wrapper-recursion), and the flag tokens are preserved. - The rewritten env var value is the wrapper path followed by the original flag tokens, joined with single spaces and without shell quoting. When there are no flags, the override is the wrapper path as a bare string, with no quoting added.
- An empty or whitespace-only value is skipped with a warning.
Non-functional constraints
- Platform support: Unix and Unix-like environments -- Linux, macOS,
BSD, and the Unix-like shells hosted on Windows (MSYS2, Git Bash,
WSL). The
CC/CXXconvention is a Unix / GNU Make inheritance. - A flagless value (a bare name such as
CC=gccor an absolute path such asCC=/usr/bin/gcc) and an unset variable follow the ordinary wrapper-mode resolution owned byinterception-wrapper-mechanism; the whitespace split is observable only when flag tokens are present.
Known limitations
- No observable effect on a native Windows build: native Windows build
tooling (MSBuild,
nmake,cmd, PowerShell) does not readCC/CXXfrom the environment.
Testing
Unit tests
Given a wrapper-mode setup with a fake compiler on PATH:
When
CC="fake-cc -std=c11", then the wrapper config is keyed by the compiler basename and points at the real compiler, and the override value forCCequals"<wrapper_path> -std=c11".
Given a wrapper-mode setup with a fake compiler at an absolute path:
When
CC="/abs/path/my-gcc -m32", then the resolved program is/abs/path/my-gcc, and the override preserves-m32.
Given a wrapper-mode setup with no flags (regression guard):
When
CC=/usr/bin/gcc, then the override value equals the wrapper path verbatim (no shell quoting introduced).
Given an empty or whitespace-only value:
When
CC=""orCC=" ", then noCCoverride appears in the build environment, and a warning is emitted.
Given a ccache masquerade directory on PATH and a real compiler past it:
When
CC="gcc -std=c11", then the wrapper config entry points at the real compiler past the masquerade directory, and the override forCCstill contains-std=c11.
Given a Unix-like shell on Windows producing forward-slash paths:
When
CC="C:/tools/fake-cc.exe -DBEAR_TEST=1", then the wrapper is registered forfake-cc.exe, and the override forCCstill contains-DBEAR_TEST=1.
Integration test
Given a C source file and a build script that runs $CC -c test.c:
When the user runs
bearin wrapper mode withCC="<compiler> -DBEAR_TEST_FLAG=1", then the build succeeds, andcompile_commands.jsonhas exactly one entry, and theargumentsarray contains-DBEAR_TEST_FLAG=1.
Notes
- Issue #686 -- bare-name CC resolution.
- Related:
interception-wrapper-mechanism-- the wrapper mode this feature extends. - Related:
interception-wrapper-recursion-- masquerade handling of the program token. - Related:
output-env-derived-flags-- folding include/option env vars into entries, a separate output-side mechanism.
Rationale
- Whitespace split, no shell parser - why the env-var value is split on whitespace instead of shell-parsed.