| Project: /_project.yaml |
| Book: /_book.yaml |
| |
| {# disableFinding(LINE_OVER_80_LINK) #} |
| {# disableFinding("below") #} |
| |
| # Bazel Tutorial: Build a Go Project |
| |
| {% include "_buttons.html" %} |
| |
| This tutorial introduces you to the basics of Bazel by showing you how to build |
| a Go (Golang) project. You'll learn how to set up your workspace, build a small |
| program, import a library, and run its test. Along the way, you'll learn key |
| Bazel concepts, such as targets and `BUILD` files. |
| |
| Estimated completion time: 30 minutes |
| |
| ## Before you begin {:#you-begin} |
| |
| ### Install Bazel {:#install-bazel} |
| |
| Before you get started, first [install bazel](/install) if you haven't done so |
| already. |
| |
| You can check if Bazel is installed by running `bazel version` in any directory. |
| |
| ### Install Go (optional) {:#install-go} |
| |
| You don't need to [install Go](https://go.dev/doc/install) to build Go projects |
| with Bazel. The Bazel Go rule set automatically downloads and uses a Go |
| toolchain instead of using the toolchain installed on your machine. This ensures |
| all developers on a project build with same version of Go. |
| |
| However, you may still want to install a Go toolchain to run commands like `go |
| get` and `go mod tidy`. |
| |
| You can check if Go is installed by running `go version` in any directory. |
| |
| ### Get the sample project {:#get-sample} |
| |
| The Bazel examples are stored in a Git repository, so you'll need to [install |
| Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) if you |
| haven't already. To download the examples repository, run this command: |
| |
| ```posix-terminal |
| git clone https://github.com/bazelbuild/examples |
| ``` |
| |
| The sample project for this tutorial is in the `examples/go-tutorial` directory. |
| See what it contains: |
| |
| ```none |
| go-tutorial/ |
| └── stage1 |
| └── stage2 |
| └── stage3 |
| ``` |
| |
| There are three subdirectories (`stage1`, `stage2`, and `stage3`), each for a |
| different section of this tutorial. Each stage builds on the previous one. |
| |
| ## Build with Bazel {:#build-bazel} |
| |
| Start in the `stage1` directory, where we'll find a program. We can |
| build it with `bazel build`, then run it: |
| |
| ```posix-shell |
| $ cd go-tutorial/stage1/ |
| $ bazel build //:hello |
| INFO: Analyzed target //:hello (0 packages loaded, 0 targets configured). |
| INFO: Found 1 target... |
| Target //:hello up-to-date: |
| bazel-bin/hello_/hello |
| INFO: Elapsed time: 0.473s, Critical Path: 0.25s |
| INFO: 3 processes: 1 internal, 2 darwin-sandbox. |
| INFO: Build completed successfully, 3 total actions |
| |
| $ bazel-bin/hello_/hello |
| Hello, Bazel! 💚 |
| ``` |
| |
| We can also build run the program with a single `bazel run` command: |
| |
| ```posix-shell |
| $ bazel run //:hello |
| bazel run //:hello |
| INFO: Analyzed target //:hello (0 packages loaded, 0 targets configured). |
| INFO: Found 1 target... |
| Target //:hello up-to-date: |
| bazel-bin/hello_/hello |
| INFO: Elapsed time: 0.128s, Critical Path: 0.00s |
| INFO: 1 process: 1 internal. |
| INFO: Build completed successfully, 1 total action |
| INFO: Running command line: bazel-bin/hello_/hello |
| Hello, Bazel! 💚 |
| ``` |
| |
| ### Understanding project structure {:#understanding-project} |
| |
| Take a look at the project we just built. |
| |
| `hello.go` contains the Go source code for the program. |
| |
| ```go |
| package main |
| |
| import "fmt" |
| |
| func main() { |
| fmt.Println("Hello, Bazel! 💚") |
| } |
| ``` |
| |
| `BUILD` contains some instructions for Bazel, telling it what we want to build. |
| You'll typically write a file like this in each directory. For this project, we |
| have a single `go_binary` target that builds our program from `hello.go`. |
| |
| ```bazel |
| load("@rules_go//go:def.bzl", "go_binary") |
| |
| go_binary( |
| name = "hello", |
| srcs = ["hello.go"], |
| ) |
| ``` |
| |
| `MODULE.bazel` tracks your project's dependencies. It also marks your project's |
| root directory, so you'll only write one `MODULE.bazel` file per project. It |
| serves a similar purpose to Go's `go.mod` file. You don't actually need a |
| `go.mod` file in a Bazel project, but it may still be useful to have one so that |
| you can continue using `go get` and `go mod tidy` for dependency management. The |
| Bazel Go rule set can import dependencies from `go.mod`, but we'll cover that in |
| another tutorial. |
| |
| Our `MODULE.bazel` file contains a single dependency on |
| [rules_go](https://github.com/bazel-contrib/rules_go), the Go rule set. We need |
| this dependency because Bazel doesn't have built-in support for Go. |
| |
| ```bazel |
| bazel_dep( |
| name = "rules_go", |
| version = "0.50.1", |
| ) |
| ``` |
| |
| Finally, `MODULE.bazel.lock` is a file generated by Bazel that contains hashes |
| and other metadata about our dependencies. It includes implicit dependencies |
| added by Bazel itself, so it's quite long, and we won't show it here. Just like |
| `go.sum`, you should commit your `MODULE.bazel.lock` file to source control to |
| ensure everyone on your project gets the same version of each dependency. You |
| shouldn't need to edit `MODULE.bazel.lock` manually. |
| |
| ### Understand the BUILD file {:#understand-build} |
| |
| Most of your interaction with Bazel will be through `BUILD` files (or |
| equivalently, `BUILD.bazel` files), so it's important to understand what they |
| do. |
| |
| `BUILD` files are written in a scripting language called |
| [Starlark](https://bazel.build/rules/language), a limited subset of Python. |
| |
| A `BUILD` file contains a list of |
| [targets](https://bazel.build/reference/glossary#target). A target is something |
| Bazel can build, like a binary, library, or test. |
| |
| A target calls a rule function with a list of |
| [attributes](https://bazel.build/reference/glossary#attribute) to describe what |
| should be built. Our example has two attributes: `name` identifies the target on |
| the command line, and `srcs` is a list of source file paths (slash-separated, |
| relative to the directory containing the `BUILD` file). |
| |
| A [rule](https://bazel.build/reference/glossary#rule) tells Bazel how to build a |
| target. In our example, we used the |
| [`go_binary`](https://github.com/bazel-contrib/rules_go/blob/master/docs/go/core/rules.md#go_binary) |
| rule. Each rule defines [actions](https://bazel.build/reference/glossary#action) |
| (commands) that generate a set of output files. For example, `go_binary` defines |
| Go compile and link actions that produce an executable output file. |
| |
| Bazel has built-in rules for a few languages like Java and C++. You can find |
| their [documentation in the Build |
| Encyclopedia](https://bazel.build/reference/be/overview#rules). You can find |
| rule sets for many other languages and tools on the [Bazel Central Registry |
| (BCR)](https://registry.bazel.build/). |
| |
| ## Add a library {:#add-library} |
| |
| Move onto the `stage2` directory, where we'll build a new program that |
| prints your fortune. This program uses a separate Go package as a library that |
| selects a fortune from a predefined list of messages. |
| |
| ```none |
| go-tutorial/stage2 |
| ├── BUILD |
| ├── MODULE.bazel |
| ├── MODULE.bazel.lock |
| ├── fortune |
| │ ├── BUILD |
| │ └── fortune.go |
| └── print_fortune.go |
| ``` |
| |
| `fortune.go` is the source file for the library. The `fortune` library is a |
| separate Go package, so its source files are in a separate directory. Bazel |
| doesn't require you to keep Go packages in separate directories, but it's a |
| strong convention in the Go ecosystem, and following it will help you stay |
| compatible with other Go tools. |
| |
| ```go |
| package fortune |
| |
| import "math/rand" |
| |
| var fortunes = []string{ |
| "Your build will complete quickly.", |
| "Your dependencies will be free of bugs.", |
| "Your tests will pass.", |
| } |
| |
| func Get() string { |
| return fortunes[rand.Intn(len(fortunes))] |
| } |
| ``` |
| |
| The `fortune` directory has its own `BUILD` file that tells Bazel how to build |
| this package. We use `go_library` here instead of `go_binary`. |
| |
| We also need to set the `importpath` attribute to a string with which the |
| library can be imported into other Go source files. This name should be the |
| repository path (or module path) concatenated with the directory within the |
| repository. |
| |
| Finally, we need to set the `visibility` attribute to `["//visibility:public"]`. |
| [`visibility`](https://bazel.build/concepts/visibility) may be set on any |
| target. It determines which Bazel packages may depend on this target. In our |
| case, we want any target to be able to depend on this library, so we use the |
| special value `//visibility:public`. |
| |
| ```bazel |
| load("@rules_go//go:def.bzl", "go_library") |
| |
| go_library( |
| name = "fortune", |
| srcs = ["fortune.go"], |
| importpath = "github.com/bazelbuild/examples/go-tutorial/stage2/fortune", |
| visibility = ["//visibility:public"], |
| ) |
| ``` |
| |
| You can build this library with: |
| |
| ```posix-shell |
| $ bazel build //fortune |
| ``` |
| |
| Next, see how `print_fortune.go` uses this package. |
| |
| ```go |
| package main |
| |
| import ( |
| "fmt" |
| |
| "github.com/bazelbuild/examples/go-tutorial/stage2/fortune" |
| ) |
| |
| func main() { |
| fmt.Println(fortune.Get()) |
| } |
| ``` |
| |
| `print_fortune.go` imports the package using the same string declared in the |
| `importpath` attribute of the `fortune` library. |
| |
| We also need to declare this dependency to Bazel. Here's the `BUILD` file in the |
| `stage2` directory. |
| |
| ```bazel |
| load("@rules_go//go:def.bzl", "go_binary") |
| |
| go_binary( |
| name = "print_fortune", |
| srcs = ["print_fortune.go"], |
| deps = ["//fortune"], |
| ) |
| ``` |
| |
| You can run this with the command below. |
| |
| ```posix-shell |
| bazel run //:print_fortune |
| ``` |
| |
| The `print_fortune` target has a `deps` attribute, a list of other targets that |
| it depends on. It contains `"//fortune"`, a label string referring to the target |
| in the `fortune` directory named `fortune`. |
| |
| Bazel requires that all targets declare their dependencies explicitly with |
| attributes like `deps`. This may seem cumbersome since dependencies are *also* |
| specified in source files, but Bazel's explictness gives it an advantage. Bazel |
| builds an [action graph](https://bazel.build/reference/glossary#action-graph) |
| containing all commands, inputs, and outputs before running any commands, |
| without reading any source files. Bazel can then cache action results or send |
| actions for [remote execution](https://bazel.build/remote/rbe) without built-in |
| language-specific logic. |
| |
| ### Understanding labels {:#understanding-labels} |
| |
| A [label](https://bazel.build/reference/glossary#label) is a string Bazel uses |
| to identify a target or a file. Labels are used in command line arguments and in |
| `BUILD` file attributes like `deps`. We've seen a few already, like `//fortune`, |
| `//:print-fortune`, and `@rules_go//go:def.bzl`. |
| |
| A label has three parts: a repository name, a package name, and a target (or |
| file) name. |
| |
| The repository name is written between `@` and `//` and is used to refer to a |
| target from a different Bazel module (for historical reasons, *module* and |
| *repository* are sometimes used synonymously). In the label, |
| `@rules_go//go:def.bzl`, the repository name is `rules_go`. The repository name |
| can be omitted when referring to targets in the same repository. |
| |
| The package name is written between `//` and `:` and is used to refer to a |
| target in from a different Bazel package. In the label `@rules_go//go:def.bzl`, |
| the package name is `go`. A Bazel |
| [package](https://bazel.build/reference/glossary#package) is a set of files and |
| targets defined by a `BUILD` or `BUILD.bazel` file in its top-level directory. |
| Its package name is a slash-separated path from the module root directory |
| (containing `MODULE.bazel`) to the directory containing the `BUILD` file. A |
| package may include subdirectories, but only if they don't also contain `BUILD` |
| files defining their own packages. |
| |
| Most Go projects have one `BUILD` file per directory and one Go package per |
| `BUILD` file. The package name in a label may be omitted when referring to |
| targets in the same directory. |
| |
| The target name is written after `:` and refers to a target within a package. |
| The target name may be omitted if it's the same as the last component of the |
| package name (so `//a/b/c:c` is the same as `//a/b/c`; `//fortune:fortune` is |
| the same as `//fortune`). |
| |
| On the command-line, you can use `...` as a wildcard to refer to all the targets |
| within a package. This is useful for building or testing all the targets in a |
| repository. |
| |
| ```posix-shell |
| # Build everything |
| $ bazel build //... |
| ``` |
| |
| ## Test your project {:#test-project} |
| |
| Next, move to the `stage3` directory, where we'll add a test. |
| |
| ```none |
| go-tutorial/stage3 |
| ├── BUILD |
| ├── MODULE.bazel |
| ├── MODULE.bazel.lock |
| ├── fortune |
| │ ├── BUILD |
| │ ├── fortune.go |
| │ └── fortune_test.go |
| └── print-fortune.go |
| ``` |
| |
| `fortune/fortune_test.go` is our new test source file. |
| |
| ```go |
| package fortune |
| |
| import ( |
| "slices" |
| "testing" |
| ) |
| |
| // TestGet checks that Get returns one of the strings from fortunes. |
| func TestGet(t *testing.T) { |
| msg := Get() |
| if i := slices.Index(fortunes, msg); i < 0 { |
| t.Errorf("Get returned %q, not one the expected messages", msg) |
| } |
| } |
| ``` |
| |
| This file uses the unexported `fortunes` variable, so it needs to be compiled |
| into the same Go package as `fortune.go`. Look at the `BUILD` file to see |
| how that works: |
| |
| ```bazel |
| load("@rules_go//go:def.bzl", "go_library", "go_test") |
| |
| go_library( |
| name = "fortune", |
| srcs = ["fortune.go"], |
| importpath = "github.com/bazelbuild/examples/go-tutorial/stage3/fortune", |
| visibility = ["//visibility:public"], |
| ) |
| |
| go_test( |
| name = "fortune_test", |
| srcs = ["fortune_test.go"], |
| embed = [":fortune"], |
| ) |
| ``` |
| |
| We have a new `fortune_test` target that uses the `go_test` rule to compile and |
| link a test executable. `go_test` needs to compile `fortune.go` and |
| `fortune_test.go` together with the same command, so we use the `embed` |
| attribute here to incorporate the attributes of the `fortune` target into |
| `fortune_test`. `embed` is most commonly used with `go_test` and `go_binary`, |
| but it also works with `go_library`, which is sometimes useful for generated |
| code. |
| |
| You may be wondering if the `embed` attribute is related to Go's |
| [`embed`](https://pkg.go.dev/embed) package, which is used to access data files |
| copied into an executable. This is an unfortunate name collision: rules_go's |
| `embed` attribute was introduced before Go's `embed` package. Instead, rules_go |
| uses the `embedsrcs` to list files that can be loaded with the `embed` package. |
| |
| Try running our test with `bazel test`: |
| |
| ```posix-shell |
| $ bazel test //fortune:fortune_test |
| INFO: Analyzed target //fortune:fortune_test (0 packages loaded, 0 targets configured). |
| INFO: Found 1 test target... |
| Target //fortune:fortune_test up-to-date: |
| bazel-bin/fortune/fortune_test_/fortune_test |
| INFO: Elapsed time: 0.168s, Critical Path: 0.00s |
| INFO: 1 process: 1 internal. |
| INFO: Build completed successfully, 1 total action |
| //fortune:fortune_test PASSED in 0.3s |
| |
| Executed 0 out of 1 test: 1 test passes. |
| There were tests whose specified size is too big. Use the --test_verbose_timeout_warnings command line option to see which ones these are. |
| ``` |
| |
| You can use the `...` wildcard to run all tests. Bazel will also build targets |
| that aren't tests, so this can catch compile errors even in packages that don't |
| have tests. |
| |
| ```posix-shell |
| $ bazel test //... |
| ``` |
| |
| ## Conclusion and further reading {:#conclusion-and} |
| |
| In this tutorial, we built and tested a small Go project with Bazel, and we |
| learned some core Bazel concepts along the way. |
| |
| - To get started building other applications with Bazel, see the tutorials for |
| [C++](/start/cpp), [Java](/start/java), [Android](/start/android-app), and |
| [iOS](/start/ios-app). |
| - You can also check the list of [recommended rules](/rules) for other |
| languages. |
| - For more information on Go, see the |
| [rules_go](https://github.com/bazel-contrib/rules_go) module, especially the |
| [Core Go |
| rules](https://github.com/bazel-contrib/rules_go/blob/master/docs/go/core/rules.md) |
| documentation. |
| - To learn more about working with Bazel modules outside your project, see |
| [external dependencies](/docs/external). In particular, for information on |
| how to depend on Go modules and toolchains through Bazel's module system, |
| see [Go with |
| bzlmod](https://github.com/bazel-contrib/rules_go/tree/master/docs/go/core/bzlmod.md). |