使用bazel构建C语言的单元测试

zf9nrax1  于 2023-08-03  发布在  其他
关注(0)|答案(3)|浏览(103)

我有一个C代码库,我试图与Bazel建立。这个代码库包含了使用fff库在C中生成函数模拟的单元测试。实际的库并不重要,我对函数模拟的整个概念有问题。
现在我有一个makefile,我在其中链接并运行我的测试。当我构建一个测试时,我编译并链接测试下的库和测试源代码本身。此测试还定义了库依赖项的模拟。链接后,模拟的符号被解析为模拟实现,一切都按预期工作。但它这样做的原因是我链接实际的依赖库,我链接测试源代码中定义的模拟符号。
主要问题是:我该怎么和Bazel合作?当链接cc_test目标的二进制文件时,Bazel编译并链接所有传递依赖项。由于被测库依赖于(通过deps)符号的真实实现,这个真实的定义与mock定义一起链接,自然会出现以下错误:multiple definition of XXX的值。
示例如下:

cc_library(
  name = "a",
  # a.cc has the real version of "void some_function()".
  srcs = ["a.cc"],
  hdrs = ["a.h"],
)

# This test is working just fine.
cc_test(
  name = "a_test",
  srcs = ["a_test.cpp"],
  deps = [":a"],
)

cc_library(
  name = "b",
   # b.cc includes a.h and uses "void some_function()".
  srcs = ["b.cc"],
  hdrs = ["b.h"],
  deps = [":a"],
)

# This test has two definitions for "void some_function()":
# the real one and the mock one.
cc_test(
  name = "b_test",
  # b_test.cpp has the mock version of "void some_function()".
  srcs = ["b_test.cpp"],
  deps = [":b"],
)

字符串
我对Bazel并不陌生,但我也不是Maven,在花了很多时间尝试之后,我失败了。有什么建议吗?

csbfibhn

csbfibhn1#

您的问题可能是一个非常基本的问题,与名称空间和/或扩展类有关。
c没有命名空间的定义,但它们可以以某种方式模拟:
https://stackoverflow.com/a/28535585/1019850
但是你可能会使用c++而不是c,所以可以直接使用名称空间。
具体的问题是,你有两个相同的函数,它们将用名称空间a.some_function()b.some_function()表示。
在Bazel中有几种解决方案,我只是发布了一些简短的复制片段,你必须在链接页面上阅读详细信息。

deps = [
    "@gtest//:main",
    "//lib:hello-greet",
],

字符串
友情链接:https://docs.bazel.build/versions/master/cpp-use-cases.html
这里的代码片段也用于测试,但我认为这与您的问题无关。
然后在Bazel中存在工具链,这里是一个例子:

config_setting(
    name = "on_linux",
    constraint_values = [
        "@bazel_tools//platforms:linux",
    ],
)

config_setting(
    name = "on_windows",
    constraint_values = [
        "@bazel_tools//platforms:windows",
    ],
)

bar_binary(
    name = "myprog",
    srcs = ["mysrc.bar"],
    compiler = select({
        ":on_linux": "//bar_tools:barc_linux",
        ":on_windows": "//bar_tools:barc_windows",
    }),
)


友情链接:https://docs.bazel.build/versions/master/toolchains.html
由于我不是用c或c++编程,通常我需要花相当长的时间来挖掘足够的解决方案,所以我不得不保留它,但没有真实的的解决方案,但我希望问题和解决它的方法变得更清晰。
我会先尝试第二个链接,在上面的第一个片段下面。

kupeojn6

kupeojn62#

我在Bazel本身看不到任何好的解决方案。在C的情况下,Bazel试图创建一个简单而清晰的依赖管理系统来减少所有的诡计,这可以在C/C编译/链接系统中完成。你的情况很不寻常。
我看到的唯一解决方案是使用弱符号将负担转移到链接器。你可以使用__attribute__((weak))来注解你的“正常”函数。你的“假”函数应该选择在所有目标中的“正常”函数,其中函数的强符号(假函数)是存在的

2sbarzqh

2sbarzqh3#

我也遇到了同样的问题,并编写了一个bazel规则来添加或删除CC目标的链接依赖项。它很粗糙,不处理任何非链接器依赖:

def _replace_cc_link_deps_impl(ctx):
    remove_labels = [target.label for target in ctx.attr.remove]
    remove_linker_inputs = []
    if ctx.attr.remove_deps:
        remove_cc_info = cc_common.merge_cc_infos(cc_infos = [target[CcInfo] for target in ctx.attr.remove])
        remove_linker_inputs = remove_cc_info.linking_context.linker_inputs.to_list()

    actual_cc_info = ctx.attr.actual[CcInfo]
    linker_inputs = [
        linker_input
        for linker_input in actual_cc_info.linking_context.linker_inputs.to_list()
        if linker_input.owner not in remove_labels and linker_input not in remove_linker_inputs
    ]
    linking_context = cc_common.create_linking_context(linker_inputs = depset(linker_inputs, order = "topological"))

    cc_info = cc_common.merge_cc_infos(cc_infos = [
        CcInfo(compilation_context = actual_cc_info.compilation_context, linking_context = linking_context),
    ] + [target[CcInfo] for target in ctx.attr.add])
    return [cc_info]

replace_cc_link_deps = rule(
    implementation = _replace_cc_link_deps_impl,
    attrs = {
        "actual": attr.label(mandatory = True, providers = [CcInfo], doc = "The target whose dependencies you want to modify"),
        "remove": attr.label_list(mandatory = True, providers = [CcInfo], doc = "The link dependencies of these targets are removed."),
        "add": attr.label_list(providers = [CcInfo], doc = "List of targets that are added as link dependencies."),
        "remove_deps": attr.bool(
            doc = "If true, also remove all dependencies of removed targets. " +
                  "This may inadvertently remove common dependencies! " +
                  "You can either try to re-add those, or explicitly remove transitive dependencies instead.",
        ),
    },
    fragments = ["cpp"],
    provides = [CcInfo],
)

字符串
根据你的例子,你可以定义:

replace_cc_link_deps(
    name = "b_without_a",
    actual = ":b",
    remove = [":a"],
)

cc_test(
  name = "b_test",
  srcs = ["b_test.cpp"],
  deps = [":b_without_a"],
)

相关问题