Package transitive deps in Bazel - c++

I would like to package lets say a binary with the pkg_tar command. But I would also automatically like it to include all deps to that binary, for example all .so files from other Bezel targets that are referenced with deps. Is it possible?
pkg_tar(
name = "example",
srcs = ["//myprogram"], # This only packages myprogram target
mode = "0644",
)

Currently, this feature isn't officially supported. You have three basic options:
explicitly enumrate all deps
use one of "hacks" from https://github.com/bazelbuild/bazel/issues/1920
use undocumented include_runfiles = True feature https://github.com/bazelbuild/rules_pkg/issues/145

Setting the (undocumented) include_runfiles = True will include the shared object and any other runfiles of all the transitive dependencies.

Related

Add icu4c (and other libs) to bazel project

I have a project that builds via CMake and requires a lot of manual installations of additional deps. I want to migrate this project to Bazel and make these libs automatically downloadable. I found a solution for Boost, but I can't understand how to add icu4c and other libs which builds via other tools.
There are many ways to make use of third-party libraries using Bazel. The chosen approach depends on different properties of the third-party library, e.g.: Does the third-party library already support Bazel? Is the library available only as a pre-build package? Does the library use code generators, or any other tools, or transitive dependencies?
Given the example of {fmt} which uses CMake as build system you can proceed as the following:
First approach: Inject a BUILD file
In your WORKSPACE file you can do something like:
maybe(
new_git_repository,
name = "fmt",
branch = "master",
remote = "https://github.com/fmtlib/fmt",
build_file = "//third_party:fmt.BUILD",
)
The corresponding fmt.BUILD file can look like this:
cc_library(
name = "fmt",
srcs = [
#"src/fmt.cc", # No C++ module support
"src/format.cc",
"src/os.cc",
],
hdrs = [
"include/fmt/args.h",
"include/fmt/chrono.h",
"include/fmt/color.h",
"include/fmt/compile.h",
"include/fmt/core.h",
"include/fmt/format.h",
"include/fmt/format-inl.h",
"include/fmt/locale.h",
"include/fmt/os.h",
"include/fmt/ostream.h",
"include/fmt/printf.h",
"include/fmt/ranges.h",
"include/fmt/xchar.h",
],
includes = [
"include",
"src",
],
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)
Advantages:
fmt-8.01 does not have out-of-the-box support for {fmt}. This way Bazel can make use of {fmt} without the need that {fmt} knows anything about Bazel
fmt-8.0.1 needs not to be modified
Disadvantages:
Reinvent the wheel: Every Bazel project that wants to use {fmt} has to reinvent this fmt.BUILD file.
Maintenance costs: If different Bazel projects want to adapt to future versions of {fmt} every single project has to do this maintenance by its own. Maybe new files will be introduced.
Missing Knowledge: Maybe for some reason, it makes sense to define some special defines upfront, etc. It also takes some time and knowledge of {fmt} to set up such a BUILD file. What is the best practice to build this lib?
Second approach: Bazelize {fmt}
Add a WORKSPACE file and BUILD file to the {fmt} repository.
This way {fmt} gets bazelized and can be used in your Bazel builds.
You could use it then this way:
Example
Create a WORKSPACE.bazel file with the following content:
load("#bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Fetch bazelized fmt
git_repository(
name = "fmt",
branch = "bazel-support", # A copy of master where BUILD.bazel, WORKSPACE.bazel, .bazelrc and .bazelversion are moved to root
remote = "https://github.com/<user_or_organisation>/fmt", # replace <user_or_organisation> by a valid account
)
Create a BUILD.bazel file and add a dependency to {fmt} (wit the content of fmt.BUILD).
In favor of keeping the {fmt} project directory clean, those files were not added to the project root directory (see here for details).
Third approach: Using the {fmt} repository with Bazel
Even though the {fmt} repository does not contain a WORKSPACE file in its root directory, there is an easy approach to use the {fmt} repository with Bazel out of the box. This is demonstrated in the following example.
Add to your WORKSPACE file:
load("#bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
# Fetch all files from fmt including the BUILD file `support/bazel/BUILD.bazel`
new_git_repository(
name = "fmt_workaround",
branch = "master",
remote = "https://github.com/fmtlib/fmt/",
build_file_content = "# Empty build file on purpose"
)
# Now the BUILD file `support/bazel/BUILD.bazel` can be used:
new_git_repository(
name = "fmt",
branch = "master",
remote = "https://github.com/fmtlib/fmt/",
build_file = "#fmt_workaround//:support/bazel/BUILD.bazel"
)
Create a BUILD.bazel file and add a dependency to {fmt}:
cc_binary( # Build a binary
name = "Demo", # Name of the binary
srcs = ["main.cpp"], # List of files - we only have main.cpp
deps = ["#fmt//:fmt"], # Depend on fmt
)
Make use of {fmt} in main.cpp:
#include "fmt/core.h"C
int main() {
fmt::print("The answer is {}.\n", 42);
}
The expected output of this example is The answer is 42.
Forth approach: Make use of patch_cmd
load("#bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "fmt",
branch = "master",
patch_cmds = [
"mv support/bazel/.bazelrc .bazelrc",
"mv support/bazel/.bazelversion .bazelversion",
"mv support/bazel/BUILD.bazel BUILD.bazel",
"mv support/bazel/WORKSPACE.bazel WORKSPACE.bazel",
],
# Windows related patch commands are only needed in the case MSYS2 is not installed
patch_cmds_win = [
"Move-Item -Path support/bazel/.bazelrc -Destination .bazelrc",
"Move-Item -Path support/bazel/.bazelversion -Destination .bazelversion",
"Move-Item -Path support/bazel/BUILD.bazel -Destination BUILD.bazel",
"Move-Item -Path support/bazel/WORKSPACE.bazel -Destination WORKSPACE.bazel",
],
remote = "https://github.com/fmtlib/fmt",
)
More details here.
Other libraries
I have written a few blog posts about Bazelizing different libs:
Bazel: Bazelizing Qt5 for macOS
Bazel: Bazelizing Embree 3.13.0
Bazel: Bazelizing Qt5 & Qt6
Bazel: Handling external dependencies in OpenEXR
Bazel: Offical support for OpenEXR
Bazel: Bazelizing OpenEXR
Bazel: Bazelizing Embree 3.12.1

How to link a library build using make rule in bazel

I have built a lib.so using make rule in bazel. How do I link this external lib.so to a regular cc_library rule. I tried adding it in deps, but the guide suggests that deps can have cc_library or objc_library targets.
Also, do I need to pass any specific linking options, and how can I read more about them?
In the BUILD file, create a cc_library target that imports the built lib.so for other cc_library targets to depend on:
cc_library(
name = "lib",
srcs = ["lib.so"],
linkopts = ["...", "..."],
)
See the documentation on C++ use cases for more information.

Bazel create C++ shared library with soname

I would like to create a shared c++ library with Bazel using a soname.
With cmake I could set properties like:
set_target_properties(my-library
PROPERTIES
SOVERSION 3
VERSION 3.2.0
)
which would then generate
libmy-library.so -> libmy-library.so.3
libmy-library.so.3 -> libmy-library.so.3.2.0
libmy-library.so.3.2.0
However in bazel documentation I cannot find anything that would allow me to do so easily. I know that I could define the soname and version directly and pass some linkopts in the build file:
cc_binary(
name = "libmy-library.so.3.2.0",
srcs = ["my-library.cpp", "my-library.h"],
linkshared = 1,
linkopts = ["-Wl,-soname,libmy-library.so.3"],
)
which does produce libmy-library.so.3.2.0 with the correct soname, but not the .so file so it would require a whole lot of hacks around to:
create libmy-library.so.3 symlink
create libmy-library.so symlink
create some import rules such that I can build binaries that link with this library.
This does not feel like the right way. What would be the right way to solve such problem?

Can I provide a relative deps path in my bazel target?

When I specify build rules in bazel, my dependencies are either full paths (from the root of the repo), or just the target name (since its in the same directory):
cc_binary(
name = "program",
srcs = ["main.cpp"],
deps = ["//a/full/path/to/the/library:lib",
"foo"]
)
Assume I'm writing a build rule from directory "the".
I was hoping to do something like this:
cc_binary(
name = "program",
srcs = ["main.cpp"],
deps = ["library:lib",
"foo"]
)
This does not seem to be possible. Is there some kind of way, where I can specify the target deeper starting from the location of the BUILD file?
You cannot.
Relative labels cannot be used to refer to targets in other packages;
the repository identifier and package name must always be specified in this case.
From Bazel labels documentation

How to use select to properly detect whether I am building C++ code in Windows or Linux?

I am writing a sample C++ project that uses Bazel to serve as an example idiom for other collaborators to follow.
Here is the repository: https://github.com/thinlizzy/bazelexample
I am interested to know if I am doing it 'right', more specifically about this file: https://github.com/thinlizzy/bazelexample/blob/38cc07931e58ff5a888dd6a83456970f76d7e5b3/demo/BUILD
when regarding to pick particular implementations.
cc_library(
name = "demo",
srcs = ["demo.cpp"],
deps = [
"//example:frontend",
],
)
cc_binary(
name = "main_win",
deps = [
":demo",
"//example:impl_win",
],
)
cc_binary(
name = "main_linux",
deps = [
":demo",
"//example:impl_linux",
],
)
Is this following a correct/expected idiom for Bazel projects? I am doing this way already for other projects, by concentrating all the platform-specific dependencies in separate targets and then the binaries just depend on them.
Someone in bazel-discuss list told me to use select, instead, but my attempts failed to 'detect' the operating system. I'm sure I did something wrong, but the lack of info and examples don't tell me much how to use it properly.
#bazel_tools contains predefined platform conditions:
$ bazel query #bazel_tools//src/conditions:all
#bazel_tools//src/conditions:windows_msys
#bazel_tools//src/conditions:windows_msvc
#bazel_tools//src/conditions:windows
#bazel_tools//src/conditions:remote
#bazel_tools//src/conditions:host_windows_msys
#bazel_tools//src/conditions:host_windows_msvc
#bazel_tools//src/conditions:host_windows
#bazel_tools//src/conditions:freebsd
#bazel_tools//src/conditions:darwin_x86_64
#bazel_tools//src/conditions:darwin
You can use them directly in the BUILD file:
cc_library(
name = "impl",
srcs = ["Implementation.cpp"] + select({
"#bazel_tools//src/conditions:windows": ["ImplementationWin.cpp"],
"#bazel_tools//src/conditions:darwin": ["ImplementationMacOS.cpp"],
"//conditions:default": ["ImplementationLinux.cpp"],
}),
# .. same for hdrs and data
)
cc_binary(
name = "demo",
deps = [":impl"],
)
See the documentation for select for details on the syntax.
Add a .bazelrc to your project. Add the lines build:vs2019 --cxxopt=/std:c++14 and build:gcc --cxxopt=-std=c++14. Build your code bazel build --config=msvc //... or bazel build --config=gcc //....
#Vertexwahn's answer caused some confusion on my end, so I hope this answer helps clarify a bit. While his answer does not directly tie into the question, it may be of use to others trying to build on entirely different platforms without file specific inclusions.
Here is a link to where I answered that particular question: How do I specify portable build configurations for different operating systems for Bazel?