Apply Maven exclusions to generated deps (#1594)
diff --git a/private/dependency_tree_parser.bzl b/private/dependency_tree_parser.bzl
index 0b82991..d8bb74c 100644
--- a/private/dependency_tree_parser.bzl
+++ b/private/dependency_tree_parser.bzl
@@ -64,6 +64,16 @@
             unique_items.append(item)
     return unique_items
 
+def _is_excluded_dependency(dep, exclusions):
+    dep_coordinates = unpack_coordinates(dep)
+    for exclusion in exclusions:
+        exclusion_coordinates = unpack_coordinates(exclusion)
+        group_matches = exclusion_coordinates.group == "*" or exclusion_coordinates.group == dep_coordinates.group
+        artifact_matches = exclusion_coordinates.artifact == "*" or exclusion_coordinates.artifact == dep_coordinates.artifact
+        if group_matches and artifact_matches:
+            return True
+    return False
+
 def _find_repository_url(artifact_url, repositories):
     longest_match = None
     for repository in repositories:
@@ -183,10 +193,13 @@
 
     # Dedupe dependencies here. Sometimes coursier will return "x.y:z:aar:version" and "x.y:z:version" in the
     # same list of dependencies.
+    artifact_exclusions = exclusions.get(simple_coord, [])
     target_import_labels = []
     for dep in artifact["deps"]:
         if get_packaging(dep) == "json":
             continue
+        if _is_excluded_dependency(dep, artifact_exclusions):
+            continue
         stripped_dep = strip_packaging_and_classifier_and_version(dep)
         dep_target_label = escape(stripped_dep)
 
@@ -576,7 +589,10 @@
             target_import_string.append("\texports = [")
 
             target_import_labels = []
+            artifact_exclusions = exclusions.get(simple_coord, [])
             for dep in artifact.get("deps", []):
+                if _is_excluded_dependency(dep, artifact_exclusions):
+                    continue
                 dep_target_label = escape(strip_packaging_and_classifier_and_version(dep))
 
                 # Coursier returns cyclic dependencies sometimes. Handle it here.
diff --git a/tests/unit/BUILD b/tests/unit/BUILD
index f3a6e1d..e6cf8f2 100644
--- a/tests/unit/BUILD
+++ b/tests/unit/BUILD
@@ -3,6 +3,7 @@
 load(":coordinates_test.bzl", "coordinates_test_suite")
 load(":coursier_test.bzl", "coursier_test_suite")
 load(":coursier_utilities_test.bzl", "coursier_utilities_test_suite")
+load(":dependency_tree_parser_test.bzl", "dependency_tree_parser_test_suite")
 load(":java_utilities_test.bzl", "java_utilities_test_suite")
 load(":maven_version_test.bzl", "maven_version_test_suite")
 load(":proxy_test.bzl", "proxy_test_suite")
@@ -21,6 +22,8 @@
 
 coursier_utilities_test_suite()
 
+dependency_tree_parser_test_suite()
+
 java_utilities_test_suite()
 
 maven_version_test_suite()
diff --git a/tests/unit/dependency_tree_parser_test.bzl b/tests/unit/dependency_tree_parser_test.bzl
new file mode 100644
index 0000000..fa43641
--- /dev/null
+++ b/tests/unit/dependency_tree_parser_test.bzl
@@ -0,0 +1,153 @@
+"""Tests for generated dependency tree BUILD target declarations."""
+
+load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
+load("//private:dependency_tree_parser.bzl", "parser")
+
+def _repo_ctx():
+    return struct(
+        attr = struct(
+            fetch_javadoc = False,
+            fetch_sources = False,
+            generate_compat_repositories = False,
+            maven_install_json = False,
+            repositories = ["{ \"repo_url\": \"https://repo1.maven.org/maven2/\" }"],
+            strict_visibility = False,
+            strict_visibility_value = ["//visibility:public"],
+        ),
+    )
+
+def _generate_imports(dependencies, exclusions):
+    result = parser.generate_imports(
+        repository_ctx = _repo_ctx(),
+        dependencies = dependencies,
+        explicit_artifacts = {},
+        neverlink_artifacts = {},
+        testonly_artifacts = {},
+        exclusions = exclusions,
+        override_targets = {},
+        override_target_visibilities = {},
+        skip_maven_local_dependencies = False,
+    )
+    return result[0]
+
+def _artifact(group_artifact, version, deps = []):
+    group, artifact = group_artifact.split(":")
+    return {
+        "coordinates": "%s:%s" % (group_artifact, version),
+        "deps": deps,
+        "file": "v1/https/repo1.maven.org/maven2/%s/%s/%s/%s-%s.jar" % (
+            group.replace(".", "/"),
+            artifact,
+            version,
+            artifact,
+            version,
+        ),
+        "urls": [
+            "https://repo1.maven.org/maven2/%s/%s/%s/%s-%s.jar" % (
+                group.replace(".", "/"),
+                artifact,
+                version,
+                artifact,
+                version,
+            ),
+        ],
+    }
+
+def _exclusion_removes_generated_dep_impl(ctx):
+    env = unittest.begin(ctx)
+
+    generated_imports = _generate_imports(
+        dependencies = [
+            _artifact(
+                "com.example:parent",
+                "1.0",
+                deps = [
+                    "com.example:excluded:1.0",
+                    "com.example:kept:1.0",
+                ],
+            ),
+            _artifact("com.example:excluded", "1.0"),
+            _artifact("com.example:kept", "1.0"),
+        ],
+        exclusions = {
+            "com.example:parent": [
+                "com.example:not-it",
+                "com.example:excluded",
+            ],
+        },
+    )
+
+    asserts.true(env, "\"maven_exclusion=com.example:not-it\"," in generated_imports)
+    asserts.true(env, "\"maven_exclusion=com.example:excluded\"," in generated_imports)
+    asserts.false(env, "\t\t\":com_example_excluded\",\n" in generated_imports)
+    asserts.true(env, "\t\t\":com_example_kept\",\n" in generated_imports)
+
+    return unittest.end(env)
+
+exclusion_removes_generated_dep_test = unittest.make(_exclusion_removes_generated_dep_impl)
+
+def _wildcard_exclusion_removes_all_generated_deps_impl(ctx):
+    env = unittest.begin(ctx)
+
+    generated_imports = _generate_imports(
+        dependencies = [
+            _artifact(
+                "com.example:parent",
+                "1.0",
+                deps = [
+                    "com.example:first:1.0",
+                    "com.example:second:1.0",
+                ],
+            ),
+            _artifact("com.example:first", "1.0"),
+            _artifact("com.example:second", "1.0"),
+        ],
+        exclusions = {
+            "com.example:parent": ["*:*"],
+        },
+    )
+
+    asserts.true(env, "\"maven_exclusion=*:*\"," in generated_imports)
+    asserts.false(env, "\t\t\":com_example_first\",\n" in generated_imports)
+    asserts.false(env, "\t\t\":com_example_second\",\n" in generated_imports)
+
+    return unittest.end(env)
+
+wildcard_exclusion_removes_all_generated_deps_test = unittest.make(_wildcard_exclusion_removes_all_generated_deps_impl)
+
+def _pom_only_exclusion_removes_generated_export_impl(ctx):
+    env = unittest.begin(ctx)
+
+    generated_imports = _generate_imports(
+        dependencies = [
+            {
+                "coordinates": "com.example:parent-pom:1.0@pom",
+                "deps": [
+                    "com.example:excluded:1.0",
+                    "com.example:kept:1.0",
+                ],
+                "file": None,
+                "urls": [],
+            },
+            _artifact("com.example:excluded", "1.0"),
+            _artifact("com.example:kept", "1.0"),
+        ],
+        exclusions = {
+            "com.example:parent-pom": ["com.example:excluded"],
+        },
+    )
+
+    asserts.false(env, "\t\t\":com_example_excluded\",\n" in generated_imports)
+    asserts.true(env, "\t\t\":com_example_kept\",\n" in generated_imports)
+
+    return unittest.end(env)
+
+pom_only_exclusion_removes_generated_export_test = unittest.make(_pom_only_exclusion_removes_generated_export_impl)
+
+def dependency_tree_parser_test_suite():
+    unittest.suite(
+        "dependency_tree_parser_tests",
+        exclusion_removes_generated_dep_test,
+        wildcard_exclusion_removes_all_generated_deps_test,
+        pom_only_exclusion_removes_generated_export_test,
+    )