I have a very simple directory structure:
.
├── libs
│ └── foo
│ ├── BUILD
│ ├── include
│ │ └── foo
│ │ └── func.h
│ └── src
│ └── func.cxx
└── WORKSPACE
With func.h
:
#pragma once
int square(int );
And func.cxx
:
#include <foo/func.h>
int square(int i) { return i * i; }
And BUILD
:
cc_library(
name = "foo",
srcs = ["src/func.cxx"],
hdrs = ["include/foo/func.h"],
visibility = ["//visibility:public"],
)
This fails to build:
$ bazel build //libs/foo
INFO: Analysed target //libs/foo:foo (0 packages loaded).
INFO: Found 1 target...
ERROR: /home/brevzin/sandbox/bazel/libs/foo/BUILD:1:1: C++ compilation of rule '//libs/foo:foo' failed (Exit 1)
libs/foo/src/func.cxx:1:22: fatal error: foo/func.h: No such file or directory
#include <foo/func.h>
^
compilation terminated.
Target //libs/foo:foo failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 0.299s, Critical Path: 0.02s
FAILED: Build did NOT complete successfully
How do I set the include path properly? I tried using include_prefix
(whether include
or include/foo
) but that didn't change the behavior.
Hmm, the tricky part about including headers from another places is that you have to specify the header file from its relative location according to the workspace (where the WORKSPACE
file resides).
Moreover, you should not use the angle-bracket including style #include <a/b.h>
unless you are including the system header files.
The related specifications for #include
can be found here: https://docs.bazel.build/versions/master/bazel-and-cpp.html#include-paths
TL;DR The only change you need to make to your func.cxx
file is that, change the first line to #include "libs/foo/include/foo/func.h"
.
And then, when you run bazel build //...
(build all targets in this workspace, similar to make
makes all
) from the root of the workspace, you will encounter no error.
However, this is not the only way that can solve your problem.
Another way to resolve this issue that does not involve changing the source code include statement would be specifying the include path in the attribute of the rule cc_library
.
Which means, you can make changes to your BUILD
in path libs/foo
to make it look like this:
cc_library(
name = "foo",
srcs = ["src/func.cxx"],
hdrs = ["include/foo/func.h"],
copts = ["-Ilibs/foo/include"], # This is the key part
visibility = ["//visibility:public"],
)
After this change, the compiler will be able to figure out where to find the header file(s), and you don't have to change your source code, yay.
Related information can be found here: https://docs.bazel.build/versions/master/cpp-use-cases.html#adding-include-paths
Nevertheless, there is another hacky way to solve your problem, however, it involves in more changes to your code.
It makes uses of the rule cc_inc_library
.
The rule cc_inc_library
will strips prefix
attribute passed to this rule from the relative path of the header files specified in the hdrs
attribute.
The example on the website is a little confusing, your code and directory structure will yield a much better demonstration purpose.
In your case, you have to modify your BUILD
file under libs/foo
to something that looks like this:
cc_library(
name = "foo",
srcs = ["src/func.cxx"],
deps = [":lib"],
copts = ["-Ilibs/foo/include"],
visibility = ["//visibility:public"],
)
cc_inc_library(
name = "lib",
hdrs = ["include/foo/func.h"],
prefix = "include/foo",
)
In your case, the header file func.h
which has a relative path from the package libs/foo
as include/foo/func.h
, which is specified in the hdrs
attribute.
Since it has a relative path to the root of the workspace as libs/foo/include/foo/func.h
, and the prefix
attribute in the cc_inc_library
is specified as include/foo
: the value include/foo
will be stripped form lib/foo/include/foo/func.h
, making it libs/foo/func.h
.
So, now you can include this header file in your func.cxx
as #include "libs/foo/func.h"
.
And now, bazel will not report error saying that it was not able to find the header file.
You can find the information about this rule at: https://docs.bazel.build/versions/master/be/c-cpp.html#cc_inc_library. However, as stated above, the explanation is confusing at best, possibly because the documentation for it is out of date.
I was puzzled by the office explanation on bazel.build
for quite some time until I read the source code for this rule at: https://github.com/bazelbuild/bazel/blob/f20ae6b20816df6a393c6e8352befba9b158fdf4/src/main/java/com/google/devtools/build/lib/rules/cpp/CcIncLibrary.java#L36-L50
The comment for the actual code that implements the function does a much, much better job at explaining what this rule actually does.
cc_inc_library
rule has been deprecated since Bazel release 0.12.
Use cc_library
approach instead.
See: https://blog.bazel.build/2018/04/11/bazel-0.12.html
What you really want here is strip_include_prefix
:
cc_library(
name = "foo",
srcs = ["src/func.cxx"],
hdrs = ["include/foo/func.h"],
# Here!
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)
This will make the headers accessible via:
#include "foo/func.h"
This attribute has been available since at least Bazel 0.17.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With