BazelDeps

Important

This feature is still under development, while it is recommended and usable and we will try not to break them in future releases, some breaking changes might still happen if necessary to prepare for the Conan 2.0 release.

Available since: 1.37.0

BazelDeps

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()

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 Repository and target names.

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.

Since Conan 1.62.0, 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):
        # DEPRECATED: Default generators folder will be "conan" in Conan 2.x
        self.folders.generators = "conan"
        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()

build_context_activated

Available since: 1.62.0

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):
    # DEPRECATED: Default generators folder will be "conan" in Conan 2.x
    self.folders.generators = "conan"
    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

Available since: 1.62.0

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")

Repository and target names

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. For instance, if openssl/3.1.4 recipe would have has these bazel_target_name and bazel_repository_name properties already declared:

conanfile.py
from conan import ConanFile

class OpenSSLConan(ConanFile):
    name = "openssl"

    # any code here

    def package_info(self):
        self.cpp_info.set_property("bazel_target_name", "local_openssl")
        self.cpp_info.set_property("bazel_repository_name", "OpenSSL")
        self.cpp_info.components["crypto"].set_property("bazel_target_name", "libcrypto")
        self.cpp_info.components["ssl"].set_property("bazel_target_name", "libssl")

Run conan install openssl/3.1.4@ -g BazelDeps and check the *.pc files created:

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

The labels to use in your personal BUILD file would be:

  • @OpenSSL//:local_openssl

  • @OpenSSL//:libssl

  • @OpenSSL//:libcrypto

  • @OpenSSL//:local_openssl_binaries

An example of a BUILD file consuming one of these targets could be something like this:

BUILD
load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
    name = "example",
    srcs = ["example.cpp"],
    deps = [
        "@OpenSSL//:local_openssl",
    ],
)