Add Go Test binary to container_image using Bazel - docker

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"],
)

Related

How to fail a Bazel build on a rule failure?

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

How to add an external dependency into a Swift project using Bazel (without using Cocoapod)

The dependency I'm trying to integrate is TensorFlowLite:
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/experimental/swift
I'm already using Carthage and SwiftPackageManager to manage dependencies of an Xcode project.
Since TensorFlowLite supports Bazel, I tried to download, build, and link the dependency using the Bazel configuration.
I downloaded the Bazel iOS demo app from here:
https://github.com/bazelbuild/examples/tree/master/tutorial
And followed the instruction in this page:
https://docs.bazel.build/versions/master/tutorial/ios-app.html
When I try to build the demo iOS app using the following command:
bazel build //ios-app
I get the following error:
ERROR: /Users/nebil/Downloads/examples-master/tutorial/ios-app/BUILD:15:14:
no such package '#TensorFlowLite//tensorflow/lite/experimental/swift': BUILD file not found in directory 'tensorflow/lite/experimental/swift' of external repository #TensorFlowLite.
Add a BUILD file to a directory to mark it as a package. and referenced by '//ios-app:TensorFlowLite'
ERROR: Analysis of target '//ios-app:ios-app' failed; build aborted: Analysis failed
In the main WORKSPACE file I added the following dependency:
git_repository(
name = "TensorFlowLite",
remote = "https://github.com/tensorflow/tensorflow",
tag = "v2.2.0",
)
And the BUILD file of the ios_app package:
load("#build_bazel_rules_apple//apple:ios.bzl", "ios_application")
load("#build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = 'TensorFlowLite',
deps = [
"#TensorFlowLite//tensorflow/lite/experimental/swift:TensorFlowLite",
],
)
ios_application(
name = "ios-app",
bundle_id = "Google.UrlGet",
families = [
"iphone",
"ipad",
],
infoplists = [":UrlGet/UrlGet-Info.plist"],
launch_storyboard = "UrlGet/UrlGetViewController.xib",
minimum_os_version = "8.0",
visibility = ["//visibility:public"],
deps = [:TensorFlowLite"],
)
I'm using the same project structure of the same project and simply added a new dependency to the main app.
The build is failing because the BUILD file is missing from the third party code, but this is not true:
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/swift/BUILD.apple
I'm not an expert with Bazel but I don't see why this shouldn't be possibile. Any clarification would be much appreciated.
Thank you

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