如何获取自定义标头模板规则以将其传递给下游cc_binary / cc_library依赖项?

时间:2019-03-28 21:01:33

标签: bazel

我正在尝试为bazel建立一个规则,以模仿CMake * .in模板系统。

这有两个挑战,第一个是生成模板输出。第二个是使输出可用于genrulesfilegroupscc_*规则。第三是使该依赖关系可传递给其他下游规则。

我让它在genfiles(或bazel-bin)中生成输出文件version.hpp,我可以得到包含它的初始库规则,但似乎无法弄清楚如何制作cc_binary规则,该规则取决于cc_library,并依赖于header_template规则来查找头文件。

我有以下.bzl规则:

def _header_template_impl(ctx):
    # this generates the output from the template
    ctx.actions.expand_template(
        template = ctx.file.template,
        output = ctx.outputs.out,
        substitutions = ctx.attr.vars,
    )

    return [
            # create a provider which says that this
            # out file should be made available as a header
            CcInfo(compilation_context=cc_common.create_compilation_context(
                headers=depset([ctx.outputs.out])
            )),

            # Also create a provider referencing this header ???
            DefaultInfo(files=depset(
                [ctx.outputs.out]
            ))
        ]

header_template = rule(
    implementation = _header_template_impl,
    attrs = {
        "vars": attr.string_dict(
            mandatory = True
        ),
        "extension": attr.string(default=".hpp"),
        "template": attr.label(
            mandatory = True,
            allow_single_file = True,
        ),
    },
    outputs = {
        "out": "%{name}%{extension}",
    },
    output_to_genfiles = True,
)

在其他地方我有cc_library规则:

load("//:tools/header_template.bzl", "header_template")

# version control
BONSAI_MAJOR_VERSION = '2'
BONSAI_MINOR_VERSION = '0'
BONSAI_PATCH_VERSION = '9'
BONSAI_VERSION = \
    BONSAI_MAJOR_VERSION + '.' + \
    BONSAI_MINOR_VERSION + '.' + \
    BONSAI_PATCH_VERSION

header_template(
    name = "bonsai_version",
    extension = ".hpp",
    template = "version.hpp.in",
    vars = {
        "@BONSAI_MAJOR_VERSION@": BONSAI_MAJOR_VERSION,
        "@BONSAI_MINOR_VERSION@": BONSAI_MINOR_VERSION,
        "@BONSAI_PATCH_VERSION@": BONSAI_PATCH_VERSION,
        "@BONSAI_VERSION@": BONSAI_VERSION,
    },
)

# ...

private = glob([
        "src/**/*.hpp",
        "src/**/*.cpp",
        "proto/**/*.hpp",
    ])
public = glob([
        "include/*.hpp",
        ":bonsai_version",
    ])

cc_library(
    # target name matches directory name so you can call:
    #  bazel build .
    name = "bonsai",
    srcs = private,
    hdrs = public,
    # public headers
    includes = [
        "include",
    ],

    # ...

    deps = [
        ":bonsai_version",
        # ...
    ],

    # ...
)

构建时,我的源文件必须能够:

#include "bonsai_version.hpp"

我认为答案涉及CcInfo,但我对如何构造它一无所知。

我已经尝试将"-I$(GENDIR)/" + package_name()添加到副本中,但无济于事。生成的标头仍然不可用。

我的期望是,我应该能够返回某种Info对象,该对象将允许我在srcs中添加依赖项。也许应该是DefaultInfo

我已经浏览了bazel规则示例和源代码,但是我缺少一些基本知识,并且找不到有关此特定主题的文档。

我希望能够执行以下操作:

header_template(
    name = "some_header",
    extension = ".hpp",
    template = "some_header.hpp.in",
    vars = {
        "@SOMEVAR@": "value",
        "{ANOTHERVAR}": "another_value",
    },
)

cc_library(
   name = "foo",
   srcs = ["foo.src", ":some_header"],
   ...
)

cc_binary(
   name = "bar",
   srcs = ["bar.cpp"],
   deps = [":foo"],
)


并包含生成的标头,如下所示:

#include "some_header.hpp"

void bar(){
}

2 个答案:

答案 0 :(得分:1)

答案看起来像是

def _header_template_impl(ctx):
    # this generates the output from the template
    ctx.actions.expand_template(
        template = ctx.file.template,
        output = ctx.outputs.out,
        substitutions = ctx.attr.vars,
    )

    return [
            # create a provider which says that this
            # out file should be made available as a header
            CcInfo(compilation_context=cc_common.create_compilation_context(

                # pass out the include path for finding this header
                includes=depset([ctx.outputs.out.dirname]),

                # and the actual header here.
                headers=depset([ctx.outputs.out])
            ))
        ]

其他地方:

header_template(
    name = "some_header",
    extension = ".hpp",
    template = "some_header.hpp.in",
    vars = {
        "@SOMEVAR@": "value",
        "{ANOTHERVAR}": "another_value",
    },
)

cc_library(
   name = "foo",
   srcs = ["foo.cpp"],
   deps = [":some_header"],
   ...
)

cc_binary(
   name = "bar",
   srcs = ["bar.cpp"],
   deps = [":foo"],
)

答案 1 :(得分:0)

如果标头具有通用名称(例如config.h),而您希望它是私有的(即srcs而不是hdrs),则可能需要其他方法。我已经看到了gflag的问题,它“泄漏”了config.h并影响了依赖它的库(issue)。

当然,在两种情况下,最简单的解决方案是为目标平台生成并提交头文件。

或者,您可以为使用生成的专用标头的copts规则设置cc_library

cc_library(
   name = "foo",
   srcs = ["foo.cpp", "some_header.hpp"],
   copts = ["-I$(GENDIR)/my/package/name"],
   ...
)

如果您希望在将存储库作为外部存储库包含时工作,那么由于bazel issue #4463,您的工作量将减少。

PS。您可能想查看cc_fix_config中的https://github.com/antonovvk/bazel_rules是否适合您。它只是perl的包装,但我发现它很有用。