When --features=external_include_paths is enabled, rules_cc appears to add the external repository root itself to external_includes, which is later emitted as -isystem <external repo root>. This can make non-header files in an external repository visible to angle-bracket includes.
In particular, on case-insensitive filesystems this can break libc++ includes. If an external repository contains a file named VERSION, and the compile action also includes -isystem external/<repo>+, libc++'s #include <version> can resolve to that repository's VERSION file instead of the standard library <version> header.
Example observed in a downstream build using libtiff through OpenCV on macOS (see this issue):
-isystem external/libtiff+
-isystem bazel-out/darwin_arm64-fastbuild/bin/external/libtiff+
-isystem external/libtiff+/libtiff
-isystem bazel-out/darwin_arm64-fastbuild/bin/external/libtiff+/libtiff
Only the libtiff subdirectory entries are expected from libtiff's includes = ["libtiff"]. The root entries come from rules_cc's external include path reclassification.
The problematic code appears to be in cc/private/compile/cc_compilation_helper.bzl:
external_include_dirs.append(repo_path)
external_include_dirs.append(gen_include_dir)
external_include_dirs.append(bin_include_dir)
external_include_dirs.extend(quote_include_dirs)
external_include_dirs.extend(system_include_dirs)
external_include_dirs.extend(include_dirs)
The first three entries are implicit quote roots for the target's own repository. Reclassifying them as public system include paths for external repositories exposes the entire repository root to downstream compilation.
This is important because external_include_paths is very useful for treating third-party headers as system headers while still keeping warnings-as-errors for first-party code. Disabling the feature works around this specific failure but loses that behavior globally.
A possible fix is to keep converting explicit public include directories (includes, system_includes, and relevant configured include dirs) into external_includes, but avoid exporting the implicit external repository root/default quote include roots as system include paths to downstream dependents.
When
--features=external_include_pathsis enabled,rules_ccappears to add the external repository root itself toexternal_includes, which is later emitted as-isystem <external repo root>. This can make non-header files in an external repository visible to angle-bracket includes.In particular, on case-insensitive filesystems this can break libc++ includes. If an external repository contains a file named
VERSION, and the compile action also includes-isystem external/<repo>+, libc++'s#include <version>can resolve to that repository'sVERSIONfile instead of the standard library<version>header.Example observed in a downstream build using
libtiffthrough OpenCV on macOS (see this issue):Only the
libtiffsubdirectory entries are expected fromlibtiff'sincludes = ["libtiff"]. The root entries come fromrules_cc's external include path reclassification.The problematic code appears to be in
cc/private/compile/cc_compilation_helper.bzl:The first three entries are implicit quote roots for the target's own repository. Reclassifying them as public system include paths for external repositories exposes the entire repository root to downstream compilation.
This is important because
external_include_pathsis very useful for treating third-party headers as system headers while still keeping warnings-as-errors for first-party code. Disabling the feature works around this specific failure but loses that behavior globally.A possible fix is to keep converting explicit public include directories (
includes,system_includes, and relevant configured include dirs) intoexternal_includes, but avoid exporting the implicit external repository root/default quote include roots as system include paths to downstream dependents.