• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2019 The Dagger Authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Skylark rules to collect Maven artifacts information.
15"""
16
17load("@rules_java//java:defs.bzl", "java_library")
18load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
19
20# TODO(b/142057516): Unfork this file once we've settled on a more general API.
21MavenInfo = provider(
22    fields = {
23        "artifact": """
24        The Maven coordinate for the artifact that is exported by this target, if one exists.
25        """,
26        "has_srcs": """
27        True if this library contains srcs..
28        """,
29        "all_transitive_deps": """
30        All transitive deps of the target with srcs.
31        """,
32        "maven_nearest_artifacts": """
33        The nearest maven deps of the target.
34        """,
35        "maven_transitive_deps": """
36        All transitive deps that are included in some maven dependency.
37        """,
38    },
39)
40
41_EMPTY_MAVEN_INFO = MavenInfo(
42    artifact = None,
43    has_srcs = False,
44    maven_nearest_artifacts = depset(),
45    maven_transitive_deps = depset(),
46    all_transitive_deps = depset(),
47)
48
49_MAVEN_COORDINATES_PREFIX = "maven_coordinates="
50
51def _collect_maven_info_impl(target, ctx):
52    tags = getattr(ctx.rule.attr, "tags", [])
53    srcs = getattr(ctx.rule.attr, "srcs", [])
54    deps = getattr(ctx.rule.attr, "deps", [])
55    exports = getattr(ctx.rule.attr, "exports", [])
56
57    artifact = None
58    for tag in tags:
59        if tag in ("maven:compile_only", "maven:shaded"):
60            return [_EMPTY_MAVEN_INFO]
61        if tag.startswith(_MAVEN_COORDINATES_PREFIX):
62            artifact = tag[len(_MAVEN_COORDINATES_PREFIX):]
63
64    all_deps = [dep.label for dep in (deps + exports) if dep[MavenInfo].has_srcs]
65    all_transitive_deps = [dep[MavenInfo].all_transitive_deps for dep in (deps + exports)]
66
67    maven_artifacts = []
68    maven_nearest_artifacts = []
69    maven_deps = []
70    maven_transitive_deps = []
71    for dep in (deps + exports):
72        # If the dep is itself a maven artifact, add it and all of its transitive deps.
73        # Otherwise, just propagate its transitive maven deps.
74        if dep[MavenInfo].artifact or dep[MavenInfo] == _EMPTY_MAVEN_INFO:
75            if (dep[MavenInfo].artifact):
76                maven_artifacts.append(dep[MavenInfo].artifact)
77            maven_deps.append(dep.label)
78            maven_transitive_deps.append(dep[MavenInfo].all_transitive_deps)
79        else:
80            maven_nearest_artifacts.append(dep[MavenInfo].maven_nearest_artifacts)
81            maven_transitive_deps.append(dep[MavenInfo].maven_transitive_deps)
82
83    return [MavenInfo(
84        artifact = artifact,
85        has_srcs = len(srcs) > 0,
86        maven_nearest_artifacts = depset(maven_artifacts, transitive = maven_nearest_artifacts),
87        maven_transitive_deps = depset(maven_deps, transitive = maven_transitive_deps),
88        all_transitive_deps = depset(all_deps, transitive = all_transitive_deps),
89    )]
90
91collect_maven_info = aspect(
92    attr_aspects = [
93        "deps",
94        "exports",
95    ],
96    doc = """
97    Collects the Maven information for targets, their dependencies, and their transitive exports.
98    """,
99    implementation = _collect_maven_info_impl,
100)
101
102def _fake_java_library(name, deps = None, exports = None, is_artifact = True):
103    src_file = ["%s.java" % name]
104    native.genrule(
105        name = "%s_source_file" % name,
106        outs = src_file,
107        cmd = "echo 'package pkg; class %s {}' > $@" % name,
108    )
109    java_library(
110        name = name,
111        srcs = src_file,
112        tags = ["maven_coordinates=%s:_:_" % name] if is_artifact else [],
113        deps = deps or [],
114        exports = exports or [],
115    )
116
117def _maven_info_test_impl(ctx):
118    env = unittest.begin(ctx)
119    asserts.equals(
120        env,
121        expected = ctx.attr.artifact if ctx.attr.artifact else None,
122        actual = ctx.attr.target[MavenInfo].artifact,
123        msg = "MavenInfo.artifact",
124    )
125    asserts.equals(
126        env,
127        expected = sorted([ctx.label.relative(dep) for dep in ctx.attr.maven_transitive_deps]),
128        actual = sorted(ctx.attr.target[MavenInfo].maven_transitive_deps.to_list()),
129        msg = "MavenInfo.maven_transitive_deps",
130    )
131    asserts.equals(
132        env,
133        expected = sorted([ctx.label.relative(dep) for dep in ctx.attr.all_transitive_deps]),
134        actual = sorted(ctx.attr.target[MavenInfo].all_transitive_deps.to_list()),
135        msg = "MavenInfo.all_transitive_deps",
136    )
137    return unittest.end(env)
138
139_maven_info_test = unittest.make(
140    _maven_info_test_impl,
141    attrs = {
142        "target": attr.label(aspects = [collect_maven_info]),
143        "artifact": attr.string(),
144        "maven_transitive_deps": attr.string_list(),
145        "all_transitive_deps": attr.string_list(),
146    },
147)
148
149def maven_info_tests():
150    """Tests for `pom_file` and `MavenInfo`.
151    """
152    _fake_java_library(name = "A")
153    _fake_java_library(
154        name = "DepOnA",
155        deps = [":A"],
156    )
157
158    _maven_info_test(
159        name = "a_test",
160        target = ":A",
161        artifact = "A:_:_",
162        maven_transitive_deps = [],
163        all_transitive_deps = [],
164    )
165
166    _maven_info_test(
167        name = "dependencies_test",
168        target = ":DepOnA",
169        artifact = "DepOnA:_:_",
170        maven_transitive_deps = [":A"],
171        all_transitive_deps = [":A"],
172    )
173
174    _fake_java_library(
175        name = "ExportsA",
176        exports = [":A"],
177    )
178
179    _maven_info_test(
180        name = "exports_test",
181        target = ":ExportsA",
182        artifact = "ExportsA:_:_",
183        maven_transitive_deps = [":A"],
184        all_transitive_deps = [":A"],
185    )
186
187    _fake_java_library(
188        name = "TransitiveExports",
189        exports = [":ExportsA"],
190    )
191
192    _maven_info_test(
193        name = "transitive_exports_test",
194        target = ":TransitiveExports",
195        artifact = "TransitiveExports:_:_",
196        maven_transitive_deps = [":ExportsA", ":A"],
197        all_transitive_deps = [":ExportsA", ":A"],
198    )
199
200    _fake_java_library(
201        name = "TransitiveDeps",
202        deps = [":ExportsA"],
203    )
204
205    _maven_info_test(
206        name = "transitive_deps_test",
207        target = ":TransitiveDeps",
208        artifact = "TransitiveDeps:_:_",
209        maven_transitive_deps = [":ExportsA", ":A"],
210        all_transitive_deps = [":ExportsA", ":A"],
211    )
212
213    _fake_java_library(name = "Node1", is_artifact = False)
214    _maven_info_test(
215        name = "test_node1",
216        target = ":Node1",
217        maven_transitive_deps = [],
218        all_transitive_deps = [],
219    )
220
221    _fake_java_library(name = "Node2_Artifact", deps = [":Node1"])
222    _maven_info_test(
223        name = "test_node2",
224        target = ":Node2_Artifact",
225        artifact = "Node2_Artifact:_:_",
226        maven_transitive_deps = [],
227        all_transitive_deps = [":Node1"],
228    )
229
230    _fake_java_library(name = "Node3", deps = [":Node2_Artifact"], is_artifact = False)
231    _maven_info_test(
232        name = "test_node3",
233        target = ":Node3",
234        maven_transitive_deps = [":Node1", ":Node2_Artifact"],
235        all_transitive_deps = [":Node1", ":Node2_Artifact"],
236    )
237
238    _fake_java_library(name = "Node4", deps = [":Node3"], is_artifact = False)
239    _maven_info_test(
240        name = "test_node4",
241        target = ":Node4",
242        maven_transitive_deps = [":Node1", ":Node2_Artifact"],
243        all_transitive_deps = [":Node1", ":Node2_Artifact", ":Node3"],
244    )
245