Bazel: share macro between multiple http_archive BUILD files - bazel

My project depends on some external libraries which I have to bazelfy myself. Thus, my WORKSPACE:
http_archive(
name = "external_lib_component1",
build_file = "//third_party:external_lib_component1.BUILD",
sha256 = "xxx",
urls = ["https://example.org/external_lib_component1.tar.gz"],
)
http_archive(
name = "external_lib_component2",
build_file = "//third_party:external_lib_component2.BUILD",
sha256 = "yyy",
urls = ["https://example.org/external_lib_component2.tar.gz"],
)
...
The two entries above are similar, and external_lib_component{1, 2}.BUILD share a lot of code.
What is the best way to share code (macros) between them?
Just putting a shared_macros.bzl file into third_party/ won't work, because it will not be copied into
the archive location on build (only the build_file is copied).

If you place a bzl file such a In your./third_party/shared_macros.bzl into your tree as you've mentioned.
Then in the //third_party:external_lib_component1.BUILD and //third_party:external_lib_component2.BUILD you provide for your external dependencies, you can load symbols from that shared file using:
load("#//third_party:shared_macros.bzl", ...)
Labels starting with #// refer to packages from the main repository, even when used in an external dependency (as they would otherwise be rooted when starting with //. You can for check docs on labels, in particular the last paragraph.
Alternatively you can also refer to the "parent" project by its name. If in your WORKSPACE file you've had:
workspace(name = "parent")
You could say:
load("#parent//third_party:shared_macros.bzl", ...)
Note: in versions prior to 2.0.0 you might want to add --incompatible_remap_main_repo if you mixed both of above approaches in your project.

Related

How to properly handle cyclic dependencies in Bazel without changing the source code?

I have read the question, but unfortunately the proposed solutions are to change source code of header files location, which on this case it's not possible.
Let's imagine I have the following structure of system includes from an installed package:
/usr/local/bar/bar1.h
/usr/local/bar/bar2.h
And
/usr/include/foo.h
Important detail:
bar1.h includes foo.h
foo.h includes bar2.h
From Bazel, I would right away create a new_git_repository() from /usr/local/bar and then create a library containing the header files from this folder as:
cc_library(
name = "libBar",
hrds = ["bar1.h", "bar2.h"],
deps = ["#foo//:libFoo"],
)
I would do another new_git_repository() from /usr/include/ and create a library:
cc_library(
name = "libFoo",
hrds = ["foo.h"],
deps = ["#bar//:libBar"],
)
As seen, this will end up in a compilation error from Bazel regarding cyclic dependencies:
libBar is dependent of libFoo, and libFoo is dependend of libBar.
Or course a solution would be to separate the libBar into some libBar1 and libBar2. But I would want to avoid this solution as the folder /usr/local/bar contains too many header files and separating this will be a nightmare.
Is there a proper solution for this case?

Bazel: How to make new_local_repository target depend on target generated by http_archive?

I have several third party libraries that depend on openssl, so I fetch and build openssl via repository mechanic (http_archive()). And I have GRPC that fetch boringssl that has same symbols that an openssl (after linking i get an error due collision).
I want to redefine boringsll using new_local_repository() method. But I don't known how to pass generated path to path argument and how make new_local_repository() call depend on openssl target.
The code that a want to get, looks like this:
new_local_repository(
name = "boringssl",
??? path = "bazel-out/k8-fastbuild/bin/external/openssl/openssl/", <-- generated path with openssl
build_file_content = """
cc_library(
name = "ssl",
deps = ["#openssl"],
srcs = ["lib/libssl.a"],
hdrs = glob(["include/openssl/*.h"]),
strip_include_prefix = "/include/openssl",
visibility = ["//visibility:public"],
)
cc_library(
name = "crypto",
deps = ["#openssl"],
srcs = ["lib/libcrypto.a"],
hdrs = glob(["include/openssl/*.h"]),
strip_include_prefix = "/include/openssl",
visibility = ["//visibility:public"],
)
""",
)
I would write a replacement #boringssl with aliases for each rule. Something like this as third_party/boringssl/BUILD.bazel:
alias(
name = "crypto",
actual = "#openssl//:libcrypto",
visibility = ["//visibility:public"],
)
Then you can ad it to your WORKSPACE with a relative path:
local_repository(
name = "boringssl",
path = "third_party/boringssl",
)
This also lets you freely map target names if they differ. Alternatively, you could write cc_library wrappers (without srcs, just deps) to do things like combine multiple #openssl targets into a single one for the #boringssl equivalent.
With your approach, even if you use some hacks to reuse the repository directory or copy the source files, you will still have linking problems. Bazel will build all the #openssl source files twice and then try linking both copies in. The linker might just pick one and make everything work, or it might refuse to link, or you might get subtle problems at runtime with duplicated global state.

