Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
load("@bazel_gazelle//:def.bzl", "gazelle")
load("@buildifier_prebuilt//:rules.bzl", "buildifier")
load("@pypi//:requirements.bzl", "all_whl_requirements")
load("@rules_python//python:pip.bzl", "compile_pip_requirements")
load("@rules_python_gazelle_plugin//manifest:defs.bzl", "gazelle_python_manifest")
load("@rules_python_gazelle_plugin//modules_mapping:def.bzl", "modules_mapping")

# Tell bazel to include some files
filegroup(
Expand Down Expand Up @@ -34,3 +39,90 @@ buildifier(
lint_mode = "fix",
mode = "fix",
)

######
# Gazelle
#
# Gazelle is an automatic BUILD(.bazel) file generator. Run via:
# bazel run //:gazelle
######

# Comments that start with "# gazelle:XYZ" are called *directives*. Some directives
# can and should be set here, in the same bazel package (BUILD file) that defines
# gazelle, while other directives (such as "# gazelle:python_root") should be
# defined in a BUILD file specific to that part of the folder tree. See
# src/BUILD for such an example - it's how we define that the "src" dir should
# be the root of python files and thus get added to sys.path.

# This directive tells gazelle that our tests are named "test_foo.py" instead
# of "foo_test.py".
# gazelle:python_test_naming_convention test_$package_name$

# This directive tells gazelle to make a single bazel target per python file.
# The default is to make a single bazel target per python _package_).
# gazelle:python_generation_mode file

# This directive would be used if, for example, we wanted to make pytest_test
# rules instead of py_test rules. However, this project doesn't use `pytest`
# so the directive is inactive (double #).
## gazelle:map_kind py_test pytest_test //tools/bazel:defs.bzl

# This directive tells gazelle to use import `foobar` using the py_library
# target, rather than the py_binary target (`foobar.py` is both a library
# and an executable, so gazelle gets confuzed, saying that multiple targets
# can satisfy the "mypackage.foobar" import).
# This directive can be set multiple times.
# gazelle:resolve py mypackage.foobar //src/mypackage:foobar

# Python's gazelle added support for the default_visibility directive in
# https://github.com/bazelbuild/rules_python/pull/1787. We can use this to make
# all targets visible by all tests.
# gazelle:python_default_visibility //$python_root:__subpackages__,//tests:__subpackages__

###### End Gazelle Directives ######

# This rule will compile the project requirements into a lock file that
# contains versions and hashes. The lock file ends up getting used when
# installing dependencies via pip.
# bazel run //:requirements.update
compile_pip_requirements(
name = "requirements",
src = "requirements.in",
requirements_txt = "requirements_lock.txt",
)

# This rule fetches the metadata for python packages we depend on. That data is
# required for the gazelle_python_manifest rule to update our manifest file.
modules_mapping(
name = "modules_map",
wheels = all_whl_requirements,
)

# Gazelle python extension needs a manifest file mapping from
# an import to the installed package that provides it.
# This macro produces two targets:
# bazel run //:gazelle_python_manifest.update
# bazel run //:gazelle_python_manifest.test
gazelle_python_manifest(
name = "gazelle_python_manifest",
modules_mapping = ":modules_map",
# This is what we called our `pip_parse` rule, where third-party
# python libraries are loaded in BUILD files.
pip_repository_name = "pypi",
# This should point to wherever we declare our python dependencies
# (the same as what we passed to the pip.parse rule in MODULE.bazel)
# This argument is optional. If provided, the `.test` target is very
# fast because it just has to check an integrity field. If not provided,
# the integrity field is not added to the manifest which can help avoid
# merge conflicts in large repos.
requirements = "//:requirements_lock.txt",
)

# Make a target for running gazelle.
# bazel run //:gazelle
# or:
# bazel run //:gazelle update # Note: "update" is the arg, not part of the target
gazelle(
name = "gazelle",
gazelle = "@rules_python_gazelle_plugin//python:gazelle_binary",
)
23 changes: 23 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,34 @@ bazel_dep(name = "bazel_skylib", version = "1.5.0")
# 4. execute py_respositories()
bazel_dep(name = "rules_python", version = "0.31.0")

# Use a pre-release version of rules_python so that we can access new directives.
# Once 0.32.0 is released we can remove this line.
git_override(
module_name = "rules_python",
commit = "cdc7f2f43186899b970996f0051c702c40b10ea6",
remote = "https://github.com/bazelbuild/rules_python",
)

# Install a prebuilt version of buildifier. Buildifier is a linter and autoformatter
# for starlark files.
# TODO: Replace with an official prebuilt version when available. This currently
# uses the unofficial https://github.com/keith/buildifier-prebuilt. See
# https://github.com/bazelbuild/buildtools/issues/1204
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)

