Handy rules for working with JVM-based projects in Bazel.
In order to use these in your own projects, in your WORKSPACE
once
you've used an http_archive
, you can load all the necessary
dependencies by:
load("@contrib_rules_jvm//:repositories.bzl", "contrib_rules_jvm_deps")
contrib_rules_jvm_deps()
load("@contrib_rules_jvm//:setup.bzl", "contrib_rules_jvm_setup")
contrib_rules_jvm_setup()
If you're looking to get started quickly, then take a look at java_test_suite (a macro for generating a test suite from a glob
of java test sources) and java_junit5_test (a drop-in replacement for java_test
that can run JUnit5 tests)
Many of the features in this repo are designed to be exposed via apple_rules_lint, which provides a framework for integrating linting checks into your builds. To take advantage of this perform the following steps:
# In your WORKSPACE, after loading `apple_rules_lint`
load("@apple_rules_lint//lint:setup.bzl", "lint_setup")
lint_setup({
# Note: this is an example config!
"java-checkstyle": "@contrib_rules_jvm//java:checkstyle-default-config",
"java-pmd": "@contrib_rules_jvm//java:pmd-config",
"java-spotbugs": "@contrib_rules_jvm//java:spotbugs-default-config",
})
You are welcome to include all (or none!) of these rules, and linting
is "opt-in": if there's no lint_setup
call in your repo's
WORKSPACE
then everything will continue working just fine and no
additional lint tests will be generated.
The linters are configured using specific rules. The mappings are:
Well known name | Lint config rule |
---|---|
java-checkstyle | checkstyle_config |
java-pmd | pmd_ruleset |
java-spotbugs | spotbugs_config |
These rules require Java 11 or above.
The gazelle plugin requires Go 1.18 or above.
checkstyle_config(name, checkstyle_binary, config_file, data, output_format)
Rule allowing checkstyle to be configured. This is typically
used with the linting rules from @apple_rules_lint
to configure how
checkstyle should run.
ATTRIBUTES
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
name | A unique name for this target. | Name | required | |
checkstyle_binary | Checkstyle binary to use. | Label | optional | @contrib_rules_jvm//java:checkstyle_cli |
config_file | The config file to use for all checkstyle tests | Label | required | |
data | Additional files to make available to Checkstyle such as any included XML files | List of labels | optional | [] |
output_format | Output format to use. Defaults to plain | String | optional | "plain" |
pmd_ruleset(name, format, pmd_binary, rulesets, shallow)
Select a rule set for PMD tests.
ATTRIBUTES
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
name | A unique name for this target. | Name | required | |
format | Generate report in the given format. One of html, text, or xml (default is xml) | String | optional | "xml" |
pmd_binary | PMD binary to use. | Label | optional | //java:pmd |
rulesets | Use these rulesets. | List of labels | optional | [] |
shallow | Use the targetted output to increase PMD's depth of processing | Boolean | optional | True |
pmd_test(name, ruleset, srcs, target)
Use PMD to lint the srcs
.
ATTRIBUTES
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
name | A unique name for this target. | Name | required | |
ruleset | - | Label | required | |
srcs | - | List of labels | optional | [] |
target | - | Label | optional | None |
spotbugs_config(name, effort, exclude_filter, fail_on_warning, spotbugs_binary)
Configuration used for spotbugs, typically by the //lint
rules.
ATTRIBUTES
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
name | A unique name for this target. | Name | required | |
effort | Effort can be min, less, default, more or max. Defaults to default | String | optional | "default" |
exclude_filter | Report all bug instances except those matching the filter specified by this filter file | Label | optional | None |
fail_on_warning | Whether to fail on warning, or just create a report. Defaults to True | Boolean | optional | True |
spotbugs_binary | The spotbugs binary to run. | Label | optional | @contrib_rules_jvm//java:spotbugs_cli |
spotbugs_test(name, config, deps, only_output_jars)
Use spotbugs to lint the srcs
.
ATTRIBUTES
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
name | A unique name for this target. | Name | required | |
config | - | Label | optional | @contrib_rules_jvm//java:spotbugs-default-config |
deps | - | List of labels | required | |
only_output_jars | If set to true, only the output jar of the target will be analyzed. Otherwise all transitive runtime dependencies will be analyzed | Boolean | optional | True |
checkstyle_binary(name, main_class, deps, runtime_deps, srcs, visibility, kwargs)
Macro for quickly generating a java_binary
target for use with checkstyle_config
.
By default, this will set the main_class
to point to the default one used by checkstyle
but it's ultimately a drop-replacement for straight java_binary
target.
At least one of runtime_deps
, deps
, and srcs
must be specified so that the
java_binary
target will be valid.
An example would be:
checkstyle_binary(
name = "checkstyle_cli",
runtime_deps = [
artifact("com.puppycrawl.tools:checkstyle"),
]
)
PARAMETERS
checkstyle_test(name, size, timeout, kwargs)
PARAMETERS
Name | Description | Default Value |
---|---|---|
name | - |
none |
size | - |
"medium" |
timeout | - |
"short" |
kwargs | - |
none |
java_binary(name, kwargs)
Adds linting tests to Bazel's own java_binary
PARAMETERS
Name | Description | Default Value |
---|---|---|
name | - |
none |
kwargs | - |
none |
java_export(name, maven_coordinates, pom_template, deploy_env, visibility, kwargs)
Adds linting tests to rules_jvm_external
's java_export
PARAMETERS
Name | Description | Default Value |
---|---|---|
name | - |
none |
maven_coordinates | - |
none |
pom_template | - |
None |
deploy_env | - |
None |
visibility | - |
None |
kwargs | - |
none |
java_junit5_test(name, test_class, runtime_deps, package_prefixes, jvm_flags, include_tags, exclude_tags, include_engines, exclude_engines, kwargs)
Run junit5 tests using Bazel.
This is designed to be a drop-in replacement for java_test
, but
rather than using a JUnit4 runner it provides support for using
JUnit5 directly. The arguments are the same as used by java_test
.
By default Bazel, and by extension this rule, assumes you want to
always run all of the tests in a class file. The include_tags
and exclude_tags
allows for selectively running specific tests
within a single class file based on your use of the @Tag
Junit5
annotations. Please see the JUnit 5
docs
for more information about using JUnit5 tag annotation to control
test execution.
The generated target does not include any JUnit5 dependencies. If
you are using the standard @maven
namespace for your
maven_install
you can add these to your deps
using
JUNIT5_DEPS
or JUNIT5_VINTAGE_DEPS
loaded from
//java:defs.bzl
Note: The junit5 runner prevents System.exit
being called
using a SecurityManager
, which means that one test can't
prematurely cause an entire test run to finish unexpectedly.
While the SecurityManager
has been deprecated in recent Java
releases, there's no replacement yet. JEP 411 has this as one of
its goals, but this is not complete or available yet.
PARAMETERS
java_library(name, kwargs)
Adds linting tests to Bazel's own java_library
PARAMETERS
Name | Description | Default Value |
---|---|---|
name | - |
none |
kwargs | - |
none |
java_test(name, kwargs)
Adds linting tests to Bazel's own java_test
PARAMETERS
Name | Description | Default Value |
---|---|---|
name | - |
none |
kwargs | - |
none |
java_test_suite(name, srcs, runner, test_suffixes, package, deps, runtime_deps, size, kwargs)
Create a suite of java tests from *Test.java
files.
This rule will create a java_test
for each file which matches
any of the test_suffixes
that are passed to this rule as
srcs
. If any non-test sources are added these will first of all
be compiled into a java_library
which will be added as a
dependency for each test target, allowing common utility functions
to be shared between tests.
The generated java_test
targets will be named after the test file:
FooTest.java
will create a :FooTest
target.
In addition, a test_suite
will be created, named using the name
attribute to allow all the tests to be run in one go.
PARAMETERS
pmd_binary(name, main_class, deps, runtime_deps, srcs, visibility, kwargs)
Macro for quickly generating a java_binary
target for use with pmd_ruleset
.
By default, this will set the main_class
to point to the default one used by PMD
but it's ultimately a drop-replacement for a regular java_binary
target.
At least one of runtime_deps
, deps
, and srcs
must be specified so that the
java_binary
target will be valid.
An example would be:
pmd_binary(
name = "pmd",
runtime_deps = [
artifact("net.sourceforge.pmd:pmd-dist"),
],
)
PARAMETERS
spotbugs_binary(name, main_class, deps, runtime_deps, srcs, visibility, kwargs)
Macro for quickly generating a java_binary
target for use with spotbugs_config
.
By default, this will set the main_class
to point to the default one used by spotbugs
but it's ultimately a drop-replacement for a regular java_binary
target.
At least one of runtime_deps
, deps
, and srcs
must be specified so that the
java_binary
target will be valid.
An example would be:
spotbugs_binary(
name = "spotbugs_cli",
runtime_deps = [
artifact("com.github.spotbugs:spotbugs"),
artifact("org.slf4j:slf4j-jdk14"),
],
)
PARAMETERS
At runtime, a handful of dependencies are required by helper classes
in this project. Rather than pollute the default @maven
workspace,
these are loaded into a @contrib_rules_jvm_deps
workspace. These
dependencies are loaded using a call to maven_install
, but we don't
want to force users to remember to load our own dependencies for
us. Instead, to add a new dependency to the project:
- Update
frozen_deps
in theWORKSPACE
file - Run
./tools/update-dependencies.sh
- Commit the updated files.
As noted above, if you are building Bazel rules which require Java parts, and hence Java dependencies, it can be useful to freeze these dependencies. This process captures the list of dependencies into a zip file. This zip file is distributed with your Bazel rules. This makes your rule more hermetic, your rule no longer relies on the user to supply the correct dependencies, because they get resolved under their own repository namespace rather than being intermingled with the user's, and your dependencies no longer conflict with the users selections. If you would like to create your dependencies as a frozen file you need to do the following:
- Create a maven install rule with your dependencies and with a
unique name, this should be in or referenced by your WORKSPACE
file.
maven_install( name = "frozen_deps", artifacts = [...], fail_if_repin_required = True, fetch_sources = True, maven_install_json = "@workspace//:frozen_deps_install.json", ) load("@frozen_deps//:defs.bzl", "pinned_maven_install") pinned_maven_install()
- Run
bazel run //tools:freeze-deps -- --repo <repo name> --zip <path/to/dependency.zip>
. The<repo name>
matches the name used for themaven_install()
rule above. This will pin the dependencies then collect them into the zip file. - Commit the zip file into your repo.
- Add a
zip_repository()
rule to your WORKSPACE to configure the frozen dependencies:maybe( zip_repository, name = "workspace_deps", path = "@workspace//path/to:dependency.zip", )
- Make sure to pin the maven install from the repostitory:
load("@workspace_deps//:defs.bzl", "pinned_maven_install") pinned_maven_install()
- Use the dependencies for your
java_library
rules from the frozen deps:java_library( name = "my_library", srcs = glob(["*.java"]), deps = [ "@workspace_deps//:my_frozen_dep", ], )
NOTE: If you need to generate the compat_repositories for the dependencies, usually because your rule depends on another rule which is still using the older compat repositories, you need to make the following changes and abide by the following restrictions.
- Add the
generate_compat_repositories = True,
attribute to the originalmaven_install()
rule. - In step 2, add the parameter
--zip-repo workspace_deps
to match the name used in thezip_repository()
rule (step 4). If you don't supply this, it uses the base name of the zip file, which may not be what you want. - In step 5, add the call to generate the compat_repositories:
load("@workspace_deps//:compat.bzl", "compat_repositories") compat_repositories()