This repository has been archived by the owner on Sep 15, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
import_external.bzl
356 lines (299 loc) · 12.5 KB
/
import_external.bzl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# Copyright 2018 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Rules for defining external Java dependencies.
java_import_external() replaces `maven_jar` and `http_jar`. It is the
recommended solution for defining third party Java dependencies that are
obtained from web servers.
This solution offers high availability, low latency, and repository
scalability at the cost of simplicity. Tooling can be used to generate
the WORKSPACE definitions from Maven metadata.
The default target in this BUILD file will always have the same name as
the repository itself. This means that other Bazel rules can depend on
it as `@repo//:repo` or `@repo` for short.
### Setup
Add the following to your `WORKSPACE` file:
```python
load("@bazel_tools//tools/build_defs/repo:java.bzl", "java_import_external")
```
### Best Practices
#### Downloading
The recommended best practices for downloading Maven jars are as follows:
1. Always follow release versions or pinned revisions.
2. Permanently mirror all dependencies to GCS or S3 as the first URL
3. Put the original URL in the GCS or S3 object name
4. Make the second URL the original repo1.maven.org URL
5. Make the third URL the maven.ibiblio.org mirror, if it isn't 404
6. Always specify the sha256 checksum
Bazel has one of the most sophisticated systems for downloading files of any
build system. Following these best practices will ensure that your codebase
takes full advantage of the level of reliability that Bazel able to offer. See
https://goo.gl/uQOE11 for more information.
#### Selection
Avoid using jars that bundle their dependencies. For example, a Maven jar for
the artifact com.initech:tps:1.0 should not contain a classes named
com.fakecorp.foo. Try to see if Initech distributes a tps jar that doesn't
bundle its dependencies. Then create a separate java_import_external() for each
one and have the first depend on the second.
Sometimes jars are distributed with their dependencies shaded. What this means
is that com.initech.tps will contain classes like
com.initech.tps.shade.com.fakecorp.foo. This is less problematic, since it
won't lead to mysterious classpath conflicts. But it can lead to inefficient
use of space and make the license of the the end product more difficult to
determine.
#### Licensing
The following values for the licenses field are typically used. If a jar
contains multiple works with difference licenses, then only the most
restrictive one is listed, and the rest are noted in accompanying comments.
The following are examples of how licenses could be categorized, ordered
by those with terms most permissive to least:
- **unencumbered**: CC0, Unlicense
- **permissive**: Beerware
- **notice**: Apache, MIT, X11, BSD, ISC, ZPL, Unicode, JSON, Artistic
- **reciprocal**: MPL, CPL, EPL, Eclipse, APSL, IBMPL, CDDL
- **restricted**: GPL, LGPL, OSL, Sleepycat, QTPL, Java, QMail, NPL
- **by_exception_only**: AGPL, WTFPL
### Naming
Bazel repository names must match the following pattern: `[_0-9A-Za-z]+`. To
choose an appropriate name based on a Maven group and artifact ID, we recommend
an algorithm https://gist.github.com/jart/41bfd977b913c2301627162f1c038e55 which
can be best explained by the following examples:
- com.google.guava:guava becomes com_google_guava
- commons-logging:commons-logging becomes commons_logging
- junit:junit becomes junit
Adopting this naming convention will help maximize the chances that your
codebase will be able to successfully interoperate with other Bazel codebases
using Java.
### Example
Here is an example of a best practice definition of Google's Guava library:
```python
java_import_external(
name = "com_google_guava",
licenses = ["notice"], # Apache 2.0
jar_urls = [
"http://bazel-mirror.storage.googleapis.com/repo1.maven.org/maven2/com/google/guava/guava/20.0/guava-20.0.jar",
"http://repo1.maven.org/maven2/com/google/guava/guava/20.0/guava-20.0.jar",
"http://maven.ibiblio.org/maven2/com/google/guava/guava/20.0/guava-20.0.jar",
],
jar_sha256 = "36a666e3b71ae7f0f0dca23654b67e086e6c93d192f60ba5dfd5519db6c288c8",
deps = [
"@com_google_code_findbugs_jsr305",
"@com_google_errorprone_error_prone_annotations",
],
)
java_import_external(
name = "com_google_code_findbugs_jsr305",
licenses = ["notice"], # BSD 3-clause
jar_urls = [
"http://bazel-mirror.storage.googleapis.com/repo1.maven.org/maven2/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar",
"http://repo1.maven.org/maven2/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar",
"http://maven.ibiblio.org/maven2/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar",
],
jar_sha256 = "905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed",
)
java_import_external(
name = "com_google_errorprone_error_prone_annotations",
licenses = ["notice"], # Apache 2.0
jar_sha256 = "e7749ffdf03fb8ebe08a727ea205acb301c8791da837fee211b99b04f9d79c46",
jar_urls = [
"http://bazel-mirror.storage.googleapis.com/repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.0.15/error_prone_annotations-2.0.15.jar",
"http://maven.ibiblio.org/maven2/com/google/errorprone/error_prone_annotations/2.0.15/error_prone_annotations-2.0.15.jar",
"http://repo1.maven.org/maven2/com/google/errorprone/error_prone_annotations/2.0.15/error_prone_annotations-2.0.15.jar",
],
)
```
### Annotation Processors
Defining jars that contain annotation processors requires a certain level of
trickery, which is best done by copying and pasting from codebases that have
already done it before. Please see the Google Nomulus and Bazel Closure Rules
codebases for examples in which java_import_external has been used to define
Dagger 2.0, AutoValue, and AutoFactory.
Please note that certain care needs to be taken into consideration regarding
whether or not these annotation processors generate actual API, or simply
generate code that implements them. See the Bazel documentation for further
information.
### Test Dependencies
It is strongly recommended that the `testonly_` attribute be specified on
libraries that are intended for testing purposes. This is passed along to the
generated `java_library` rule in order to ensure that test code remains
disjoint from production code.
### Provided Dependencies
The feature in Bazel most analagous to Maven's provided scope is the neverlink
attribute. This should be used in rare circumstances when a distributed jar
will be loaded into a runtime environment where certain dependencies can be
reasonably expected to already be provided.
"""
_HEADER = "# DO NOT EDIT: generated by java_import_external()"
_PASS_JAR_PROPS = (
"neverlink",
"testonly_",
"visibility",
"exports",
"runtime_deps",
"deps",
"tags",
)
_PASS_AAR_PROPS = (
"testonly_",
"visibility",
"exports",
"deps",
"tags",
)
def _java_import_external(repository_ctx):
"""Implementation of `java_import_external` rule."""
is_neverlink = repository_ctx.attr.neverlink if hasattr(repository_ctx.attr, "neverlink") else False
if (repository_ctx.attr.generated_linkable_rule_name and not is_neverlink):
fail("Only use generated_linkable_rule_name if neverlink is set")
packaging = repository_ctx.attr._packaging
name = repository_ctx.attr.generated_rule_name or repository_ctx.name
urls = repository_ctx.attr.jar_urls if packaging == "jar" else repository_ctx.attr.aar_urls
sha = repository_ctx.attr.jar_sha256 if packaging == "jar" else repository_ctx.attr.aar_sha256
path = repository_ctx.name + "." + packaging
for url in urls:
if url.endswith(".jar") or url.endswith(".aar"):
path = url[url.rindex("/") + 1:]
break
srcurls = repository_ctx.attr.srcjar_urls
srcsha = repository_ctx.attr.srcjar_sha256
srcpath = repository_ctx.name + "-src.jar" if srcurls else ""
for url in srcurls:
if url.endswith(".jar"):
srcpath = url[url.rindex("/") + 1:].replace("-sources.jar", "-src.jar")
break
lines = [_HEADER, ""]
if repository_ctx.attr.default_visibility:
lines.append("package(default_visibility = %s)" % (
repository_ctx.attr.default_visibility
))
lines.append("")
lines.append("licenses(%s)" % repr(repository_ctx.attr.licenses))
lines.append("")
if packaging == "jar":
lines.extend(_make_java_import(
name,
path,
srcpath,
repository_ctx.attr,
_PASS_JAR_PROPS,
))
if (is_neverlink and repository_ctx.attr.generated_linkable_rule_name):
lines.extend(_make_java_import(
repository_ctx.attr.generated_linkable_rule_name,
path,
srcpath,
repository_ctx.attr,
[p for p in _PASS_JAR_PROPS if p != "neverlink"],
))
elif packaging == "aar":
lines.extend(_make_aar_import(
name,
path,
srcpath,
repository_ctx.attr,
_PASS_AAR_PROPS,
))
else:
fail("Unsupported packaging type: %s" % packaging)
extra = repository_ctx.attr.extra_build_file_content
if extra:
lines.append(extra)
if not extra.endswith("\n"):
lines.append("")
repository_ctx.download(urls, path, sha)
if srcurls:
repository_ctx.download(srcurls, srcpath, srcsha)
repository_ctx.file("BUILD", "\n".join(lines))
repository_ctx.file("%s/BUILD" % packaging, "\n".join([
_HEADER,
"",
"package(default_visibility = %r)" % (
repository_ctx.attr.visibility or
repository_ctx.attr.default_visibility
),
"",
"alias(",
" name = \"%s\"," % packaging,
" actual = \"@%s\"," % repository_ctx.name,
")",
"",
]))
def _make_aar_import(name, path, srcpath, attrs, props):
lines = [
"aar_import(",
" name = %s," % repr(name),
" aar = %s," % repr(path),
]
return _append_import_rule(lines, srcpath, attrs, props)
def _make_java_import(name, path, srcpath, attrs, props):
lines = [
"java_import(",
" name = %s," % repr(name),
" jars = [%s]," % repr(path),
]
return _append_import_rule(lines, srcpath, attrs, props)
def _append_import_rule(lines, srcpath, attrs, props):
if srcpath:
lines.append(" srcjar = %s," % repr(srcpath))
for prop in props:
value = getattr(attrs, prop, None)
if value:
if prop.endswith("_"):
prop = prop[:-1]
lines.append(" %s = %s," % (prop, repr(value)))
lines.append(")")
lines.append("")
return lines
COMMON_ATTRS = {
"licenses": attr.string_list(
mandatory = True,
allow_empty = False,
),
"srcjar_urls": attr.string_list(),
"srcjar_sha256": attr.string(),
"deps": attr.string_list(),
"testonly_": attr.bool(),
"exports": attr.string_list(),
"generated_rule_name": attr.string(),
"generated_linkable_rule_name": attr.string(),
"default_visibility": attr.string_list(default = ["//visibility:public"]),
"extra_build_file_content": attr.string(),
}
JAVA_IMPORT_EXTERNAL_ATTRS = {
"jar_urls": attr.string_list(
mandatory = True,
allow_empty = False,
),
"jar_sha256": attr.string(mandatory = True),
"_packaging": attr.string(default = "jar"),
"neverlink": attr.bool(),
"runtime_deps": attr.string_list(),
}
JAVA_IMPORT_EXTERNAL_ATTRS.update(COMMON_ATTRS)
AAR_IMPORT_EXTERNAL_ATTRS = {
"aar_urls": attr.string_list(
mandatory = True,
allow_empty = False,
),
"aar_sha256": attr.string(mandatory = True),
"_packaging": attr.string(default = "aar"),
}
AAR_IMPORT_EXTERNAL_ATTRS.update(COMMON_ATTRS)
java_import_external = repository_rule(
attrs = JAVA_IMPORT_EXTERNAL_ATTRS,
implementation = _java_import_external,
)
aar_import_external = repository_rule(
attrs = AAR_IMPORT_EXTERNAL_ATTRS,
implementation = _java_import_external,
)