# Gazelle for auto BUILD generation. See
# https://github.com/bazelbuild/rules_python/blob/main/gazelle/README.md
# First install ruleset specifc to python, then gazelle itself.
bazel_dep(name = "rules_python_gazelle_plugin", version = "0.31.0") # same version as rules_python
bazel_dep(name = "gazelle", version = "0.35.0", repo_name = "bazel_gazelle")

# Use a pre-release version of rules_python_gazelle_plugin so that we can
# access new directives. Once 0.32.0 is released we can remove this line.
local_path_override(
module_name = "rules_python_gazelle_plugin",
path = "/c/dev/rules_python/gazelle",
)

# Initialize the python toolchain using the rules_python extension.
# This is similar to the "python_register_toolchains" function in WORKSPACE.
# It creates a hermetic python rather than relying on a system-installed interpreter.
Expand All @@ -40,6 +61,8 @@ pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")

# Configure how we fetch python dependencies via pip
pip.parse(
# Use the bazel downloader for pulling pypi packages.
experimental_index_url = "https://pypi.org/simple",
# This name is what gets used in other BUILD files with `load()`.
hub_name = "pypi",
python_version = "3.10",
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,21 @@ bazel run //:buildifier.check

[buildifier]: https://github.com/bazelbuild/buildtools/blob/master/buildifier/README.md
[starlark]: https://github.com/bazelbuild/starlark


### Gazelle

[Gazelle][gazelle] is a tool for autogenerating `BUILD(.bazel)` files from source
code.

Run by calling all these, in order:

```shell
# If any python dependencies change:
bazel run //:requirements.update
bazel run //:gazelle_python_manifest.update
# Run gazelle and generate BUILD files and targets:
bazel run //:gazelle
```

[gazelle]: https://github.com/bazelbuild/bazel-gazelle
17 changes: 17 additions & 0 deletions gazelle_python.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# GENERATED FILE - DO NOT EDIT!
#
# To update this file, run:
# bazel run //:gazelle_python_manifest.update

manifest:
modules_mapping:
pathspec: pathspec
pathspec.gitignore: pathspec
pathspec.pathspec: pathspec
pathspec.pattern: pathspec
pathspec.patterns: pathspec
pathspec.patterns.gitwildmatch: pathspec
pathspec.util: pathspec
pip_repository:
name: pypi
integrity: e72e5d00a84c0e367082ae476df48fdf05a88a2a91a37815e5704e755950b758
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pathspec
10 changes: 10 additions & 0 deletions requirements_lock.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# bazel run //:requirements.update
#
pathspec==0.12.1 \
--hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \
--hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712
# via -r requirements.in
9 changes: 8 additions & 1 deletion src/mypackage/foo.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
from .subpackage import subfoo
# N.B.: It seems like gazelle doesn't really like relative imports yet.
# So don't do `from .subpackage import subfoo` or else it won't get the dep
# tree correct. Or maybe I'm doing something wrong?
from mypackage.subpackage import subfoo

import pathspec

_ = pathspec


def add(a: int, b: int) -> int:
Expand Down