Skip to content

Add deps filter rules for managing dependencies #261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jan 24, 2025
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
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ maven.install(
# test deps
"junit:junit:4.13.2",
"org.hamcrest:hamcrest-core:2.2",
"org.assertj:assertj-core:3.25.3",
],
lock_file = "//:maven_install.json",
)
Expand Down
4 changes: 2 additions & 2 deletions examples/demoapp/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# "@rules_spring//springboot" when you consume the official release via http_archive.

load("//springboot:springboot.bzl", "springboot")
load("//springboot:deps_filter_transitive.bzl", "deps_filter_transitive")
load("//springboot/deps_filter_rules:deps_filter.bzl", "deps_filter")
load("//tools/license:licenses_used.bzl", "licenses_used")
load(":generate-build-info.bzl", "gen_buildinfo_rule")

Expand Down Expand Up @@ -39,7 +39,7 @@ springboot_deps = [

# Sometimes you have a transitive that you don't want. The unwanted_classes.md doc
# covers this case, and this snippet shows how to use it:
deps_filter_transitive(
deps_filter(
name = "filtered_deps",
deps_exclude = [
# tomcat comes in transitively, but we want to use jetty
Expand Down
2 changes: 1 addition & 1 deletion maven_install.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL",
"__INPUT_ARTIFACTS_HASH": 1186442234,
"__INPUT_ARTIFACTS_HASH": -692374429,
"__RESOLVED_ARTIFACTS_HASH": -2071005252,
"conflict_resolution": {
"com.google.errorprone:error_prone_annotations:2.5.1": "com.google.errorprone:error_prone_annotations:2.18.0",
Expand Down
129 changes: 129 additions & 0 deletions springboot/deps_filter_rules/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
load("//springboot/deps_filter_rules:deps_filter.bzl", "deps_filter")
load("//springboot/deps_filter_rules:deps_filter_disable_transitives.bzl", "deps_filter_disable_transitives")

deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
]

test_deps = [
"@maven//:junit_junit",
"@maven//:org_assertj_assertj_core",
]

deps_filter(
name = "filtered_test_deps_with_transitives_excluded",
deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
deps_exclude = [
"@maven//:org_springframework_spring_web", # share transitives with spring-jdbc
],
exclude_transitives = True,
testonly = True,
)

deps_filter(
name = "filtered_test_deps_without_transitives_excluded",
deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
deps_exclude = [
"@maven//:org_springframework_spring_web", # share transitives with spring-jdbc
],
exclude_transitives = False,
testonly = True,
)

deps_filter_disable_transitives(
name = "filtered_deps_disable_transitives_case_A",
deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
deps_to_exclude_transitives = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
testonly = True,
)

deps_filter_disable_transitives(
name = "filtered_deps_disable_transitives_case_B",
deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
deps_to_exclude_transitives = [
# "@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
testonly = True,
)

deps_filter_disable_transitives(
name = "filtered_deps_disable_transitives_case_C",
deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
deps_to_exclude_transitives = [
"@maven//:org_springframework_spring_jdbc",
# "@maven//:org_springframework_spring_web",
],
testonly = True,
)

deps_filter_disable_transitives(
name = "filtered_deps_disable_transitives_case_D",
deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
testonly = True,
)

java_test(
name = "TransitiveDepsFilterCaseA",
size = "small",
srcs = ["src/test/java/com/depsfilter/TransitiveDepsFilterCaseA.java"],
deps = [":filtered_deps_disable_transitives_case_A"] + test_deps,
)

java_test(
name = "TransitiveDepsFilterCaseB",
size = "small",
srcs = ["src/test/java/com/depsfilter/TransitiveDepsFilterCaseB.java"],
deps = [":filtered_deps_disable_transitives_case_B"] + test_deps,
)

java_test(
name = "TransitiveDepsFilterCaseC",
size = "small",
srcs = ["src/test/java/com/depsfilter/TransitiveDepsFilterCaseC.java"],
deps = [":filtered_deps_disable_transitives_case_C"] + test_deps,
)

java_test(
name = "TransitiveDepsFilterCaseD",
size = "small",
srcs = ["src/test/java/com/depsfilter/TransitiveDepsFilterCaseD.java"],
deps = [":filtered_deps_disable_transitives_case_D"] + test_deps,
)


java_test(
name = "DepsFilterWithTransitivesExclusionTest",
size = "small",
srcs = ["src/test/java/com/depsfilter/DepsFilterWithTransitivesExclusionTest.java"],
deps = [":filtered_test_deps_with_transitives_excluded"] + test_deps,
)

java_test(
name = "DepsFilterWithoutTransitivesExclusionTest",
size = "small",
srcs = ["src/test/java/com/depsfilter/DepsFilterWithoutTransitivesExclusionTest.java"],
deps = [":filtered_test_deps_without_transitives_excluded"] + test_deps,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ def _depaggregator_rule_impl(merged, ctx):
to assemble a cohesive set of jars essential for the build process. During
this process, it excludes deps specified in 'deps_exclude', which
lists jar labels to be omitted from packaging due to issues that cannot
be resolved upstream. By default, with 'exclude_transitives' set to true, any
transitive deps that are only required by excluded deps
be resolved upstream. If 'exclude_transitives' is set to 'true' (default:
'false'), any transitive deps that are only required by excluded deps
are also omitted, ensuring that only necessary transitives are included
in the final package. It uses 'deps_exclude_paths' to exclude deps
based on partial filename matches, ensuring problematic files are also
Expand Down Expand Up @@ -76,13 +76,13 @@ def _depaggregator_rule_impl(merged, ctx):

return jars

def _deps_filter_transitive_impl(ctx):
def _deps_filter_impl(ctx):
"""
This rule filters out specified deps and JARs from the compile-time
and runtime deps. It utilizes the 'deps_exclude' attribute to omit
specific JAR labels and the 'deps_exclude_paths' attribute to exclude
deps based on partial paths in their filenames. By default, with
'exclude_transitives' set to true, any transitive deps solely required
deps based on partial paths in their filenames. If 'exclude_transitives'
is set to `True` (default: `False`), any transitive deps solely required
by the deps in 'deps_exclude' are also excluded. These exclusions ensure
the final collection includes only the necessary elements for the build
process, eliminating problematic deps.
Expand Down Expand Up @@ -114,13 +114,13 @@ def _deps_filter_transitive_impl(ctx):
]


deps_filter_transitive = rule(
implementation = _deps_filter_transitive_impl,
deps_filter = rule(
implementation = _deps_filter_impl,
attrs = {
"deps": attr.label_list(providers = [java_common.provider]),
"runtime_deps": attr.label_list(providers = [java_common.provider], allow_empty = True),
"deps_exclude": attr.label_list(providers = [java_common.provider], allow_empty = True),
"deps_exclude_paths": attr.string_list(),
"exclude_transitives": attr.bool(default = True),
"exclude_transitives": attr.bool(default = False),
},
)
135 changes: 135 additions & 0 deletions springboot/deps_filter_rules/deps_filter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# `deps_filter` Rule
## Overview
The `deps_filter` rule provides a way to filter java dependencies in Bazel projects, ensuring only the necessary ones
are included in the build. This rule can be referenced by other java targets, such as `java_library` and helps to
remove problematic or unwanted dependencies, offering better control over dependency graphs.

It removes Java dependencies from your dependency graph. This is done when you have knowledge that Bazel does not,
specifically that your application does not need a dependency at runtime.

This is useful in cases where:
- You want to exclude a dependency for a specific reason (it has a vulnerability)
- There are multiple versions of a dependency on the classpath (dupe classes) and you want to exclude the unfavored one.


## Rule Definition
The rule is defined as `dep_filter` in `deps_filter.bzl`. It filters out specified deps and JARs from the compile-time and runtime deps. It utilizes the `deps_exclude` attribute to omit specific JAR labels and the `deps_exclude_paths` attribute to exclude dependencies based on partial paths in their filenames. If `exclude_transitives` is set to `True` (default: `False`), any transitive dependencies solely required by the dependencies in `deps_exclude` are also excluded. These exclusions ensure the final collection includes only the necessary elements for the build process, eliminating problematic dependencies.

```
deps_filter(
name = <string>,
deps = <list of labels>,
runtime_deps = <list of labels>,
deps_exclude = <list of labels>,
deps_exclude_paths = <list of strings>,
exclude_transitives = <boolean, default = False>,
testonly = <boolean, default = False>,
)
```


### Attributes:
- `name` (Required): Name of the target.
- `deps` (Required): List of dependencies to include.
- `runtime_deps` (Optional): List of runtime dependencies to include.
- `deps_exclude` (Optional): Dependencies to exclude from the build.
- `deps_exclude_paths` (Optional): Filename patterns for excluding dependencies.
- `exclude_transitives` (Optional, Default: `False`): If `True`, transitive dependencies of excluded dependencies are
also removed, unless needed by other included dependencies.
- `testonly` (Optional, Default: `False`): Restricts usage to test environments.

### Behavior
1. **Excludes Specific Dependencies**:
- Removes any dependencies listed in `deps_exclude`.
2. **Handles Transitive Dependencies**:
- If `exclude_transitives` is `True`, transitive dependencies that are only required by excluded dependencies are removed.
- If `False`, transitive dependencies remain in the build.
3. **Filename-Based Exclusions**:
- Dependencies matching patterns in `deps_exclude_paths` are excluded.


## Adding to Your Project
### Step 1: Add `rules_spring` to your workspace:
Follow the steps mentioned [here](../../README.md#loading-the-spring-rules-in-your-workspace).

### Step 2: Load the `deps_filter` rule in your BUILD file:
```
load("@rules_spring//springboot/deps_filter_rules:deps_filter.bzl", "deps_filter")
```

### Step 3: Define and reference the `deps_filter` rule in other targets (e.g., `java_library`) to manage their dependencies:
```
load("@rules_spring//springboot/deps_filter_rules:deps_filter.bzl", "deps_filter")

deps = [
...
]

runtime_deps = [
...
]

deps_filter(
name = "filtered_deps",
deps = deps,
runtime_deps = runtime_deps,
deps_exclude_labels = [
"@maven//:some_dep_you_dont_want",
],
deps_exclude_paths = [
"javax-servlet",
],
exclude_transitives = True,
)

java_library(
name = "my_library",
deps = [":filtered_deps"], # <--- KEY LINE: USE YOUR FILTERED DEPS HERE!
...
)

# the filter is not specific to springboot apps, but is often used for springboot
# due to the complexity of classpaths in springboot apps
salesforce_springboot(
name = "myspringboot_app",
java_library = ":my_library",
)

```

## Example Usage
### Example 1: Without Excluding Transitives
```
deps_filter(
name = "filtered_deps",
deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
deps_exclude = [
"@maven//:org_springframework_spring_web",
],
exclude_transitives = False,
testonly = True,
)
```
#### Behavior:
`org_springframework_spring_web` is excluded, but its transitive dependencies remain.

### Example 2: With Excluding Transitives
```
deps_filter(
name = "filtered_deps",
deps = [
"@maven//:org_springframework_spring_jdbc",
"@maven//:org_springframework_spring_web",
],
deps_exclude = [
"@maven//:org_springframework_spring_web",
],
exclude_transitives = True,
testonly = True,
)
```
#### Behavior:
Both `org_springframework_spring_web` and its transitive dependencies are excluded, except those required by `org_springframework_spring_jdbc`.
Loading