A linting framework for Bazel
You must load and configure the linting framework before anything else. This is because later rulesets that depend on the linting framework will attempt to ensure that linters are configured by registering no-op implementations of lint configs. You can do this by:
# WORKSPACE
load("@apple_rules_lint//lint:repositories.bzl", "lint_deps")
lint_deps()
load("@apple_rules_lint//lint:setup.bzl", "lint_setup")
lint_setup({
"java-checkstyle": "//your:checkstyle-config",
})
Alternatively, using Bzlmod:
# MODULE.bazel
bazel_dep(name = "apple_rules_lint", version = "0.4.0")
linter = use_extension("@apple_rules_lint//lint:extensions.bzl", "linter")
linter.configure(name = "java-checkstyle", config = "//your:checkstyle-config")
use_repo(linter, "apple_linters")
You may override specific lint configurations on a per-package basis by:
# BUILD.bazel
load("@apple_rules_lint//lint:defs.bzl", "package_lint_config")
package_lint_config({
"java-checkstyle": ":alternative-checkstyle-config",
})
@@apple_linters errorBazel may report an error like this:
ERROR: Failed to load Starlark extension '@@apple_linters//:defs.bzl'.
Cycle in the workspace file detected. This indicates that a repository is used prior to being defined.
The following chain of repository dependencies lead to the missing definition.
- @@apple_linters
This could either mean you have to add the '@@apple_linters' repository with a statement like `http_archive` in your WORKSPACE file (note that transitive dependencies are not added automatically), or move an existing definition earlier in your WORKSPACE file.
The @apple_linters repository is defined when lint_setup is called. You'll need to figure out where load("@apple_linters//:defs.bzl, ...) is getting called, and modify your build to ensure that lint_setup is called before linting is loaded.
Can be found in the api docs
To add linter support to your repo, add this to...
# repositories.bzl
load("@apple_rules_lint//lint:repositories.bzl", "lint_deps")
lint_deps()
Then add this to...
# setup.bzl
load("@apple_rules_lint//lint:setup.bzl", "ruleset_lint_setup")
ruleset_lint_setup()
Add:
# MODULE.bazel
bazel_dep(name = "apple_rules_lint", version = "0.1.1")
linter = use_extension("@apple_rules_lint//lint:extensions.bzl", "linter")
linter.register(name = "java-checkstyle")
To obtain the currently configured config for a ruleset, use:
# your_rules.bzl
load("@apple_rules_lint//lint:defs.bzl", "get_lint_config")
config = get_lint_config("java-checkstyle", tags)
if config != None:
# set up lint targets
pass
Where tags are the tags of the rule to check.
apple_rules_lint With Your RulesetsFor the sake of this example, we'll show how apple_rules_lint is
integrated with the Selenium project, but the same process can be
followed for any linter:
Wrap the linter with a _test rule, so you can run them with bazel
test. In Selenium, this is the
spotbugs_test
It is recommended, but not required, that your test return a LinterInfo
so that other tooling can detect whether this is a lint test.
Create a config rule or a marker rule of some sort. For example, spotbugs_config
Pick a "well known" name: lang-tool seems to work well (such as
java-spotbugs, but you might have go-gofmt or py-black)
Create a macro that uses get_lint_config to look up the config for you. If that's present, create a new instance of your test rule. You can see this in action here.
As you write code, make sure your macro is called. If you're a ruleset author, this can be as lightweight as exporting the macro created above as the default way to call your rules.
...
Profit!
Users can then use the "well known" name to point to an instance of
the config rule in their WORKSPACE files:
lint_setup({
"java-spotbugs": "//java:spotbugs-config",
})