BazelDeps¶
警告
此功能为实验性功能,可能会有破坏性更改。有关更多信息,请参阅 Conan 稳定性 部分。
BazelDeps
是 Bazel 的依赖生成器。它为每个依赖生成一个 <REPOSITORY>/BUILD.bazel 文件,其中 <REPOSITORY>/ 文件夹默认为 Conan recipe 引用名称,例如,mypkg/BUILD.bazel。除此之外,它还会生成 Bazel 6.x 兼容文件,如 dependencies.bzl,以及其他 Bazel >= 7.1 兼容文件,如 conan_deps_module_extension.bzl 和 conan_deps_repo_rules.bzl。所有这些文件都包含通过您的 WORKSPACE | MODULE.bazel 加载所有 Conan 依赖的逻辑。
BazelDeps
生成器可以通过名称在 conanfile 中使用
class Pkg(ConanFile):
generators = "BazelDeps"
[generators]
BazelDeps
它也可以在 conanfile 的 generate()
方法中完全实例化
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()
生成的文件¶
当使用 BazelDeps
生成器时,每次调用 conan install
都会生成多个 bazel 文件。例如,对于上面的 conanfile.py
$ conan install .
.
├── BUILD.bazel
├── conanfile.py
├── dependencies.bzl
└── zlib
└── BUILD.bazel
每次 conan install 生成以下文件
BUILD.bazel:一个空文件,旨在与 dependencies.bzl 文件放在一起。更多信息请参考 这里。
zlib/BUILD.bazel:包含您可以从任何 BUILD 文件加载的所有目标。更多信息请参考 自定义。
dependencies.bzl:(Bazel 6.x 兼容)此文件告诉您的 Bazel WORKSPACE 如何加载依赖项。
conan_deps_module_extension.bzl:(自 Conan 2.4.0 起)(Bazel >= 7.1 兼容)此文件用于将每个依赖项作为仓库加载。
conan_deps_repo_rules.bzl:(自 Conan 2.4.0 起)(Bazel >= 7.1 兼容)此文件提供的规则用于创建仓库。它不供消费者使用,而是供 conan_deps_module_extension.bzl 使用。
让我们检查一下创建的文件的内容
Bazel 6.x 兼容
# 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",
)
Bazel >= 7.1 兼容
# This bazel repository rule is used to load Conan dependencies into the Bazel workspace.
# It's used by a generated module file that provides information about the conan packages.
# Each conan package is loaded into a bazel repository rule, with having the name of the
# package. The whole method is based on symlinks to not copy the whole package into the
# Bazel workspace, which is expensive.
def _conan_dependency_repo(rctx):
package_path = rctx.workspace_root.get_child(rctx.attr.package_path)
child_packages = package_path.readdir()
for child in child_packages:
rctx.symlink(child, child.basename)
rctx.symlink(rctx.attr.build_file_path, "BUILD.bazel")
conan_dependency_repo = repository_rule(
implementation = _conan_dependency_repo,
attrs = {
"package_path": attr.string(
mandatory = True,
doc = "The path to the Conan package in conan cache.",
),
"build_file_path": attr.string(
mandatory = True,
doc = "The path to the BUILD file.",
),
},
)
# This module provides a repo for each requires-dependency in your conanfile.
# It's generated by the BazelDeps, and should be used in your Module.bazel file.
load(":conan_deps_repo_rules.bzl", "conan_dependency_repo")
def _load_dependenies_impl(mctx):
conan_dependency_repo(
name = "zlib",
package_path = "/path/to/conan/package/folder/",
build_file_path = "/your/current/working/directory/zlib/BUILD.bazel",
)
return mctx.extension_metadata(
# It will only warn you if any direct
# dependency is not imported by the 'use_repo' or even it is imported
# but not created. Notice that root_module_direct_dev_deps can not be None as we
# are giving 'all' value to root_module_direct_deps.
# Fix the 'use_repo' calls by running 'bazel mod tidy'
root_module_direct_deps = 'all',
root_module_direct_dev_deps = [],
# Prevent writing function content to lockfiles:
# - https://bazel.build/rules/lib/builtins/module_ctx#extension_metadata
# Important for remote build. Actually it's not reproducible, as local paths will
# be different on different machines. But we assume that conan works correctly here.
# IMPORTANT: Not compatible with bazel < 7.1
reproducible = True,
)
conan_extension = module_extension(
implementation = _load_dependenies_impl,
os_dependent = True,
arch_dependent = True,
)
鉴于以上示例,并假设您的 WORKSPACE | MODULE.bazel 与其在同一目录中,您需要添加以下行
Bazel 6.x 兼容
load("@//:dependencies.bzl", "load_conan_dependencies")
load_conan_dependencies()
Bazel >= 7.1 兼容
load_conan_dependencies = use_extension("//:conan_deps_module_extension.bzl", "conan_extension")
# use_repo(load_conan_dependencies, "dep1", "dep2", ..., "depN")
use_repo(load_conan_dependencies, "zlib")
正如您所看到的,zlib/BUILD.bazel 定义了以下全局目标
# 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"],
)
zlib
:bazel 库目标。用于依赖它的标签将是@zlib//:zlib
。zlib_binaries
:bazel filegroup 目标。用于依赖它的标签将是@zlib//:zlib_binaries
。
您可以使用 bazel_layout
将 BazelDeps
生成的所有文件放入另一个文件夹
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()
再次运行 conan install 命令,我们现在得到以下结构
$ conan install .
.
├── conan
│ ├── BUILD.bazel
│ ├── dependencies.bzl
│ ├── conan_deps_module_extension.bzl
│ ├── conan_deps_repo_rules.bzl
│ └── zlib
│ └── BUILD.bazel
└── conanfile.py
现在您的 Conan-bazel 文件已在 conan/ 文件夹中生成,您的 WORKSPACE 将如下所示
load("@//conan:dependencies.bzl", "load_conan_dependencies")
load_conan_dependencies()
或者您的 MODULE.bazel
load_conan_dependencies = use_extension("//conan:conan_deps_module_extension.bzl", "conan_extension")
use_repo(load_conan_dependencies, "zlib")
自定义¶
命名¶
<REPOSITORY>/BUILD.bazel
文件包含依赖项声明的所有目标。默认情况下,<REPOSITORY>/
文件夹和其中声明的目标都将遵循以下规则命名
- 对于包,它使用包名称作为文件夹/目标名称,例如,包
zlib/1.2.11
将具有 文件夹:
zlib/BUILD.bazel
。全局目标:
zlib
。如何使用:
@zlib//:zlib
。
- 对于包,它使用包名称作为文件夹/目标名称,例如,包
- 对于组件,包名称 + 连字符 + 组件名称,例如,包
openssl/3.1.4
将具有 文件夹:
openssl/BUILD.bazel
。全局目标:
openssl
。组件目标:
openssl-ssl
和openssl-crypto
。- 如何使用
@openssl//:openssl
(包含所有组件的全局目标)@openssl//:openssl-ssl
(组件目标)@openssl//:openssl-crypto
(组件目标)
- 对于组件,包名称 + 连字符 + 组件名称,例如,包
您可以使用 bazel_target_name
和 bazel_repository_name
属性更改默认行为。请参阅下面的 属性部分。
参考¶
- class BazelDeps(conanfile)¶
- 参数:
conanfile –
< ConanFile 对象 >
当前 recipe 对象。始终使用self
。
- build_context_activated¶
为指定的 Conan 包名称激活构建上下文。
- generate()¶
在构建文件夹中生成所有目标 <DEP>/BUILD.bazel 文件、一个 dependencies.bzl 文件(用于 bazel<7)、一个 conan_deps_repo_rules.bzl 文件和一个 conan_deps_module_extension.bzl 文件(用于 bazel>=7.1)。
对于 bazel < 7,重要的是要强调
dependencies.bzl
文件应由您的 WORKSPACE Bazel 文件加载load("@//[BUILD_FOLDER]:dependencies.bzl", "load_conan_dependencies") load_conan_dependencies()
对于 bazel >= 7.1,
conan_deps_module_extension.bzl
文件应由您的 Module.bazel 文件加载,例如像这样load_conan_dependencies = use_extension( "//build:conan_deps_module_extension.bzl", "conan_extension" ) use_repo(load_conan_dependencies, "dep-1", "dep-2", ...)
build_context_activated¶
当您有构建需求时,默认情况下不会生成 Bazel 文件。但是您可以使用 build_context_activated 属性激活它
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()
运行 conan install 命令,创建的结构如下
$ conan install . -pr:b default
.
├── conan
│ ├── BUILD.bazel
│ ├── build-my_tool
│ │ └── BUILD.bazel
│ ├── conan_deps_module_extension.bzl
│ ├── conan_deps_repo_rules.bzl
│ └── dependencies.bzl
└── conanfile.py
请注意,my_tool Bazel 文件夹以 build-
为前缀,这表示它正在构建上下文中使用。
属性¶
以下属性会影响 BazelDeps
生成器
bazel_target_name 属性将定义在
<REPOSITORY>/BUILD.bazel
中声明的目标的名称。此属性可以在全局和组件cpp_info
级别定义。bazel_repository_name 属性将定义分配依赖项 BUILD.bazel 的文件夹的名称。此属性只能在全局
cpp_info
级别定义。
示例
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")