Build status

App Engine Rules for Bazel

Overview

These build rules are used for building Java App Engine application or Python App Engine application with Bazel. It does not aim at general web application support but can be easily modified to handle a standard web application.

Setup

To be able to use the rules, you must make the App Engine SDK available to Bazel. The easiest way to do so is by adding the following to your WORKSPACE file:

Note: The ${LANG}_appengine_repository() lines are only needed for the languages you plan to use.

git_repository(
    name = "io_bazel_rules_appengine",
    remote = "https://github.com/bazelbuild/rules_appengine.git",
    # Check https://github.com/bazelbuild/rules_appengine/releases for the latest version.
    tag = "0.0.8",
)
# Java
load(
    "@io_bazel_rules_appengine//appengine:java_appengine.bzl",
    "java_appengine_repositories",
)

java_appengine_repositories()

# Python
load(
    "@io_bazel_rules_appengine//appengine:sdk.bzl",
    "appengine_repositories",
)

appengine_repositories()

load(
    "@io_bazel_rules_appengine//appengine:py_appengine.bzl",
    "py_appengine_repositories",
)

py_appengine_repositories()

The App Engine rules download the App Engine SDK, which is a few hundred megabytes in size. To avoid downloading this multiple times for multiple projects or inadvertently re-downloading it, you might want to add the following lines to your $HOME/.bazelrc file:

build --experimental_repository_cache=/home/user/.bazel/cache
fetch --experimental_repository_cache=/home/user/.bazel/cache

Requesting a specific App Engine SDK

All ${LANG}_appengine_repository macros accept optional arguments version and sha256.

py_appengine_repositories(
    version = '1.9.67',
    sha256 = 'f9f45150643424cb164185d9134b86511c2bec3001499247ef9027f1605ef8a3',
)

Using a predownloaded SDK version

You can, optionally, specify the environment variable ${LANG}_APPENGINE_SDK_PATH to use an SDK that is unzipped on your filesystem (instead of downloading a new one).

PY_APPENGINE_SDK_PATH=/path/to/google_appengine bazel build //whatever
JAVA_APPENGINE_SDK_PATH=/path/to/appengine-java-sdk-1.9.50 bazel build //whatever

Basic Java Example

Suppose you have the following directory structure for a simple App Engine application:

[workspace]/
    WORKSPACE
    hello_app/
        BUILD
        java/my/webapp/
            TestServlet.java
        webapp/
            index.html
        webapp/WEB-INF
            web.xml
            appengine-web.xml

BUILD definition

Then, to build your webapp, your hello_app/BUILD can look like:

load("@io_bazel_rules_appengine//appengine:java_appengine.bzl", "appengine_war")

java_library(
    name = "mylib",
    srcs = ["java/my/webapp/TestServlet.java"],
    deps = [
        "//external:appengine/java/api",
        "@io_bazel_rules_appengine//appengine:javax.servlet.api",
    ],
)

appengine_war(
    name = "myapp",
    jars = [":mylib"],
    data = glob(["webapp/**"]),
    data_path = "/webapp",
)

For simplicity, you can use the java_war rule to build an app from source. Your hello_app/BUILD file would then look like:

load("@io_bazel_rules_appengine//appengine:java_appengine.bzl", "java_war")

java_war(
    name = "myapp",
    srcs = ["java/my/webapp/TestServlet.java"],
    data = glob(["webapp/**"]),
    data_path = "/webapp",
    deps = [
        "//external:appengine/java/api",
        "@io_bazel_rules_appengine//appengine:javax.servlet.api",
    ],
)

You can then build the application with bazel build //hello_app:myapp.

Run on a local server

You can run it in a development server with bazel run //hello_app:myapp. This will bind a test server on port 8080. If you wish to select another port, use the --port option:

bazel run //hello_app:myapp -- --port=12345

You can see other options with -- --help (the -- tells Bazel to pass the rest of the arguments to the executable).

Deploy to Google App Engine

Another target //hello_app:myapp.deploy allows you to deploy your application to App Engine. It takes an optional argument: the APP_ID. If not specified, it uses the default APP_ID provided in the application. This target needs to open a browser to authenticate with App Engine, then have you copy-paste a "key" from the browser in the terminal. Since Bazel closes standard input, you can only input this by building the target and then running:

$ bazel-bin/hello_app/myapp.deploy APP_ID

After the first launch, subsequent launch will be registered to App Engine so you can just do a normal bazel run //hello_app:myapp.deploy -- APP_ID to deploy next versions of your application.

Java specific details

Note: App Engine uses Java 7. If you are using a more recent version of Java, you will get the following error message when you try to deploy:

java.lang.IllegalArgumentException: Class file is Java 8 but max supported is Java 7

To build with Java 7, use the toolchain bundled with these App Engine rules:

$ bazel build --java_toolchain=@io_bazel_rules_appengine//appengine:jdk7 //my-project

To avoid having to specify this toolchain during every build, you can add this to your project's .bazelrc. Create a .bazelrc file in the root directory of your project and add the line:

build --java_toolchain=@io_bazel_rules_appengine//appengine:jdk7

appengine_war

appengine_war(name, jars, data, data_path)
Attributes
name Name, required

A unique name for this rule.

jars List of labels, required

List of JAR files that will be uncompressed as the code for the Web Application.

If it is a `java_library` or a `java_import`, the JAR from the runtime classpath will be added in the `lib` directory of the Web Application.

data List of files, optional

List of files used by the Web Application at runtime.

This attribute can be used to specify the list of resources to be included into the WAR file.

data_path String, optional

Root path of the data.

The directory structure from the data is preserved inside the WebApplication but a prefix path determined by `data_path` is removed from the the directory structure. This path can be absolute from the workspace root if starting with a `/` or relative to the rule's directory. It is set to `.` by default.

java_war

java_war(name, data, data_path, **kwargs)
Attributes
name Name, required

A unique name for this rule.

data List of labels, optional

List of files used by the Web Application at runtime.

Passed to the appengine_war rule.

data_path String, optional

Root path of the data.

Passed to the appengine_war rule.

**kwargs see java_library

The other arguments of this rule will be passed to build a `java_library` that will be passed in the `jar` arguments of a appengine_war rule.

Python specific details

py_appengine_binary

py_appengine_binary(name, srcs, configs, deps=[], data=[], overwrite_appengine_config=True)
Attributes
name Name, required

A unique name for this rule.

configs List of labels, required

the path to your app.yaml/index.yaml/cron.yaml files

srcs List of labels, optional

The list of source files that are processed to create the target.

deps List of labels, optional

The list of libraries to link into this library.

data List of labels, optional

List of files used by the Web Application at runtime.

overwrite_appengine_config Boolean, optional

If true, patch the user's appengine_config into the base one. If false, use the user specified config directly. Set to False to behave pre 0.0.8.

py_appengine_test

py_appengine_test(name, srcs, deps=[], data=[], libraries={})
Attributes
name Name, required

A unique name for this rule.

srcs List of labels, required

The list of source files that are processed to create the target.

deps List of labels, optional

The list of libraries to link into this library.

data List of labels, optional

List of files used by the Web Application at runtime.

libraries dict, optional

dictionary of name and the corresponding version for third-party libraries required from sdk.