How to fail a Bazel build on a rule failure? - bazel

I am using Bazel rules in NodeJS in my application. The aim is to simply lint a set of files and fail the build if linting fails. What I'm currently experiencing is that the build is successful despite lint errors.
Here's a part of my BUILD file:
load("#npm//htmlhint:index.bzl", "htmlhint")
filegroup(
name = "htmldata",
srcs = glob(["**/*.html"]),
)
htmlhint(
name = "compile",
data = [
"htmlhint.conf",
"//:htmldata"
],
args = [
"--config",
"htmlhint.conf",
"$(locations //:htmldata)"
]
)
I first load the hinting library, then I define a filegroup for all the HTML files that I want to lint. Afterward, I use the rule with its data and arguments.
To run the build, I use the default option via npm script: bazel build //...

Your build file is working as expected. Unfortunately it doesn't do what you want, because when you load the macro from #npm//htmlhint:index.bzl it sets up the nodejs binary which is a runnable target, which means that it will only create runfiles + executable when building. In this case, the build will not run the library.
There are several options to do what you want:
Use the htmlhint_test macro to create a test target.
Create a custom rule that will use the nodejs binary to build some artefacts. In this case, you can force the build to fail.
However, I suggest using the first approach, because if htmlhint is a linting tool, it won't produce any meaningful outputs and is best to keep it as part of the test suite.
Here's what you need to do to set up the compile target as a test target
diff --git a/BUILD.bazel b/BUILD.bazel
index 4e58ac5..3db5dbb 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
## -1,11 +1,11 ##
-load("#npm//htmlhint:index.bzl", "htmlhint")
+load("#npm//htmlhint:index.bzl", "htmlhint_test")
filegroup(
name = "htmldata",
srcs = glob(["**/*.html"]),
)
-htmlhint(
+htmlhint_test(
name = "compile",
data = [
"htmlhint.conf",
Then you can check it with bazel test //....
If you want to see the output just run your compile target with bazel run //path/to:compile

Related

What is causing duplicate builds of same target in Bazel?

We are seeing duplicate builds of the same target in Bazel and wondering what could cause this.
Here is a sample output:
[52,715 / 55,135] 12 action running
Bazel package: some-pkg - Target: a_target - Generating files at bazel-out/host/bin/some-pkg/a_target_generate [for host]; 264s remote-cache, processwrapper-sandbox
Bazel package: some-pkg - Target: a_target - Generating files at bazel-out/k8-fastbuild/bin/some-pkg/a_target_generate; 264s remote-cache, processwrapper-sandbox
...
We have not been able to identify the issue. It looks like this is only happening on Linux but not on Macs.
The target a_target is a custom_rule target. It should be platform independent.
custom_rule = rule(
attrs = dict(
...
_custom_rule_java_binary = attr.label(
cfg = "host",
default = Label("//tools/bazel/build/rules/custom-rule:custom_rule_bin"),
executable = True,
),
_singlejar = attr.label(
cfg = "host",
default = Label("#bazel_tools//tools/jdk:singlejar"),
executable = True,
allow_files = True,
),
),
implementation = ...,
)
custom_rule_bin is defined as follow:
java_library(
name = "custom_rule",
srcs = glob(["src/main/java/**/*.java"]),
deps = [
...,
],
)
java_binary(
name = "custom_rule_bin",
visibility = ["//visibility:public"],
main_class = "...",
runtime_deps = [
"#org_slf4j_simple",
":custom_rule",
"//some-pkg:some_pkg", # same some-pkg where a_target is built twice
],
)
The difference is that one says "for host" and the other doesn't. Anyone knows what the extra "for host" build is?
I do have a feeling that it's somehow related to the cfg attribute on the custom rule. This is likely coming from some example code. We use the same value on all our rules which generate code. This custom rule is special because it requires code from the application being built by Bazel to run and generate additional code.
Any insights appreciated why host would be wrong and what would be the correct value.
Any ideas/tips how to debug this?
First, one note is that the host configuration is being mostly deprecated, and "exec" is usually preferred. Some info about that is here: https://bazel.build/rules/rules#configurations.
What's happening is that that target is being depended upon in multiple configurations, and so bazel will build that target in each configuration. You can use cquery to figure out what's going on
As a very simple example:
genrule(
name = "gen_bin",
outs = ["bin"],
srcs = [":gen_lib"],
exec_tools = [":gen_tool"],
cmd = "touch $#",
)
genrule(
name = "gen_tool",
outs = ["tool"],
srcs = [":gen_lib"],
cmd = "touch $#",
)
genrule(
name = "gen_lib",
outs = ["lib"],
cmd = "touch $#; sleep 10",
)
Building bin, bazel runs the gen_lib genrule twice (in parallel):
$ bazel build bin
INFO: Analyzed target //:bin (5 packages loaded, 16 targets configured).
INFO: Found 1 target...
[1 / 5] 2 actions running
Executing genrule //:gen_lib; 1s linux-sandbox
Executing genrule //:gen_lib; 1s linux-sandbox
bazel config gives the configurations that are currently in the in-memory build graph:
$ bazel config
Available configurations:
5b39bc31deb1f1d37f1f858e7eec3964394eacce5bede4456dd59d417af4a6e9 (exec)
723da02ae6d0c5577e98242c8f06ca1bd1c6d7b295c97345ac31b844bfe8f79c
8960923b9e7dc13418be101268efd8e57d80283213d18174705345598b699c6b
fd805cc1de357c04c7abac1b40bae600e3d9ee56a8d17af0c28b5031ca09bfb9 (host)
then cquery:
$ bazel cquery "rdeps(//..., gen_lib)"
INFO: Analyzed 3 targets (0 packages loaded, 1 target configured).
INFO: Found 3 targets...
//:gen_lib (5b39bc3)
//:gen_lib (8960923)
//:gen_tool (5b39bc3)
//:gen_bin (8960923)
//:gen_tool (8960923)
INFO: Elapsed time: 0.052s
INFO: 0 processes.
INFO: Build completed successfully, 0 total actions
(cquery gives the first 7 digits of the configuration hash)
--output=graph gives a dot graph which is a little more useful:
$ bazel cquery "rdeps(//..., gen_lib)" --output=graph > /tmp/graph
$ xdot /tmp/graph
So gen_bin is in the target configuration (8960923), and it depends on gen_lib, so gen_lib will also be built in the target configuration.
gen_bin also depends on gen_tool via the exec_tools attribute, and exec_tools builds everything in the exec configuration (5b39bc3).
gen_tool also depends on gen_lib, and since gen_tool is in the exec configuration, a version of gen_lib is built in the exec configuration.
(There's also another version of gen_tool in the target configuration in the output, and that's an artifact of using //... in the "universe" argument of rdeps(), since //... will capture every target. Similarly, doing bazel build //... would cause gen_tool to be built twice.)

Add Go Test binary to container_image using Bazel

I am building a go test package that I would like to have included in a Docker image. The binary can be built using bazel build //testing/e2e:e2e_test. This creates a binary in the bazel_bin folder. Now I would like to take this binary and add it to a docker image...
container_image(
name = "image",
base = "#alpine_linux_amd64//image",
entrypoint = ["/e2e_test"],
files = [":e2e_test"],
)
This gives me the following error
ERROR: ...BUILD.bazel:27:16: in container_image_ rule //testing/e2e:image: non-test target '//testing/e2e:image' depends on testonly target '//testing/e2e:e2e_test' and doesn't have testonly attribute set
ERROR: Analysis of target '//testing/e2e:image' failed; build aborted: Analysis of target '//testing/e2e:image' failed
Ultimately, what I am trying to accomplish is creating a application that contains a suite of end to end tests using the Go Test framework. I can then distribute these tests in a docker container to be run in testing environment.
The error is saying that a non-testonly target depends on a testonly one, which the docs for testonly say isn't allowed:
If True, only testonly targets (such as tests) can depend on this target.
Equivalently, a rule that is not testonly is not allowed to depend on any rule that is testonly.
You can do what you're looking for by making your target testonly like this:
container_image(
name = "image",
testonly = True,
base = "#alpine_linux_amd64//image",
entrypoint = ["/e2e_test"],
files = [":e2e_test"],
)

how to build cc_test for android using bazel

I am trying to use bazel as build system.
My project looks like this:
a static library which contains a bunch of classes and functions
a dynamic library (so/dll) using the same code as the static library (need to have it because of windows, cc_library rule does not automatically build a dll on windows)
a cc_test rule which build an executable. it contains unit tests based on google test framework
It works when running on Windows and Linux.
The test rule fails when trying to build Android like this
bazel build //unit:unit --crosstool_top=#androidndk//:default_crosstool --cpu=armeabi-v7a
INFO: Invocation ID: b7c88128-3448-4eb7-bf25-ce8269895956 ERROR: ../yg32wcuz/external/androidndk/BUILD.bazel:39:1: in cc_toolchain_suite rule #androidndk//:toolchain-gnu-libstdcpp: cc_toolchain_suite '#androidndk//:toolchain-gnu-libstdcpp' does not contain a toolchain for cpu 'x64_windows'
ERROR: Analysis of target '//unit:unit' failed; build aborted: Analysis of target '#androidndk//:toolchain-gnu-libstdcpp' failed; build aborted
It looks like that bazel seems to have problem with cc_test and android toolchain
Is there any way to build and run an executable for android using bazel? Maybe I missed some command line arguments
Edit:
tried the solution below and added a sh_test rule but it fails again
using #androidsdk//:adb and leads to the following error
ERROR: missing input file '#androidsdk//:platform-tools/adb'
ERROR: unit/BUILD:61:1: //unit:unit_android: missing input file '#androidsdk//:platform-tools/adb' Target //unit:unit_android failed to build
ERROR: unit/BUILD:61:1 1 input file(s) do not exist
I also need to use $ANDROID_HOME/platform-tools/adb to get the adb binary. external/androidsdk/platform-tools/adb does not work. my BUILD file is in a sub folder of the workspace, maybe this is the issue.
removing #androidsdk//:adb fixes this error. there are some adjustments needed in sh_test rule like:
sh_test(
name = "unit_android",
srcs = ["unit_android.sh"],
data = [
":unit",
#"#androidsdk//:adb",
],
deps = [
"#bazel_tools//tools/bash/runfiles", # to access the manifest
],
)
using runfiles dependency allows me to access the binary via $(rlocation ..) in shell script. but now there seems to be another issue:
when using 'bazel run':
It looks like that bazel is trying to upload the file to msys shell (i am using windows) and not to the device:
adb: error: failed to copy '.../_bazel_exb_a/yg32wcuz/execroot/test/bazel-out/armeabi-v7a-fastbuild/bin/unit/unit' to 'C:/Development/msys2/data/local/tmp/unit'
when using 'bazel test':
it just states an error and the content of test log is
unknown parameter - /users
Edit 2:
WORKSPACE file about android sdk/ndk
android_ndk_repository(
name = "androidndk", # Required. Name *must* be "androidndk".
api_level = 26
)
android_sdk_repository(
name = "androidsdk", # Required. Name *must* be "androidsdk".
api_level = 26
)
In both case I assume env var ANDROID_NDK_HOME (points to ndk), ANDROID_SDK_HOME (points to sdk) and ANDROID_HOME (points to sdk) are set. I also checked the external dir, sdk is in there. Removing "#androidsdk//:adb" seem to work but the bazel shell environment now tries to add a prefix before "/data/local/tmp" and tries to upload to a non existing folder.
forget about the issue with "/users" (windows path issue ...)
--crosstool_top by itself sets both the target and host crosstool, so you may just need to set --host_crosstool_top back to the default: --host_crosstool_top=#bazel_tools//tools/cpp:toolchain
Edit:
Running the test on a device is unfortunately not supported out of the box by bazel test. There needs to be some test runner that knows how to put the test on the device, run it, and collect the results. A very simple version of that might look like:
test.cc:
int main(int argc, char** argv) {
// Test always passes.
// Return non-zero for test failure.
return 0;
}
example_android_cc_test.sh:
adb=external/androidsdk/platform-tools/adb
# The test requires a running emulator or connected device.
# The name of the cc_test binary can be passed in using the
# args attribute of sh_test to make this script generic.
$adb push example_test /data/local/tmp
# adb shell returns the exit code of the command
# that was executed, and the exit code of the
# test shell script determines if the sh_test target
# passes or fails.
$adb shell "/data/local/tmp/example_test"
BUILD:
cc_test(
name = "example_test",
srcs = ["test.cc"],
linkopts = ["-pie"],
linkstatic = 1,
)
sh_test(
name = "example_android_cc_test",
srcs = ["example_android_cc_test.sh"],
data = [
":example_test",
"#androidsdk//:adb",
],
)
Note that this approach is not hermetic because it relies on an emulator to already be running, or a device to be already connected. It's possible to start an emulator as part of the test, but that's more involved.

Specify "--build_python_zip" flag within Bazel py_binary rule

Is it possible to specify the bazel "--build_python_zip" flag from within the py_binary rule so that I don't need to add this flag every time I use Bazel in my workspace?
It doesn't seem that there's a way to specify this per py_binary target, according to this issue.
However, you can use a bazelrc file to store common options. Add the following line to your workspace's bazelrc at <workspace>/.bazelrc:
build --build_python_zip
Note that using this method, every invocation of bazel build, bazel test and bazel run will include this flag.
After PR 9453, you can:
filegroup(
name = "foo_zip",
srcs = [":foo_binary"],
output_group = "python_zip_file",
)
py_binary(
name = "foo_binary",
srcs = ["foo.py"],
)
Then you can invoke bazel as:
bazel build :foo_zip
and don't have to specify --build_python_zip. This will also allow par_binary rules to co-exist with native zips.

Bazel cc_library dependency on other cc_library when each compile with a different crosstool

I have a code generator tool that generates C/C++ code. This code generator tool is compiled with crosstool1. The generated C/C++ code needs to be compiled with crosstool2.
So the actions are:
Using Crosstool1 compile 'code_generator'.
Execute 'code_generator' and generate 'generated_code.cpp'
Using Crosstool2 compile 'generated_code.cpp'
Is it possible to make a cc_library() determine the crosstool to use? I saw that Skylark rules now allow a 'toolchains' parameter which I'm not sure how this is used, also I do not want to do the heavy lifting of C/C++ compiling bare bone with Skylark.
Is there an example of using a proper Host Crosstool and Target Crosstool except for the Tenserflow example? I get a headache each time I read it :D
Assume //crosstool1:toolchain is a label for cc_toolchain_suite rule describing first crosstool, //crosstool2:toolchain is a label for cc_toolchain_suite for second crosstool, and the build file for the project is:
cc_binary(
name = "generator",
srcs = [ "main.cc" ],
)
genrule(
name = "generate",
outs = ["generated.cc"],
cmd = "$(location :generator) > $#",
tools = [":generator"],
)
cc_binary(
name = "generated",
srcs = [ "generated.cc" ],
)
Then running:
bazel build --host_crosstool_top=//crosstool1:toolchain --crosstool_top=//crosstool2:toolchain :generated
will do exactly what you describe, it will use crosstool1 to build :generator, and crosstool2 to build generated. Genrules use host configuration by default, so all should just work.

Resources