David Chen | c4af828 | 2016-01-20 11:06:35 +0000 | [diff] [blame] | 1 | --- |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 2 | layout: documentation |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 3 | title: Build Tutorial - Android |
David Chen | c4af828 | 2016-01-20 11:06:35 +0000 | [diff] [blame] | 4 | --- |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 5 | |
ahumesky | 71c1441 | 2017-12-08 11:55:53 -0800 | [diff] [blame] | 6 | Introduction to Bazel: Building an Android App |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 7 | ========== |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 8 | |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 9 | In this tutorial, you will learn how to build a simple Android app. You'll do |
| 10 | the following: |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 11 | |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 12 | * [Set up your environment](#set-up-your-environment) |
| 13 | * [Install Bazel](#install-bazel) |
| 14 | * [Install Android Studio](#install-android-studio) |
| 15 | * [Get the sample project](#get-the-sample-project) |
| 16 | * [Set up a workspace](#set-up-a-workspace) |
| 17 | * [Create a WORKSPACE file](#create-a-workspace-file) |
| 18 | * [Update the WORKSPACE file](#update-the-workspace-file) |
| 19 | * [Review the source files](#review-the-source-files) |
| 20 | * [Create a BUILD file](#create-a-build-file) |
| 21 | * [Add an android_library rule](#add-an-android_library-rule) |
| 22 | * [Add an android_binary rule](#add_an-android_binary-rule) |
| 23 | * [Build the app](#build-the-app) |
| 24 | * [Find the build outputs](#find-the-build-outputs) |
| 25 | * [Run the app](#run-the-app) |
| 26 | * [Review your work](#review-your-work) |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 27 | |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 28 | ## Set up your environment |
| 29 | |
| 30 | To get started, install Bazel and Android Studio, and get the sample project. |
| 31 | |
| 32 | ### Install Bazel |
| 33 | |
| 34 | Follow the [installation instructions](../install.md) to install Bazel and |
| 35 | its dependencies. |
| 36 | |
| 37 | ### Install Android Studio |
| 38 | |
| 39 | Download and install Android Studio as described in [Install Android Studio](https://developer.android.com/sdk/index.html). |
| 40 | |
| 41 | The installer does not automatically set the `ANDROID_HOME` variable. |
| 42 | Set it to the location of the Android SDK, which defaults to `$HOME/Android/Sdk/` |
| 43 | . |
| 44 | |
| 45 | For example: |
| 46 | |
| 47 | `export ANDROID_HOME=$HOME/Android/Sdk/` |
| 48 | |
| 49 | For convenience, add the above statement to your `~/.bashrc` file. |
| 50 | |
| 51 | ### Get the sample project |
| 52 | |
| 53 | You also need to get the sample project for the tutorial from GitHub. The repo |
| 54 | has two branches: `source-only` and `master`. The `source-only` branch contains |
| 55 | the source files for the project only. You'll use the files in this branch in |
| 56 | this tutorial. The `master` branch contains both the source files and completed |
| 57 | Bazel `WORKSPACE` and `BUILD` files. You can use the files in this branch to |
| 58 | check your work when you've completed the tutorial steps. |
| 59 | |
| 60 | Enter the following at the command line to get the files in the `source-only` |
| 61 | branch: |
| 62 | |
| 63 | ```bash |
| 64 | cd $HOME |
| 65 | git clone -b source-only https://github.com/bazelbuild/examples |
| 66 | ``` |
| 67 | |
| 68 | The `git clone` command creates a directory named `$HOME/examples/`. This |
| 69 | directory contains several sample projects for Bazel. The project files for this |
| 70 | tutorial are in `$HOME/examples/tutorial/android`. |
| 71 | |
| 72 | ## Set up a workspace |
| 73 | |
| 74 | A [workspace](../build-ref.html#workspaces) is a directory that contains the |
| 75 | source files for one or more software projects, as well as a `WORKSPACE` file |
| 76 | and `BUILD` files that contain the instructions that Bazel uses to build |
| 77 | the software. The workspace may also contain symbolic links to output |
| 78 | directories. |
| 79 | |
| 80 | A workspace directory can be located anywhere on your filesystem and is denoted |
| 81 | by the presence of the `WORKSPACE` file at its root. In this tutorial, your |
| 82 | workspace directory is `$HOME/examples/tutorial/`, which contains the sample |
| 83 | project files you cloned from the GitHub repo in the previous step. |
| 84 | |
| 85 | Note that Bazel itself doesn't make any requirements about how you organize |
| 86 | source files in your workspace. The sample source files in this tutorial are |
| 87 | organized according to conventions for the target platform. |
| 88 | |
| 89 | For your convenience, set the `$WORKSPACE` environment variable now to refer to |
| 90 | your workspace directory. At the command line, enter: |
| 91 | |
| 92 | ```bash |
| 93 | export WORKSPACE=$HOME/examples/tutorial |
| 94 | ``` |
| 95 | |
| 96 | ### Create a WORKSPACE file |
| 97 | |
| 98 | Every workspace must have a text file named `WORKSPACE` located in the top-level |
| 99 | workspace directory. This file may be empty or it may contain references |
| 100 | to [external dependencies](../external.html) required to build the |
| 101 | software. |
| 102 | |
| 103 | For now, you'll create an empty `WORKSPACE` file, which simply serves to |
| 104 | identify the workspace directory. In later steps, you'll update the file to add |
| 105 | external dependency information. |
| 106 | |
| 107 | Enter the following at the command line: |
| 108 | |
| 109 | ```bash |
| 110 | touch $WORKSPACE/WORKSPACE |
| 111 | ``` |
| 112 | This creates the empty `WORKSPACE` file. |
| 113 | |
| 114 | ### Update the WORKSPACE file |
| 115 | |
| 116 | Bazel needs to run the Android SDK |
| 117 | [build tools](https://developer.android.com/tools/revisions/build-tools.html) |
| 118 | and uses the SDK libraries to build the app. This means that you need to add |
| 119 | some information to your `WORKSPACE` file so that Bazel knows where to find |
| 120 | them. Note that this step is not required when you build for other platforms. |
| 121 | For example, Bazel automatically detects the location of Java, C++ and |
| 122 | Objective-C compilers from settings in your environment. |
| 123 | |
| 124 | Add the following lines to your `WORKSPACE` file: |
| 125 | |
| 126 | ```python |
| 127 | android_sdk_repository( |
dannark | 5eb684f | 2017-09-06 02:29:30 +0200 | [diff] [blame] | 128 | name = "androidsdk" |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 129 | ) |
| 130 | ``` |
| 131 | |
| 132 | This will use the Android SDK referenced by the `ANDROID_HOME` environment |
dannark | 5eb684f | 2017-09-06 02:29:30 +0200 | [diff] [blame] | 133 | variable, and automatically detect the highest API level and the latest version |
| 134 | of build tools installed within that location. |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 135 | |
| 136 | Alternatively, you can explicitly specify the location of the Android |
dannark | 5eb684f | 2017-09-06 02:29:30 +0200 | [diff] [blame] | 137 | SDK, the API level, and the version of build tools to use by including the |
| 138 | `path`,`api_level`, and `build_tools_version` attributes. You can specify any |
| 139 | subset of these attributes: |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 140 | |
| 141 | ```python |
| 142 | android_sdk_repository( |
| 143 | name = "androidsdk", |
| 144 | path = "/path/to/Android/sdk", |
| 145 | api_level = 25, |
dannark | 5eb684f | 2017-09-06 02:29:30 +0200 | [diff] [blame] | 146 | build_tools_version = "26.0.1" |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 147 | ) |
| 148 | ``` |
| 149 | |
| 150 | **Optional:** This is not required by this tutorial, but if you want to compile |
| 151 | native code into your Android app, you also need to download the |
| 152 | [Android NDK](https://developer.android.com/ndk/downloads/index.html) and |
| 153 | tell Bazel where to find it by adding the following rule to your `WORKSPACE` |
| 154 | file: |
| 155 | |
| 156 | ```python |
| 157 | android_ndk_repository( |
dannark | 5eb684f | 2017-09-06 02:29:30 +0200 | [diff] [blame] | 158 | name = "androidndk" |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 159 | ) |
| 160 | ``` |
| 161 | |
| 162 | `api_level` is the version of the Android API the SDK and the NDK target |
dannark | 5eb684f | 2017-09-06 02:29:30 +0200 | [diff] [blame] | 163 | (for example, 23 for Android 6.0 and 25 for Android 7.1). If not explicitly |
| 164 | set, `api_level` will default to the highest available API level for |
| 165 | `android_sdk_repository` and `android_ndk_repository`. It's not necessary to |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 166 | set the API levels to the same value for the SDK and NDK. |
| 167 | [This web page](https://developer.android.com/ndk/guides/stable_apis.html) |
| 168 | contains a map from Android releases to NDK-supported API levels. |
| 169 | |
| 170 | Similar to `android_sdk_repository`, the path to the Android NDK is inferred |
| 171 | from the `ANDROID_NDK_HOME` environment variable by default. The path can also |
| 172 | be explicitly specified with a `path` attribute on `android_ndk_repository`. |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 173 | |
| 174 | ## Review the source files |
| 175 | |
| 176 | Let's take a look at the source files for the app. These are located in |
| 177 | `$WORKSPACE/android/`. |
| 178 | |
| 179 | The key files and directories are: |
| 180 | |
| 181 | <table class="table table-condensed table-striped"> |
| 182 | <thead> |
| 183 | <tr> |
| 184 | <td>Name</td> |
| 185 | <td>Location</td> |
| 186 | </tr> |
| 187 | </thead> |
| 188 | <tbody> |
| 189 | <tr> |
| 190 | <td>Manifest file</td> |
| 191 | <td><code>src/main/java/com/google/bazel/example/android/AndroidManifest.xml</code></td> |
| 192 | </tr> |
| 193 | <tr> |
| 194 | <td>Activity source file</td> |
| 195 | <td><code>src/main/java/com/google/bazel/example/android/activities/MainActivity.java</code></td> |
| 196 | </tr> |
| 197 | <tr> |
| 198 | <td>Resource file directory</td> |
| 199 | <td><code>src/main/java/com/google/bazel/example/android/res/</code></td> |
| 200 | </tr> |
| 201 | </tbody> |
| 202 | </table> |
| 203 | |
| 204 | Note that you're just looking at these files now to become familiar with the |
| 205 | structure of the app. You don't have to edit any of the source files to complete |
| 206 | this tutorial. |
| 207 | |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 208 | ## Create a BUILD file |
| 209 | |
dzc | 205125b | 2017-06-26 11:01:47 +0200 | [diff] [blame] | 210 | A [`BUILD` file](../build-ref.html#BUILD_files) is a text file that describes |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 211 | the relationship between a set of build outputs -- for example, compiled |
| 212 | software libraries or executables -- and their dependencies. These dependencies |
| 213 | may be source files in your workspace or other build outputs. `BUILD` files are |
| 214 | written in the Bazel *build language*. |
| 215 | |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 216 | `BUILD` files are part of a concept in Bazel known as the *package hierarchy*. |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 217 | The package hierarchy is a logical structure that overlays the directory |
dzc | 205125b | 2017-06-26 11:01:47 +0200 | [diff] [blame] | 218 | structure in your workspace. Each [package](../build-ref.html#packages) is a |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 219 | directory (and its subdirectories) that contains a related set of source files |
| 220 | and a `BUILD` file. The package also includes any subdirectories, excluding |
| 221 | those that contain their own `BUILD` file. The *package name* is the name of the |
| 222 | directory where the `BUILD` file is located. |
| 223 | |
| 224 | Note that this package hierarchy is distinct from, but coexists with, the Java |
| 225 | package hierarchy for your Android app. |
| 226 | |
| 227 | For the simple Android app in this tutorial, we'll consider all the source files |
| 228 | in `$WORKSPACE/android/` to comprise a single Bazel package. A more complex |
| 229 | project may have many nested packages. |
| 230 | |
| 231 | At a command-line prompt, open your new `BUILD` file for editing: |
| 232 | |
| 233 | ```bash |
| 234 | vi $WORKSPACE/android/BUILD |
| 235 | ``` |
| 236 | |
| 237 | ### Add an android_library rule |
| 238 | |
| 239 | A `BUILD` file contains several different types of instructions for Bazel. The |
dzc | 205125b | 2017-06-26 11:01:47 +0200 | [diff] [blame] | 240 | most important type is the [build rule](../build-ref.html#funcs), which tells |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 241 | Bazel how to build an intermediate or final software output from a set of source |
| 242 | files or other dependencies. |
| 243 | |
| 244 | Bazel provides two build rules, `android_library` and `android_binary`, that you |
| 245 | can use to build an Android app. For this tutorial, you'll first use the |
dzc | 205125b | 2017-06-26 11:01:47 +0200 | [diff] [blame] | 246 | [`android_library`](../be/android.html#android_library) rule to tell |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 247 | Bazel how to build an |
| 248 | [Android library module](http://developer.android.com/tools/projects/index.html#LibraryProjects) |
| 249 | from the app source code and resource files. Then you'll use the |
| 250 | `android_binary` rule to tell it how to build the Android application package. |
| 251 | |
| 252 | Add the following to your `BUILD` file: |
| 253 | |
| 254 | ```python |
| 255 | android_library( |
| 256 | name = "activities", |
| 257 | srcs = glob(["src/main/java/com/google/bazel/example/android/activities/*.java"]), |
| 258 | custom_package = "com.google.bazel.example.android.activities", |
| 259 | manifest = "src/main/java/com/google/bazel/example/android/activities/AndroidManifest.xml", |
| 260 | resource_files = glob(["src/main/java/com/google/bazel/example/android/activities/res/**"]), |
| 261 | ) |
| 262 | ``` |
| 263 | |
| 264 | As you can see, the `android_library` build rule contains a set of attributes |
| 265 | that specify the information that Bazel needs to build a library module from the |
| 266 | source files. Note also that the name of the rule is `activities`. You'll |
| 267 | reference the rule using this name as a dependency in the `android_binary` rule. |
| 268 | |
| 269 | ### Add an android_binary rule |
| 270 | |
dzc | 205125b | 2017-06-26 11:01:47 +0200 | [diff] [blame] | 271 | The [`android_binary`](../be/android.html#android_binary) rule builds |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 272 | the Android application package (`.apk` file) for your app. |
| 273 | |
| 274 | Add the following to your build file: |
| 275 | |
| 276 | ```python |
| 277 | android_binary( |
| 278 | name = "android", |
| 279 | custom_package = "com.google.bazel.example.android", |
| 280 | manifest = "src/main/java/com/google/bazel/example/android/AndroidManifest.xml", |
| 281 | resource_files = glob(["src/main/java/com/google/bazel/example/android/res/**"]), |
| 282 | deps = [":activities"], |
| 283 | ) |
| 284 | ``` |
| 285 | |
| 286 | Here, the `deps` attribute references the output of the `activities` rule you |
| 287 | added to the `BUILD` file above. This means that, when Bazel builds the output |
| 288 | of this rule, it checks first to see if the output of the `activities` library |
| 289 | rule has been built and is up-to-date. If not, it builds it and then uses that |
| 290 | output to build the application package file. |
| 291 | |
| 292 | Now, save and close the file. You can compare your `BUILD` file to the |
| 293 | [completed example](https://github.com/bazelbuild/examples/blob/master/tutorial/android/BUILD) |
| 294 | in the `master` branch of the GitHub repo. |
| 295 | |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 296 | ## Build the app |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 297 | |
| 298 | You use the |
dmarting | cdb8a63 | 2017-09-04 11:43:35 +0200 | [diff] [blame] | 299 | [`bazel`](../user-manual.html) command-line tool to run builds, execute |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 300 | unit tests and perform other operations in Bazel. This tool is located in the |
| 301 | `output` subdirectory of the location where you installed Bazel. During |
dzc | 205125b | 2017-06-26 11:01:47 +0200 | [diff] [blame] | 302 | [installation](../install.md), you probably added this location to your |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 303 | path. |
| 304 | |
| 305 | Before you build the sample app, make sure that your current working directory |
| 306 | is inside your Bazel workspace: |
| 307 | |
| 308 | ```bash |
| 309 | cd $WORKSPACE |
| 310 | ``` |
| 311 | |
| 312 | Now, enter the following to build the sample app: |
| 313 | |
| 314 | ```bash |
| 315 | bazel build //android:android |
| 316 | ``` |
| 317 | |
Jingwen Chen | 7633ab0 | 2018-03-05 09:30:14 -0800 | [diff] [blame] | 318 | The [`build`](../user-manual.html#build) subcommand instructs Bazel to |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 319 | build the target that follows. The target is specified as the name of a build |
| 320 | rule inside a `BUILD` file, with along with the package path relative to |
| 321 | your workspace directory. Note that you can sometimes omit the package path |
| 322 | or target name, depending on your current working directory at the command |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 323 | line and the name of the target. See [Labels](../build-ref.html#labels) in the |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 324 | *Bazel Concepts and Terminology* page for more information about target labels |
| 325 | and paths. |
| 326 | |
| 327 | Bazel now launches and builds the sample app. During the build process, its |
| 328 | output will appear similar to the following: |
| 329 | |
| 330 | ```bash |
| 331 | INFO: Found 1 target... |
| 332 | Target //android:android up-to-date: |
| 333 | bazel-bin/android/android_deploy.jar |
| 334 | bazel-bin/android/android_unsigned.apk |
| 335 | bazel-bin/android/android.apk |
| 336 | INFO: Elapsed time: 7.237s, Critical Path: 5.81s |
| 337 | ``` |
| 338 | |
| 339 | ## Find the build outputs |
| 340 | |
| 341 | Bazel stores the outputs of both intermediate and final build operations in |
| 342 | a set of per-user, per-workspace output directories. These directories are |
| 343 | symlinked from the following locations: |
| 344 | |
| 345 | * `$WORKSPACE/bazel-bin`, which stores binary executables and other runnable |
| 346 | build outputs |
| 347 | * `$WORKSPACE/bazel-genfiles`, which stores intermediary source files that are |
| 348 | generated by Bazel rules |
| 349 | * `$WORKSPACE/bazel-out`, which stores other types of build outputs |
| 350 | |
| 351 | Bazel stores the Android `.apk` file generated using the `android_binary` rule |
| 352 | in the `bazel-bin/android/` directory, where the subdirectory name `android` is |
| 353 | derived from the name of the Bazel package. |
| 354 | |
| 355 | At a command prompt, list the contents of this directory and find the |
| 356 | `android.apk` file: |
| 357 | |
| 358 | ```bash |
| 359 | ls $WORKSPACE/bazel-bin/android |
| 360 | ``` |
| 361 | |
| 362 | ## Run the app |
| 363 | |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 364 | **NOTE:** The app launches standalone but requires a backend server in order to |
| 365 | produce output. See the README file in the sample project directory to find out |
| 366 | how to build the backend server. |
| 367 | |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 368 | You can now deploy the app to a connected Android device or emulator from the |
| 369 | command line using the |
Jingwen Chen | 7633ab0 | 2018-03-05 09:30:14 -0800 | [diff] [blame] | 370 | [`bazel mobile-install`](../user-manual.html#mobile-install) |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 371 | command. This command uses the Android Debug Bridge (`adb`) to communicate with |
| 372 | the device. You must set up your device to use `adb` following the instructions |
| 373 | in |
| 374 | [Android Debug Bridge](http://developer.android.com/tools/help/adb.html) before |
Googler | b343cda | 2017-06-07 14:45:17 -0400 | [diff] [blame] | 375 | deployment. You can also choose to install the app on the Android emulator |
| 376 | included in Android Studio. Make sure the emulator is running before executing |
| 377 | the command below. |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 378 | |
| 379 | Enter the following: |
| 380 | |
| 381 | ```bash |
| 382 | bazel mobile-install //android:android |
| 383 | ``` |
| 384 | |
| 385 | Note that the `mobile-install` subcommand also supports the |
Jingwen Chen | 7633ab0 | 2018-03-05 09:30:14 -0800 | [diff] [blame] | 386 | [`--incremental`](../user-manual.html#mobile-install) |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 387 | flag that can be used to deploy only those parts of the app that have changed |
| 388 | since the last deployment. |
| 389 | |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 390 | ## Review your work |
dzc | 22b85a2 | 2017-05-31 20:37:50 +0200 | [diff] [blame] | 391 | |
Googler | 3d70c31 | 2017-08-09 00:23:38 +0200 | [diff] [blame] | 392 | In this tutorial, you used Bazel to build an Android app. To accomplish that, |
| 393 | you: |
| 394 | |
| 395 | * Set up your environment by installing Bazel and Android Studio, and |
| 396 | downloading the sample project |
| 397 | * Set up a Bazel [workspace](workspace.md) that contained the source code |
| 398 | for the app and a `WORKSPACE` file that identifies the top level of the |
| 399 | workspace directory |
| 400 | * Updated the `WORKSPACE` file to contain references to the required |
| 401 | external dependencies |
| 402 | * Created a `BUILD` file |
| 403 | * Ran Bazel to build the app |
| 404 | * Deployed and ran the app on an Android emulator and device |
| 405 | |
| 406 | The built app is located in the `$WORKSPACE/bazel-bin` directory. |
| 407 | |
| 408 | Note that completed `WORKSPACE` and `BUILD` files for this tutorial are located |
| 409 | in the |
| 410 | [master branch](https://github.com/bazelbuild/examples/tree/master/tutorial) |
| 411 | of the GitHub repo. You can compare your work to the completed files for |
| 412 | additional help or troubleshooting. |