I am new to bazel and am playing around with monorepo and multirepo builds with dependencies.
I successfully setup the monorepo but am unable to get the multrepo to work.
For the multirepo I created two C++ repositories where repo app2 depends on repo lib2 When I build app2 I would like bazel to fetch lib2. To achieve this I have added a git_repository definition to my WORKSPACE. My issue is that when I run bazel build //app2:app bazel complains that it can not find the lib2 headers
INFO: Analyzed target //app2:app2 (0 packages loaded, 0 targets configured).
ERROR: /home/bazel/.cache/bazel/_bazel_bazel/36a4e7d3b3c11620f7fa4375ad21cf55/external/lib2/BUILD.bazel:6:11: Compiling time_util.cpp failed: (Exit 1): gcc failed: error executing CppCompile command (from target @@lib2//:time_util) /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer '-std=c++11' -MD -MF ... (remaining 21 arguments skipped)
Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
external/lib2/time_util.cpp:1:10: fatal error: lib2/time_util.h: No such file or directory
1 | #include "lib2/time_util.h"
| ^~~~~~~~~~~~~~~~~~
compilation terminated.
ERROR: /home/bazel/testing/app2/BUILD.bazel:7:10: Compiling app2/app.cpp failed: (Exit 1): gcc failed: error executing CppCompile command (from target //app2:app2) /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer '-std=c++11' -MD -MF ... (remaining 32 arguments skipped)
Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
app2/app.cpp:1:10: fatal error: lib2/time_util.h: No such file or directory
1 | #include "lib2/time_util.h"
| ^~~~~~~~~~~~~~~~~~
compilation terminated.
My bazel files are as follows
WORKSPACE.bazel
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "lib2",
remote = "https://wszarmach@bitbucket.org/szarmach/lib2.git",
branch = "main", # branch, tag, commit, hash
)
app2/BUILD.bazel
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "app",
srcs = ["app.cpp"],
#deps = ["//lib2:time_util"],
deps = ["@lib2//:time_util"],
visibility = ["//visibility:public"],
)
lib2/BUILD.bazel
load("@rules_cc//cc:defs.bzl", "cc_library")
cc_library(
name = "time_util",
srcs = ["time_util.cpp"],
hdrs = ["time_util.h"],
#hdrs = glob(["**/*.h"]),
copts = ["-Ilib1"],
includes = ["."],
visibility = ["//visibility:public"],
)
Searching stack overflow I have seen various solutions to almost identical issues but I have not been able to apply them successfully.
What am I missing for the multi repo case to have bazel build?
It seems like you have a single WORKSPACE.bazel
file, which means you would have a single repository — to have two repos, you'd need two WORKSPACE.bazel
files. If I clone both of your repos (app2
and lib2
), and I put the WORKSPACE.bazel
file you mentioned in the root, and run a bazel query //...
, I get the two targets:
//app2:app
//lib2:time_util
As a side note, since you're using Bazel 7, you might want to switch to using MODULE.bazel
instead of WORKSPACE.bazel
files. So here is how you'd do something like this with Bazel modules, which is the modern way of managing dependencies.
Create a file app2/MODULE.bazel
:
bazel_dep(name = "rules_cc", version = "0.0.9")
non_module_dependencies = use_extension("//:extensions.bzl", "non_module_dependencies")
use_repo(non_module_dependencies, "lib2")
Then create the app2/extensions.bzl
file:
"""Registers any non-module dependencies here."""
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
def _non_module_dependencies_impl(_ctx):
git_repository(
name = "lib2",
remote = "https://wszarmach@bitbucket.org/szarmach/lib2.git",
branch = "main", # branch, tag, commit, hash
)
non_module_dependencies = module_extension(
implementation = _non_module_dependencies_impl,
)
The name = lib2
in the git_repository
arg is what you need to add in the use_repo
call in MODULE.bazel
, to make it available as a module dependency.
You also need rules_cc
, which comes straight from the BCR.
Now that you have your dependencies set up, you need to change your app2/BUILD.bazel
as follows:
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "app",
srcs = ["app.cpp"],
deps = ["@lib2//:time_util"]
)
Note that now we're using @lib2//:time_util
instead of //lib2:time_util
.
You no longer need the WORKSPACE.bazel
file in the root directory, since you don't have a package root there. Each repo has its own package root.
After setting this all up, you'll notice that the build will still fail. This is because in app.cpp
, you reference the library like this:
#include "lib2/time_util.h"
You need to change this to be:
#include "time_util.h"
This is because, while previously you needed the full paths from the workspace root, now each workspace has its own root, and in lib2
, time_util.h
is actually at the root, not under a sub-package.
When you invoke bazel build //:app
and it fails, you can see that Bazel adds the C compiler flag -iquote external/_main~non_module_dependencies~lib2
, among others. This essentially means anything under the lib2
repo can be #include
d using quotes, without any prefix.
Note that, in lib2
, you also need to change the #include
in time_util.cpp
, and don't forget to commit & push the change, or use a local override while developing on multiple modules locally.