aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/build_defs/repo/java.bzl
diff options
context:
space:
mode:
authorGravatar jart <jart@google.com>2017-08-08 23:11:45 +0200
committerGravatar Marcel Hlopko <hlopko@google.com>2017-08-09 11:33:52 +0200
commit062fe70189fc622285833311d241021be313680b (patch)
treec5a0d1b3b2dc31457cb01bf2bbe920dc76cdc74a /tools/build_defs/repo/java.bzl
parentae93cdf5f2377f9903a3003396b499787e334165 (diff)
Introduce java_import_external
This Skylark rule is a replacement for maven_jar. See also #1410 PiperOrigin-RevId: 164642813
Diffstat (limited to 'tools/build_defs/repo/java.bzl')
-rw-r--r--tools/build_defs/repo/java.bzl279
1 files changed, 279 insertions, 0 deletions
diff --git a/tools/build_defs/repo/java.bzl b/tools/build_defs/repo/java.bzl
new file mode 100644
index 0000000000..7f3657bc69
--- /dev/null
+++ b/tools/build_defs/repo/java.bzl
@@ -0,0 +1,279 @@
+# Copyright 2017 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
+7. Prefer http over https unless curl -I says the http URL redirects to https
+
+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_PROPS = (
+ "neverlink",
+ "testonly_",
+ "visibility",
+ "exports",
+ "runtime_deps",
+ "deps",
+)
+
+def _java_import_external(repository_ctx):
+ """Implementation of `java_import_external` rule."""
+ if (repository_ctx.attr.generated_linkable_rule_name and
+ not repository_ctx.attr.neverlink):
+ fail("Only use generated_linkable_rule_name if neverlink is set")
+ name = repository_ctx.attr.generated_rule_name or repository_ctx.name
+ urls = repository_ctx.attr.jar_urls
+ sha = repository_ctx.attr.jar_sha256
+ path = repository_ctx.name + ".jar"
+ for url in urls:
+ if url.endswith(".jar"):
+ 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("")
+ lines.extend(_make_java_import(
+ name, path, srcpath, repository_ctx.attr, _PASS_PROPS))
+ if (repository_ctx.attr.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_PROPS if p != "neverlink"]))
+ 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("jar/BUILD", "\n".join([
+ _HEADER,
+ "",
+ "package(default_visibility = %r)" % (
+ repository_ctx.attr.visibility or
+ repository_ctx.attr.default_visibility),
+ "",
+ "alias(",
+ " name = \"jar\",",
+ " actual = \"@%s\"," % repository_ctx.name,
+ ")",
+ "",
+ ]))
+
+def _make_java_import(name, path, srcpath, attrs, props):
+ lines = [
+ "java_import(",
+ " name = %s," % repr(name),
+ " jars = [%s]," % repr(path),
+ ]
+ 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
+
+java_import_external = repository_rule(
+ implementation=_java_import_external,
+ attrs={
+ "licenses": attr.string_list(mandatory=True, allow_empty=False),
+ "jar_urls": attr.string_list(mandatory=True, allow_empty=False),
+ "jar_sha256": attr.string(mandatory=True),
+ "srcjar_urls": attr.string_list(),
+ "srcjar_sha256": attr.string(),
+ "deps": attr.string_list(),
+ "runtime_deps": attr.string_list(),
+ "testonly_": attr.bool(),
+ "exports": attr.string_list(),
+ "neverlink": attr.bool(),
+ "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(),
+ })