IIUC "layering check" is a clever combination of using clang's C++ modules with automatically constructing module maps for each C++ target based on the dependencies declared in BUILD files.
So, C++ modules, without the C++ code having to know that we're using modules. :)
The mapping works like this:
private *.h files in "srcs" => private textual header declarations in cppmap
public *.h files in "hdrs" => textual header declarations in cppmap
other targets in "deps" => use declarations in cppmap and a -fmodule-map-file=${target}.cppmap flags in the generated clang command
Let's say you have these target definitions:
# BUILD
cc_binary(
name = "a",
srcs = ["a.cc", "a.h"],
deps = [":b"],
)
cc_library(
name = "b",
hdrs = ["b.h"],
deps = [":c"],
)
cc_library(
name = "c",
srcs = ["c.cc"],
hdrs = ["c.h"],
)
The targets would get converted to these C++ module maps:
# a.cppmap
module "a" {
export *
private textual header "a.h"
use "b"
}
extern module "b" "b.cppmap"
# b.cppmap
module "b" {
export *
textual header "b.h"
use "c"
}
extern module "c" "c.cppmap"
# c.cppmap
module "c" {
export *
textual header "c.h"
}
The sources & headers look like this:
// a.cc
#include "a.h"
int main() { fc(); }
// a.h
#include "c.h"
// b.h
#include "c.h"// c.h
void fc();
Our target "a" only depends on the other target "b", but it includes a header file from "c".
Thanks to the generated clang command looking like this:
clang \
-c a.cc \
-o a.o \
-fmodule-name=a \
-fmodule-map-file=a.cppmap \
-fmodules-strict-decluse \
-Wprivate-header \
-fmodule-map-file=b.cppmap
# Note the missing -fmodule-map-file=c.cppmap due to themissing dependency on "c"
clang will complain that a.cc is including c.h, despite "a" not declaring a direct dependency on "c":
./a.h:1:10: error: module a does not directly depend on a module exporting 'c.h', which is part of indirectly-used module c
1 | #include "c.h"
| ^
This is the "layering check" feature in a nutshell.
Cheers,
Philipp