Does Bazel need external-repo BUILD files to be in $WORKSPACE_ROOT/external?

I made a repository for glfw with this:
load("#bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
new_git_repository(
name = "glfw",
build_file = "BUILD.glfw",
remote = "https://github.com/glfw/glfw.git",
tag = "3.2.1",
)
I put BUILD.glfw in the WORKSPACE root. When I built, I saw:
no such package '#glfw//': Not a regular file: [snipped/external/BUILD.glfw
I moved BUILD.glfw to external/BUILD.glfw and it seems to work, but I couldn't find documentation about this. The docs about new_git_repository say that build_file "...is a label relative to the main workspace."; I don't see anything about 'external' there.
This is due to an inconsistent semantical difference between the native and (newer) Skylark versions of new_git_repository. To use the native new_git_repository, comment/remove the load statement:
# load("#bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
Assuming that new_git_repository has the same problem that http_archive has, per Bazel issue 6225 you need to refer to the BUILD file for glfw as #//:BUILD.glfw

Using bazel macros across repositories with labels

I've got two repositories, Client and Library.
Inside of Client's WORKSPACE file Client imports Library as a http_archive with the name "foo".
Inside of Client, I want to use Library macros that reference targets inside Library. My problem is that the Library macros don't know that were imported as "foo", so when the macro is expanded the targets are not found.
library/WORKSPACE:
workspace(name = "library")
library/some.bzl:
def my_macro():
native.java_library(name = "my_macro_lib",
deps = ["#library//:my_macro_lib_dependnecy"]
)
library/BUILD.bazel:
java_library(name = "my_macro_lib_dependnecy",
...
)
client/WORKSPACE:
workspace(name = "client")
http_archive(
name = "library",
urls = [...],
strip_prefix = ...,
sha256 = ...,
)
Because both workspaces use the same name for library workspace (name = "library") and because the macro refers to the workspace name in its dependencies (#library//:my_macro_lib_dependnecy) this works.
Note this works but has some quirks which will be resolved in 0.17.0

How to modify just one file when repackaging an archive?

I'm trying to take a non-Bazel produced zipfile, modify some files in it, keeping most of them alone, and then ultimately produce a new tarball with the ~original content (plus my modifications)
I'm having trouble specifying my rules in a clean way and it'd be great if there was a suggestion on how to do it.
I'm importing the original zip file via the 'new_http_archive' WORKSPACE rule. This works pretty well. I put the build file in a package one level under the root. Let's call this 'foo_repackage'.
In foo_repackage/BUILD.root_archive:
package(default_visibility = ["//visibility:public"])
filegroup(
name = "all_files",
srcs = glob(
["**"],
exclude = ["*", "share/doc/api/**"]
),
)
The bigger issue is in the foo_repackage/BUILD file, I'd like to take all of the files out of the all_files group above, except for a few of them that I will modify. I can't see how to do this easily. It seems like every file that I want to modify I should exclude from the above glob and make a new rule that specifies that file. This means that I have to keep modifying the global all_files exclude rule.
If I could create a new filegroup that was all of the above files with some files excluded, that'd be ideal.
I should mention that the last step is of course to use pkg_tar to repackage the result - this is in foo_repackage/BUILD
pkg_tar(
name = "OutputTarball",
files = ["#root_archive//:all_files"],
deps = [":layers_of_modified_files"],
strip_prefix = "/../root_archive",
)
Does anyone have a better way to do this?
Thanks, Sean
Could you use a variable like:
MODIFIABLE_FILES = [
"some/file",
"another/file",
...
]
filegroup(
name = "static-files",
srcs = glob(["**"], exclude = MODIFIABLE_FILES)
)
filegroup(
name = "modifiable-files",
srcs = MODIFIABLE_FILES,
)
Then the list of static files and modifiable files will be kept in sync and you'll get a build error if you accidentally specify a non-existent modifiable file.

Resources