5 minute guide to Bazel, Part 2: Command lines and tools
The aim of this guide is to get you up and running with Bazel as fast as possible. The steps will assume you have Bazel installed.
This part will show how to run a command line using genrule
. This rule is the generic way to specify sources, a tool (like a shell script), a command line, and the outputs. You can think of it as a way to define a function in your BUILD
file with the following signature:
genrule :: (name, sources, tool, command) -> output
In this example, we want to create a C source file, copy it using cp
, and run sed
on it with a shell script, and build an executable from the result.
Let’s get started in an empty directory called dir
.
- Create an empty
WORKSPACE
file.
dir $ touch WORKSPACE
- Create a file called
main.c
and write some C in it.
// dir/main.c
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello Blaze.\n");
return 0;
}
- Write a
BUILD
file with thegenrule
to copymain.c
.
# dir/BUILD
genrule(
name = "copy_of_main",
srcs = ["main.c"],
outs = ["copy_of_main.c"],
cmd = "cp $< $@",
)
$<
expands to the location of main.c
, and $@
expands to the location of copy_of_main.c
. See the full list of supported variables here.
- Let’s build this target,
//:copy_of_main
.
dir $ bazel build //:copy_of_main
....................
INFO: Analysed target //:copy_of_main (7 packages loaded).
INFO: Found 1 target...
Target //:copy_of_main up-to-date:
bazel-genfiles/copy_of_main.c
INFO: Elapsed time: 10.323s, Critical Path: 0.08s
INFO: Build completed successfully, 2 total actions
dir $ cat bazel-genfiles/copy_of_main.c
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello Blaze.\n");
return 0;
}
The file is copied successfully!
- Use the
tool
attribute to specify a separate tool to run in thecmd
string.
We want to substitute the word “Blaze” with “Bazel” in the source code, because that’s the name the build system was open sourced with. Let’s write the genrule
for that:
# dir/BUILD
# ...
genrule(
name = "renamed_main",
srcs = ["copy_of_main.c"],
outs = ["renamed_main.c"],
tools = ["substitute.sh"],
cmd = "$(location substitute.sh) 'Blaze' 'Bazel' $< $@",
)
location
is Bazel’s helper function to resolve the location of the tool when this command is executed.
Then, create a file substitute.sh
that calls out to sed
:
#!/bin/bash
sed "s/$1/$2/" $3 > $4
Don’t forget to make it executable with chmod u+x substitute.sh
.
- Build the target
//:renamed_main
.
dir $ bazel build :renamed_main
INFO: Analysed target //:renamed_main (0 packages loaded).
INFO: Found 1 target...
Target //:renamed_main up-to-date:
bazel-genfiles/renamed_main.c
INFO: Elapsed time: 0.261s, Critical Path: 0.07s
INFO: Build completed successfully, 2 total actions
dir $ cat bazel-genfiles/renamed_main.c
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello Bazel.\n");
return 0;
}
We are now using the correct name.
- To wrap it all up, let’s use
cc_binary
from Part 1.
# dir/BUILD
cc_binary(
name = "hello_bazel",
srcs = [":renamed_main"],
)
dir $ bazel run //:hello_bazel
INFO: Analysed target //:hello_bazel (3 packages loaded).
INFO: Found 1 target...
Target //:hello_bazel up-to-date:
bazel-bin/hello_bazel
INFO: Elapsed time: 5.817s, Critical Path: 0.52s
INFO: Build completed successfully, 5 total actions
INFO: Running command line: bazel-bin/hello_bazel
Hello Bazel.
This is how we can use genrule
to preprocess files before passing them in to other rules. It’s a simple and flexible way to create pipelines using Bazel.