kodos
kodos

Reputation: 35

Include C++ library in Bazel project

I'm currently messing around with Google's Mediapipe, which uses Bazel as a build tool. The folder has the following structure:

mediapipe
    ├ mediapipe
    |   └ examples
    |        └ desktop
    |             └ hand_tracking
    |                  └ BUILD
    ├ calculators
    |   └ tensor
    |        └ tensor_to_landmarks_calculator.cc
    |        └ BUILD
    └ WORKSPACE

There are a bunch of other files in there as well, but they are rather irrelevant to this problem. They can be found in the git repo linked above if you need them.

I'm at a stage where I can build and run the hand_tracking example without any problems. Now, I want to include the cereal library to the build, so that I can use #include <cereal/archives/binary.hpp> from within tensors_to_landmarks_calculator.cc. The cereal library is located at C:\\cereal, but can be moved to other locations if it simplifies the process.

Basically, I'm looking for the Bazel equivalent of adding a path to Additional Include Directories in Visual Studio.

How would I need to modify the WORKSPACE and BUILD files in order to include the library in my project, assuming they are in a default state?

Unfortunately, this official doc page only covers one-file libraries, and other implementations kept giving me File could not be found errors at build time.

Thanks in advance!

Upvotes: 1

Views: 2457

Answers (1)

Chris Uzdavinis
Chris Uzdavinis

Reputation: 6131

First you have to tell Bazel about the code living "outside" the workspace area. It needs to know how to find it, how to build it, and what to call it, etc. These are known as remote repositories. They can be local to your disk (outside the Bazel workspace area), or actually remote on another machine or server, like github. The important thing is it must be described to Bazel with enough information that it can use.

As most third party code does not come with BUILD.bazel files, you may need to provide one yourself and tell Bazel "use this as if it was a build file found in that code."

For a local directory outside your bazel project

Add a repository rule like this to your WORKSPACE file:

# This could go in your WORKSPACE file
# (But prefer the http_archive solution below)
new_local_repository(
    name = "cereal",
    build_file = "//third_party:cereal.BUILD.bazel",
    path = "<path-to-directory>",
)

("new_local_repository" is built-in to bazel)

Somewhere under your Bazel WORKSPACE area you'll also need to make a cereal.BUILD.bazel file and export it from the package. I choose a directory called //third_party, but you can put it anywhere else, and name it anything else, as long as the repository rule provides a proper bazel label for it.) The contents might look like this:

# contents of //third_party/cereal.BUILD.bazel
cc_library(
    name = "cereal-lib",
    srcs = glob(["**/*.hpp"]),
    includes = ["include"],
    visibility = ["//visibility:public"],
)

Bazel will pretend this was the BUILD file that "came with" the remote repository, even though it's actually local to your repo. When Bazel fetches this remote repostiory code it copies it, and the BUILD file you provide, into its external area for caching, building, etc.

To make //third_party:cereal.BUILD.bazel a valid target in your directory, add a BUILD.bazel file to that directory:

# contents of //third_party/BUILD.bazel

exports_files = [
    "cereal.BUILD.bazel",
]

Without exporting it, you won't be able to refer to the buildfile from your repository rule.

Local disk repositories aren't very portable since people may have different versions installed and it's not very hermetic (making it hard to share caches of builds with others), and it requires they put them in the same place, and that kind of setup can be problematic. It also will fail when you mix operating systems, etc, if you refer to it as "C:..."

Downloading a tarball of the library from github, for example

A better way is to download a fixed version from github, for example, and let Bazel manage it for you in its external area:

http_archive(
    name = "cereal",
    sha256 = "329ea3e3130b026c03a4acc50e168e7daff4e6e661bc6a7dfec0d77b570851d5",
    urls = 
["https://github.com/USCiLab/cereal/archive/refs/tags/v1.3.0.tar.gz"],
    build_file = "//third_party:cereal.BUILD.bazel",
)

The sha256 is important, since it downloads and computes it, compares to what you specified, and can cache it. In the future, it won't re-download it if the local file's sha matches.

Notice, it again says build_file = //third_party:cereal.BUILD.bazel., all the same things from new_local_repository above apply here. Make sure you provide the build file for it to use, and export it from where you put it.

*To test that the remote repository is setup ok

on the command line issue

bazel fetch @cereal//:cereal-lib

I sometimes have to clear it out to make it try again, if my rule isn't quite right, but the "bad" version sticks around.

bazel clean --expunge

will remove it, but might be overkill.

Finally

We have:

  • defined a remote repository called @cereal
  • defined a target in it called cereal-lib
  • the target is thus @cereal//:cereal-lib

To use it

Go to the package where you would like to include cereal, and add a dependency on this repository to the rule that builds the c++ code that would like to use cereal. That is, in your case, the BUILD rule that causes tensor_to_landmarks_calculator.cc to get built, add:

   deps = [
       "@cereal//:cereal-lib",
       ...
   ]

And then in your c++ code:

#include "cereal/cereal.hpp"

That should do it.

Upvotes: 4

Related Questions