使用 bazel 构建一个简单的库,修复包含路径

Building a simple library with bazel, fixing include path

我有一个非常简单的目录结构:

.
├── libs
│   └── foo
│       ├── BUILD
│       ├── include
│       │   └── foo
│       │       └── func.h
│       └── src
│           └── func.cxx
└── WORKSPACE

func.h:

#pragma once

int square(int );

func.cxx

#include <foo/func.h>

int square(int i) { return i * i; }

BUILD

cc_library(
    name = "foo",
    srcs = ["src/func.cxx"],
    hdrs = ["include/foo/func.h"],
    visibility = ["//visibility:public"],
)

构建失败:

$ 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

如何正确设置包含路径?我尝试使用 include_prefix(无论是 include 还是 include/foo),但这并没有改变行为。

嗯,包含来自其他地方的头文件的棘手部分是您必须根据工作空间(WORKSPACE 文件所在的位置)从其相对位置指定头文件。

此外,除非包含系统头文件,否则不应使用尖括号包含样式 #include <a/b.h>

#include的相关规格可以在这里找到:https://docs.bazel.build/versions/master/bazel-and-cpp.html#include-paths

TL;DR 您需要对 func.cxx 文件进行的唯一更改是,将第一行更改为 #include "libs/foo/include/foo/func.h".

然后,当您从工作区的根目录 运行 bazel build //...(在此工作区中构建所有目标,类似于 make 生成 all)时,您不会遇到错误。


但是,这不是解决您问题的唯一方法。

另一种不涉及更改源代码包含语句的解决此问题的方法是在规则 cc_library.

的属性中指定包含路径

这意味着,您可以更改路径 libs/foo 中的 BUILD,使其看起来像这样:

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"],
)

此更改后,编译器将能够确定在哪里可以找到头文件,您不必更改源代码,是的。

相关信息可以在这里找到:https://docs.bazel.build/versions/master/cpp-use-cases.html#adding-include-paths


尽管如此,还有另一种 hacky 方法可以解决您的问题,但是,它需要对您的代码进行更多更改。

它使用规则 cc_inc_library

规则 cc_inc_library 将从 hdrs 属性中指定的头文件的相对路径中删除传递给此规则的 prefix 属性。

网站上的示例有点混乱,您的代码和目录结构将产生更好的演示目的。

在您的情况下,您必须将 libs/foo 下的 BUILD 文件修改为如下所示:

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",
)

在您的例子中,头文件 func.h 具有包 libs/foo 的相对路径作为 include/foo/func.h,这是在 hdrs 属性中指定的。
由于它有一个到工作区根目录的相对路径为libs/foo/include/foo/func.h,并且cc_inc_library中的prefix属性被指定为include/foo:值include/foo将从 lib/foo/include/foo/func.h 中剥离,使其成为 libs/foo/func.h.

因此,现在您可以将此头文件包含在您的 func.cxx 中作为 #include "libs/foo/func.h"
而现在,bazel 不会报找不到头文件的错误了。

您可以在以下位置找到有关此规则的信息:https://docs.bazel.build/versions/master/be/c-cpp.html#cc_inc_library。 但是,如上所述,解释充其量是令人困惑的,可能是因为它的文档已过时。

我对 bazel.build 上的办公室解释感到困惑了很长一段时间,直到我阅读了这条规则的源代码:https://github.com/bazelbuild/bazel/blob/f20ae6b20816df6a393c6e8352befba9b158fdf4/src/main/java/com/google/devtools/build/lib/rules/cpp/CcIncLibrary.java#L36-L50

实现该功能的实际代码的注释在解释此规则的实际作用方面做得非常非常好。

cc_inc_library 规则自 Bazel 版本 0.12 以来已被弃用。

改用cc_library方法。

参见:https://blog.bazel.build/2018/04/11/bazel-0.12.html

你真正想要的是strip_include_prefix:

cc_library(
    name = "foo",
    srcs = ["src/func.cxx"],
    hdrs = ["include/foo/func.h"],
    # Here!
    strip_include_prefix = "include",
    visibility = ["//visibility:public"],
)

这将使 headers 可通过以下方式访问:

#include "foo/func.h"

此属性从 至少 Bazel 0.17 开始可用。