Skip to content

Commit

Permalink
cmd/autogazelle: bazel wrapper for automatically updating build files (
Browse files Browse the repository at this point in the history
  • Loading branch information
jayconrod committed Oct 9, 2018
1 parent bc107b5 commit 9a2880e
Show file tree
Hide file tree
Showing 278 changed files with 184,856 additions and 0 deletions.
45 changes: 45 additions & 0 deletions cmd/autogazelle/BUILD.bazel
@@ -0,0 +1,45 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "go_default_library",
srcs = [
"autogazelle.go",
"client_unix.go",
"server_unix.go",
],
importpath = "github.com/bazelbuild/bazel-gazelle/cmd/autogazelle",
visibility = ["//visibility:private"],
deps = select({
"@io_bazel_rules_go//go/platform:darwin": [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
],
"@io_bazel_rules_go//go/platform:linux": [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
],
"@io_bazel_rules_go//go/platform:nacl": [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
],
"@io_bazel_rules_go//go/platform:solaris": [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
],
"//conditions:default": [],
}),
)

go_binary(
name = "autogazelle",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
144 changes: 144 additions & 0 deletions cmd/autogazelle/README.rst
@@ -0,0 +1,144 @@
Autogazelle: fast, automatic build file generation
==================================================

.. _cmd/autogazelle/autogazelle.bash: autogazelle.bash

Autogazelle is a wrapper program that runs Gazelle as part of each
Bazel command. You can add, delete, or rename source files, add imports, then
just ``bazel build``. Your build files are updated automatically during the
build.

*Autogazelle is highly experimental and may change significantly in the future.
Use with caution. See* `Limitations`_ *below.*

Setting up autogazelle
----------------------

Before you begin
~~~~~~~~~~~~~~~~

Make sure the ``bazel_gazelle`` repository is declared in your ``WORKSPACE``.
It should look the the snippet below, but the versions will likely be different.
It may be possible to used Autogazelle from a vendored ``bazel_gazelle``
repository, but this is not directly supported yet.

.. code:: bzl
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "io_bazel_rules_go",
urls = ["https://github.com/bazelbuild/rules_go/releases/download/0.15.4/rules_go-0.15.4.tar.gz"],
sha256 = "7519e9e1c716ae3c05bd2d984a42c3b02e690c5df728dc0a84b23f90c355c5a1",
)
http_archive(
name = "bazel_gazelle",
urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/0.14.0/bazel-gazelle-0.14.0.tar.gz"],
sha256 = "c0a5739d12c6d05b6c1ad56f2200cb0b57c5a70e03ebd2f7b87ce88cabf09c7b",
)
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
go_rules_dependencies()
go_register_toolchains()
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
gazelle_dependencies()
Make sure you have a ``gazelle`` rule declared in your root build file.
It should look this this, probably with different options.

.. code:: bzl
load("@bazel_gazelle//:def.bzl", "gazelle")
# gazelle:prefix github.com/example/project
gazelle(name = "gazelle")
Installing the wrapper
~~~~~~~~~~~~~~~~~~~~~~

In order to run autogazelle as part of each Bazel command, you'll need to
copy `cmd/autogazelle/autogazelle.bash`_ to ``tools/bazel`` within your
repository. Make sure the script is executable. The ``bazel`` script in
your ``PATH`` will execute this script instead of the real ``bazel`` binary
if it's present and executable.

Deleting build files
~~~~~~~~~~~~~~~~~~~~

If your build files are completely generated by Gazelle, you may want to
delete them from source control, since they can be easily regenerated.
When Gazelle generates new files, it names them ``BUILD.bazel`` by default,
so you can add ``BUILD.bazel`` to your ``.gitignore`` file after deleting
them.

It's likely that you'll have some build files with manual modifications
you want to keep around. At minimum, you'll need to keep the root build
file since it contains the ``gazelle`` rule. The simplest way to keep these
files is to name the ``BUILD`` instead of ``BUILD.bazel``. Bazel and Gazelle
will both recognize files named ``BUILD``, and Git will not ignore them.

Another option is to name your files ``BUILD.bazel.in`` or ``BUILD.in``.
Autogazelle will copy these files to ``BUILD.bazel`` or ``BUILD`` when it
starts running before invoking Gazelle.

How autogazelle works
---------------------

Autogazelle has three components: a wrapper script, a client, and a server.

The *wrapper script* is a bash script installed in ``tools/bazel`` in workspaces
that use autogazelle. The ``bazel`` command installed in your ``PATH`` will look
for an executable file at this location and will execute it instead of the real
bazel. The script builds and runs the *autogazelle client* using ``bazel run``
before invoking the real bazel binary with the original command-line arguments.

The *client* is a Go program that attempts to connect to the *server*
over a UNIX domain socket. If the server isn't running, the client will
start it and connect. Once connected, the client will wait for the server
to disconnect before exiting. The client does no other work.

The *server* is a Go program (actually the same binary as the client, started
with different options) that listens for connections on a UNIX domain socket.
When it accepts a connection, it runs Gazelle using ``bazel run``, then closes
the connection. The socket is just used to make the client wait until Gazelle
completes; no information is exchanged, other than log messages. While the
server is waiting for a connection, it watches the file system for changes that
could affect build files. When the server runs Gazelle, it runs only in
directories that have changed. This makes Gazelle run much faster. The server
exits after being idle for an hour.

Limitations
-----------

Dependency resolution
~~~~~~~~~~~~~~~~~~~~~

Autogazelle tries to run Gazelle quickly by only updating certain
directories. To support this, it runs Gazelle with the flags ``-r=false``
(don't recurse through selected directories) and ``-index=false`` (don't
build an index of library targets for dependency resolution). This means
that you'll need to set ``external = "vendored"`` explicitly on your ``gazelle``
rule if you have a vendor directory, and you'll need to add
``# gazelle:resolve`` directives in your root build files for any imports
that should be resolved to custom names.

Platform support
~~~~~~~~~~~~~~~~

Autogazelle uses UNIX-domain sockets to synchronize the client and server. This
only works on UNIX-like platforms; these sockets are not supported on Windows.

Autogazelle uses ``github.com/fsnotify/fsnotify`` to watch the file system. This
library works on multiple platforms, but it won't work on file systems that
don't support watches (e.g., NFS and most other network file systems).

Autogazelle has only been tested on Linux. It is intended to work on macOS, but
this has not been tested yet.

Credits
-------

The original idea for running Gazelle automatically was proposed by Matthew
Moore (@mattmoor). The initial plan was to run Gazelle in a repository rule
which regenerate a repository full of build files on each run.

Erick Fejta (@fejta) prototyped this idea for Kubernetes. You can find the
prototype at https://github.com/kubernetes/test-infra/tree/master/autogo.
33 changes: 33 additions & 0 deletions cmd/autogazelle/autogazelle.bash
@@ -0,0 +1,33 @@
#!/bin/bash

# Copyright 2017 The Bazel Authors. All rights reserved.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


# autogazelle.bash is a bazel wrapper script that runs gazelle automatically
# before running bazel commands. See autogazelle.go for details.
#
# This script may be installed at tools/bazel in your workspace. It must
# be executable.

set -euo pipefail

case "$1" in
build|coverage|cquery|fetch|mobile-install|print_action|query|run|test)
"$BAZEL_REAL" run @bazel_gazelle//cmd/autogazelle -- -gazelle=//:gazelle
echo "done running autogazelle" >&1
;;
esac

exec "$BAZEL_REAL" "$@"

0 comments on commit 9a2880e

Please sign in to comment.