BazelDeps

Warning

This feature is experimental and subject to breaking changes. See the Conan stability section for more information.

The BazelDeps is the dependencies generator for Bazel. Generates a <REPOSITORY>/BUILD.bazel file per dependency, where the <REPOSITORY>/ folder is the Conan recipe reference name by default, e.g., mypkg/BUILD.bazel. Apart from that, it also generates a dependencies.bzl file which contains a Bazel function to load all your Conan dependencies.

The BazelDeps generator can be used by name in conanfiles:

conanfile.py
class Pkg(ConanFile):
    generators = "BazelDeps"
conanfile.txt
[generators]
BazelDeps

And it can also be fully instantiated in the conanfile generate() method:

from conan import ConanFile
from conan.tools.google import BazelDeps

class App(ConanFile):
    settings = "os", "arch", "compiler", "build_type"
    requires = "zlib/1.2.11"

    def generate(self):
        bz = BazelDeps(self)
        bz.generate()

Generated files

When the BazelDeps generator is used, every invocation of conan install will generate several bazel files. For the conanfile.py above, for example:

$ conan install .
.
├── BUILD.bazel
├── conanfile.py
├── dependencies.bzl
└── zlib
    └── BUILD.bazel

Every conan install generates these files:

  • BUILD.bazel: An empty file aimed to be alongside the dependencies.bzl one. More information here.

  • dependencies.bzl: this file tells your Bazel WORKSPACE how to load the dependencies.

  • zlib/BUILD.bazel: contains all the targets that you can load from any of your BUILD files. More information in Customization.

Let’s check the content of the files created:

dependencies.bzl
# This Bazel module should be loaded by your WORKSPACE file.
# Add these lines to your WORKSPACE one (assuming that you're using the "bazel_layout"):
# load("@//conan:dependencies.bzl", "load_conan_dependencies")
# load_conan_dependencies()

def load_conan_dependencies():
    native.new_local_repository(
        name="zlib",
        path="/path/to/conan/package/folder/",
        build_file="/your/current/working/directory/zlib/BUILD.bazel",
    )

Given the example above, and imagining that your WORKSPACE is at the same directory, you would have to add these lines in there:

WORKSPACE
load("@//:dependencies.bzl", "load_conan_dependencies")
load_conan_dependencies()
zlib/BUILD.bazel
load("@rules_cc//cc:defs.bzl", "cc_import", "cc_library")

# Components precompiled libs
# Root package precompiled libs
cc_import(
    name = "z_precompiled",
    static_library = "lib/libz.a",
)

# Components libraries declaration
# Package library declaration
cc_library(
    name = "zlib",
    hdrs = glob([
        "include/**",
    ]),
    includes = [
        "include",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":z_precompiled",
    ],
)

# Filegroup library declaration
filegroup(
    name = "zlib_binaries",
    srcs = glob([
        "bin/**",
    ]),
    visibility = ["//visibility:public"],
)

As you can observe, the zlib/BUILD.bazel defines these global targets:

  • zlib: bazel library target. The label used to depend on it would be @zlib//:zlib.

  • zlib_binaries: bazel filegroup target. The label used to depend on it would be @zlib//:zlib_binaries.

You can put all the files generated by BazelDeps into another folder using the bazel_layout:

conanfile.py
from conan import ConanFile
from conan.tools.google import BazelDeps, bazel_layout

class App(ConanFile):
    settings = "os", "arch", "compiler", "build_type"
    requires = "zlib/1.2.11"

    def layout(self):
        bazel_layout(self)

    def generate(self):
        bz = BazelDeps(self)
        bz.generate()

Running again the conan install command, we now get this structure:

$ conan install .
.
├── conan
│   ├── BUILD.bazel
│   ├── dependencies.bzl
│   └── zlib
│       └── BUILD.bazel
└── conanfile.py

Now your Conan-bazel files were generated in the conan/ folder, so your WORKSPACE will look like:

WORKSPACE
load("@//conan:dependencies.bzl", "load_conan_dependencies")
load_conan_dependencies()

Customization

Naming

The <REPOSITORY>/BUILD.bazel file contains all the targets declared by the dependency. Both the <REPOSITORY>/ folder and the targets declared in there will be named following these rules by default:

  • For packages, it uses the package name as folder/target name, e.g., package zlib/1.2.11 will have:
    • Folder: zlib/BUILD.bazel.

    • Global target: zlib.

    • How it can be consumed: @zlib//:zlib.

  • For components, the package name + hyphen + component name, e.g., package openssl/3.1.4 will have:
    • Folder: openssl/BUILD.bazel.

    • Global target: openssl.

    • Components targets: openssl-ssl, and openssl-crypto.

    • How it can be consumed:
      • @openssl//:openssl (global one which includes all the components)

      • @openssl//:openssl-ssl (component one)

      • @openssl//:openssl-crypto (component one)

You can change that default behavior with the bazel_target_name and the bazel_repository_name properties. See Properties section below.

Reference

class BazelDeps(conanfile)
Parameters:

conanfile< ConanFile object > The current recipe object. Always use self.

build_context_activated

Activates the build context for the specified Conan package names.

generate()

Generates all the targets <DEP>/BUILD.bazel files and the dependencies.bzl one in the build folder. It’s important to highlight that the dependencies.bzl file should be loaded by your WORKSPACE Bazel file:

load("@//[BUILD_FOLDER]:dependencies.bzl", "load_conan_dependencies")
load_conan_dependencies()

build_context_activated

When you have a build-requirement, by default, the Bazel files are not generated. But you can activate it using the build_context_activated attribute:

def build_requirements(self):
    self.tool_requires("my_tool/0.0.1")

def layout(self):
    bazel_layout(self)

def generate(self):
    bz = BazelDeps(self)
    # generate the build-mytool/BUILD.bazel file for the tool require
    bz.build_context_activated = ["my_tool"]
    bz.generate()

Running the conan install command, the structure created is as follows:

$ conan install . -pr:b default
.
├── conan
│   ├── BUILD.bazel
│   ├── build-my_tool
│      └── BUILD.bazel
│   └── dependencies.bzl
└── conanfile.py

Notice that my_tool Bazel folder is prefixed with build- which indicates that it’s being used in the build context.

Properties

The following properties affect the BazelDeps generator:

  • bazel_target_name property will define the name of the target declared in the <REPOSITORY>/BUILD.bazel. This property can be defined at both global and component cpp_info level.

  • bazel_repository_name property will define the name of the folder where the dependency BUILD.bazel will be allocated. This property can only be defined at global cpp_info level.

Example:

def package_info(self):
    self.cpp_info.set_property("bazel_target_name", "my_target")
    self.cpp_info.set_property("bazel_repository_name", "my_repo")
    self.cpp_info.components["mycomponent"].set_property("bazel_target_name", "component_name")