Bazel library which allows dealing with the whole RPM dependency lifecycle solely with pure go rules and a static go binary.
The rpm
rule represents a pure RPM dependency. This dependency is not
processed in any way. They can be added to your WORKSPACE
file like this:
load("@bazeldnf//:deps.bzl", "rpm")
rpm(
name = "libvirt-devel-6.1.0-2.fc32.x86_64.rpm",
sha256 = "2ebb715341b57a74759aff415e0ff53df528c49abaa7ba5b794b4047461fa8d6",
urls = [
"https://download-ib01.fedoraproject.org/pub/fedora/linux/releases/32/Everything/x86_64/os/Packages/l/libvirt-devel-6.1.0-2.fc32.x86_64.rpm",
"https://storage.googleapis.com/builddeps/2ebb715341b57a74759aff415e0ff53df528c49abaa7ba5b794b4047461fa8d6",
],
)
rpmtree
Takes a list of rpm
dependencies and merges them into a single
tar
package. rpmtree
rules can be added like this to your BUILD
files:
load("@bazeldnf//:deps.bzl", "rpmtree")
rpmtree(
name = "rpmarchive",
rpms = [
"@libvirt-libs-6.1.0-2.fc32.x86_64.rpm//rpm",
"@libvirt-devel-6.1.0-2.fc32.x86_64.rpm//rpm",
],
)
Since rpmarchive
is just a tar archive, it can be put into a container
immediately:
container_layer(
name = "gcloud-layer",
tars = [
":rpmarchive",
],
)
rpmtrees allow injecting relative symlinks (pkg_tar
can only inject absolute
symlinks) and xattrs capabilities
. The following example adds a relative
link and gives one binary the cap_net_bind_service
capability to connect to
privileged ports:
rpmtree(
name = "rpmarchive",
rpms = [
"@libvirt-libs-6.1.0-2.fc32.x86_64.rpm//rpm",
"@libvirt-devel-6.1.0-2.fc32.x86_64.rpm//rpm",
],
symlinks = {
"/var/run": "../run",
},
capabilities = {
"/usr/libexec/qemu-kvm": [
"cap_net_bind_service",
],
},
)
The bazeldnf repository needs to be added to your WORKSPACE
:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "bazeldnf",
sha256 = "74f977ab8f13e466168ff0c80322bcb665db9f79d1bc497c742f9512737b91ea",
strip_prefix = "bazeldnf-0.0.1",
urls = [
"https://github.com/rmohr/bazeldnf/archive/v0.0.1.tar.gz",
],
)
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
load("@bazeldnf//:deps.bzl", "bazeldnf_dependencies")
go_rules_dependencies()
go_register_toolchains()
bazeldnf_dependencies()
Define the bazeldnf
executable rule in your BUILD.bazel
file:
load("@bazeldnf//:def.bzl", "bazeldnf")
bazeldnf(name = "bazeldnf")
After adding this code, you can run bazeldnf with Bazel:
bazel run //:bazeldnf -- --help
One important use-case is to expose headers and libraries inside the RPMs to build targets in bazel.
If we would just blindly expose all libraries to build targets, bazel would try to link any one of them to our binary.
This would obviously not work. Therefore we need a mediator
between cc_library
and rpmtree
. This mediator is the tar2files
target. This target allows extracting
a subset of libraries and headers and providing them to cc_library
targets.
An example:
load("@bazeldnf//:deps.bzl", "rpm", "rpmtree", "tar2files")
tar2files(
name = "libvirt-libs",
files = {
"/usr/include/libvirt": [
"libvirt-admin.h",
"libvirt-common.h",
"libvirt-domain-checkpoint.h",
"libvirt-domain-snapshot.h",
"libvirt-domain.h",
"libvirt-event.h",
],
"/usr/lib64": [
"libacl.so.1",
"libacl.so.1.1.2253",
"libattr.so.1",
],
},
tar = ":libvirt-devel",
visibility = ["//visibility:public"],
)
tar
can take any input which is a tar archive. Conveniently this is what rpmtree
creates as the default target.
So any rpmtree
can be used here.
The files
section contains then files per folder which one wants to expose to cc_library
:
cc_library(
name = "rpmlibs",
srcs = [
":libvirt-libs/usr/lib64",
],
hdrs = [
":libvirt-libs/usr/include/libvirt",
],
strip_include_prefix="/libvirt-libs/",
prefix= "libvirt",
)
At this point source code linking to these libraries can be compiled, but unit tests would only work if we would manually
list any transitive library. This would be tedious and error prone. However bazeldnf can introspect for you shared libraries
and create tar2files
rules for you, based on a provided set of libraries.
First define a target like this:
load("@bazeldnf//:deps.bzl", "rpm", "rpmtree", "tar2files")
load("@bazeldnf//:def.bzl", "bazeldnf")
bazeldnf(
name = "ldd",
command = "ldd",
libs = [
"/usr/lib64/libvirt-lxc.so.0",
"/usr/lib64/libvirt-qemu.so.0",
"/usr/lib64/libvirt.so.0",
],
rpmtree = ":libvirt-devel",
rulename = "libvirt-libs",
)
rulename
containes the tar2files
target name, rpmtree
references a given rpmtree
and libs
contains
libraries which one wants to link. When now executing the target like this:
bazel run //:ldd
the tar2files
target will be updated with all transitive library dependencies for the specified libraries.
In addition, all header directories are updated too for convenience.
One key part of managing RPM dependencies and RPM repository updates via bazel
is the ability to resolve RPM dependencies from repos without external tools
like dnf
or yum
and write the resolved dependencies to your WORKSPACE
.
Here an example on how to add libvirt and bash to your WORKSPACE and BUILD files.
First write the repo.yaml
file which contains some basic rpm repos to query:
bazeldnf init --fc 32 # write a repo.yaml file containing the usual release and update repos for fc32
Then write a rpmtree
rule called libvirttree
to your BUILD file and all
corresponding RPM dependencies into your WORKSPACE for libvirt:
bazeldnf rpmtree --workspace /my/WORKSPACE --buildfile /my/BUILD.bazel --name libvirttree libvirt
Do the same for bash with a bashrpmtree
target:
bazeldnf rpmtree --workspace /my/WORKSPACE --buildfile /my/BUILD.bazel --name bashtree bash
Finally prune all unreferenced old RPM files:
bazeldnf prune --workspace /my/WORKSPACE --buildfile /my/BUILD.bazel
By default bazeldnf rpmtree
will try to find a solution which only contains
the newest packages of all involved repositories. The only exception are pinned
versions themselves. If pinned version require other outdated packages,
the --nobest
option can be supplied. With this option all packages are
considered. Newest packages will have the higest weight but it may not always be
able to choose them and older packages may be pulled in instead.
- Resolving
requires
entries which contain boolean logic like(gcc if something)
The goal is to build minimal containers with RPMs based on scratch containers. Therefore the following RPM repository hints will be ignored:
recommends
supplements
suggests
enhances