I'm trying to Bazel-wrap a vendor-provided SDK for an embedded project. Part of that SDK includes a hardware abstraction layer (HAL), which has a set of identically named C headers and sources, separate for each part supported by the vendor. I was hoping to be able to create targets for each part, and then use select
to include them into a meta-target, which always choses the right dependency, based on the chosen platform
. However, depending on the meta-target is not equivalent: Because of the indirection, the HAL headers are not considered properly depended on, and thus will fail the layering_check
. Is there a way to explicitly re-export the headers of its dependencies in the meta-target?
Sketch:
cc_library(
name = "hal_part_A",
hdrs = ["part_A/hal_regs.h"],
strip_include_prefix = "part_A/",
target_compatible_with = ["//part:part_A"],
)
cc_library(
name = "hal_part_B",
hdrs = ["part_B/hal_regs.h"],
strip_include_prefix = "part_B/",
target_compatible_with = ["//part:part_B"],
)
cc_library(
name = "hal",
deps = select({
"//part:part_A": [":hal_part_A"],
"//part:part_B": [":hal_part_B"],
}),
# <- add in some 'magic' way to re-export the headers declared by the dependencies
)
cc_binary(
name = "main",
srcs = ["main.c"],
deps = [":hal"],
)
If the source file main.c
tries to include hal_regs.h
provided by the HAL, bazel complains with Compiling main.c failed: undeclared inclusion(s) in rule '//:main': 'part_A/hal_regs.h'
(assuming a platform using //part:part_A
was selected), because //:main
doesn't directly depend on hal_part_A
, which exposes that header.
Is there a way to tell bazel that hal
should re-export the headers exported from it's dependencies?
Workarounds that are suboptimal:
- Not using any meta targets like
hal
: Insteadmain
could directly depend on aselect
linking to the corresponding HAL implementations. Disadvantage: Theselect
logic needs to be copied to every dependant. - Explicitly re-exporting all relevant headers from the meta target: The meta target could have explicit
hdrs = select(...)
, again listing all the headers. Things likestrip_include_prefix
complicate this, but can probably be solved. Still, suboptimal due to the duplication between the implementation targets and the meta target.
For quick and dirty... and probably correct to use in this instance, scroll to the bottom for using
alias
instead of wrapping library.Something else isn't probably right,
cc_library
dependencies should be transitive... and as a matter of fact I took your example... filling in few blanks (adding trivial source examples and defined platforms providingconstraint_value
//part:part_A
and//part:part_B
) leaving yourBUILD
file intact... and it "does work":So, perhaps try to debug around a little: platform resolution,
select()
results and even C sources themselves... in fact...Tracing back to your question and the reported failure complaining about... Well, I suspect the problem is how stuff got included? Since
strip_include_prefix = "part_A/"
for part_A (for instance) was used, you should no longer say:but just:
I would presume by some coincidence (and perhaps a little oddity of tree layout) and the fact the sandbox isn't as hermetic as one would sometimes tend to hope and believe (RBE setup is a good way to test the assumptions and expose accidental leaks with much higher probability)... Preprocessor still managed to find
hal_regs.h
searching forpart_A/hal_regs.h
... but bazel (correctly here) detected this as undeclared dependency (which I think is where / what your error is emitted from / for).I may have assumed too much about the context / setup, sorry about that. One quick hack (workaround) that comes to mind (regardless of exact mode of failure) would be to provide macro for the HAL selection, to make the first option in your question a little more palatable. E.g. something like
hal.bzl
:And then consume like:
For the less quick (and probably more correct) answer now considering a little more understanding of the context. This really is a question about
layering_check
feature and how it generates "intermediate" module maps... namely in this case it would do for instance something like this for//:hal
target:Which would not make LLVM happy when
main.c
grabs stuff from what is here would be another hop"//:hal_part_A"
without (generated)//:hal
asking for re-export.This may be relatively young feature and could still see more development (incl. new API to define such use cases)? ATM I suspect this would me diving into it one-self and extending it (trying to upstream the changes).
One more update... and of course another very obvious (that I've missed so far) but possibly viable in this instance workaround, use
alias
toselect()
instead of a wrappingcc_library
:This used to be a bit more problematic when dealing with
constraint_value
, but seems to be OK now... I suspect at least since bzl 5 (or 6?).