OS dependent rules in Bazel [duplicate] - bazel

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?

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?

What label to use in `deps` for `bazel` rule `proto_library` to be able to use `google/protobuf/timestamp.proto`?

I have a .proto file in which I'd like to import "google/protobuf/timestamp.proto";
The build fails, though, with google/protobuf/timestamp.proto: File not found. I'm assuming it needs deps to be specified for the proto_library bazel rule but don't know exactly what label to use.
The following from https://github.com/bazelbuild/rules_nodejs/issues/1820 works:
proto_library(
name = "proto_library",
srcs = glob(["*.proto"]),
visibility = ["//visibility:public"],
deps = [
"#com_google_protobuf//:timestamp_proto",
],
)

Bazel: share macro between multiple http_archive BUILD files

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.

How do I tell Bazel where Python.h lives?

I'm building a C++ executable that needs to #include "Python.h" from the user's Python installation.
To express Python.h (and the various header files it includes) in Bazel, I need to know where the Python include directory is. This location will be different on Windows and Linux, and I'd like a single Bazel configuration to build them both.
What's the best Bazel practice for referencing software that exists outside of the WORKSPACE root?
So to tell Bazel about external dependencies you need to use one of the Workspace Rules to specify the location of the external dependency, as well as the BUILD file for Bazel to use with that external dependency.
To have something work cross-platform you need to use the select() function to have Bazel select the proper library to build against for your host operating system.
Here's a stab at accomplishing it:
First we have the WORKSPACE file in your project's root that defines the two libraries and the BUILD file to use for them. Here I'm just using build_file_content but if that becomes too complex you can put it in it's own file and reference it instead. The BUILD file here exposes the prebuild library shipped with Python along with the header files needed. It also adds an include path for any targets that depend on these libraries so you can do #include "Python.h"
new_local_repository(
name = "python_linux",
path = "/usr",
build_file_content = """
cc_library(
name = "python35-lib",
srcs = ["lib/python3.5/config-3.5m-x86_64-linux-gnu/libpython3.5.so"],
hdrs = glob(["include/python3.5/*.h"]),
includes = ["include/python3.5"],
visibility = ["//visibility:public"]
)
"""
)
new_local_repository(
name = "python_win",
path = "C:/Python35",
build_file_content = """
cc_library(
name = "python35-lib",
srcs = ["libs/python35.lib"],
hdrs = glob(["include/*.h"]),
includes = ["include/"],
visibility = ["//visibility:public"]
)
"""
)
Next the BUILD file for your application. Here you need to define some config_settings. This allows us to define platform dependent settings for our build. We use the cpu value to determine the host OS.
In the cc_binary rule we use the select() function to choose the correct host library to link against based on the configuration.
config_setting(
name = "linux_x86_64",
values = {"cpu": "k8"},
visibility = ["//visibility:public"],
)
config_setting(
name = "windows",
values = {"cpu": "x64_windows"},
visibility = ["//visibility:public"],
)
cc_binary(
name="python-test",
srcs = [
"main.c",
],
deps = select({
"//:linux_x86_64": [
"#python_linux//:python35-lib"
],
"//:windows": [
"#python_win//:python35-lib"
]
})
)
FWIW here's the main.c I was playing around with to get this working.
#include "Python.h"
int main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]); /* optional but recommended */
Py_Initialize();
PyRun_SimpleString("from time import time,ctime\n"
"print('Today is',ctime(time()))\n");
Py_Finalize();
return 0;
}
Another way (and perhaps simpler) is checking the python headers and libraries into your repository. You will still need to use select() to choose the correct library to link against but at least you won't need to add anything to your WORKSPACE file and can just rely on another BUILD file in your repository. If you look at the Bazel repo they check in lots of external dependencies into the third_party directory, so it's a common practice.

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