Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In BAZEL, Is there a way to prevent dependency C/C++ headers from being propagated to dependent library?

Tags:

For example: lib_a is an internal library. It exposes interface header alpha.h.

lib_b is an API library. It includes alpha.h in beta.c and exposes interface header beta.h which does not include alpha.h.

exe_c is a test app that utilizes lib_b. Since it is "external" to API library lib_b, it should not have access to internal library lib_a's headers, namely alpha.h.

Now, is there a way to prevent the include path of alpha.h from being added to exe_c 's compilation command-line?

like image 601
xiay Avatar asked May 15 '20 18:05

xiay


1 Answers

Update 5/24/22: Bazel added an implementation_deps feature.

This is possible with a custom rule and macro.

Here's how you can write a macro my_cc_library so that you can do something like this

my_cc_library(
    name = "b",
    hdrs = [
        "b.h",
    ],
    srcs = [
        "b.cc",
    ]
    impl_deps = [
        "//a:a",
    ],
)

And targets that depend on b will not be able to include headers from dependency a.

Step 1

Add a custom rule that takes a base library and forms a new CcInfo provider with only the headers you want to expose.

def _my_cc_slim_library(ctx):
  cc_info = ctx.attr.base_library[CcInfo]
  compilation_ctx = cc_info.compilation_context

  compilation_ctx_p = cc_common.create_compilation_context(
      headers = depset(direct=ctx.files.hdrs),
      includes = compilation_ctx.includes,
      quote_includes = compilation_ctx.quote_includes,
  )

  dep_cc_infos = [dep[CcInfo] for dep in ctx.attr.deps]

  cc_info_p = cc_common.merge_cc_infos(
      cc_infos = [
          CcInfo(
              compilation_context = compilation_ctx_p,
              linking_context = cc_info.linking_context,
          )
      ] + dep_cc_infos,
  )
  
  return [cc_info_p]

my_cc_slim_library = rule(
    implementation = _my_cc_slim_library,
    attrs = {
        "base_library": attr.label(mandatory=True, providers = [CcInfo]),
        "hdrs": attr.label_list(allow_files=True),
        "deps": attr.label_list(providers = [CcInfo]),
    },
)

Step 2

Add the custom macro my_cc_library. This macro will create a base library and then use the rule my_cc_slim_library to strip out the unwanted header dependencies.

def my_cc_library(
    name,
    hdrs = [],
    deps = [],
    impl_deps = [],
    **kwargs):
  native.cc_library(
      name = name + "-base",
      hdrs = hdrs,
      deps = deps + impl_deps,
      **kwargs)
  my_cc_slim_library(
      name = name,
      hdrs = hdrs,
      base_library = name + "-base",
      deps = deps,
  )

Example

Now here's how to use it in the example.

bazel/my_cc_library.bzl <- contains the custom rule and macro

a/a.h

int do_a();

a/a.cc

#include "a/a.h"

int do_a() {
  return 1;
}

a/BUILD

package(default_visibility = ["//visibility:public"])

cc_library(
    name = "a",
    hdrs = [
        "a.h",
    ],
    srcs = [
        "a.cc",
    ],
)

b/b.h

int do_b();

b/b.cc

#include "a/a.h"

int do_b() {
  return do_a() + 2;
}

b/BUILD

load(
    "//bazel:my_cc_library.bzl",
    "my_cc_library",
)

package(default_visibility = ["//visibility:public"])

my_cc_library(
    name = "b",
    hdrs = [
        "b.h",
    ],
    srcs = [
        "b.cc",
    ],
    impl_deps = [
        "//a:a",
    ],
)

main.cc

#include <iostream>

// #include "a/a.h"   <- this would cause an error
#include "b/b.h"

int main() {
  std::cout << do_b() << std::endl;
  return 0;
}

BUILD

cc_binary(
    name = "exe",
    srcs = [
        "main.cc",
    ],
    deps = [
        "//b:b",
    ],
)
like image 84
Ryan Burn Avatar answered Sep 19 '22 12:09

Ryan Burn