# 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 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. """ load("@bazel_tools//tools/build_defs/repo:jvm.bzl", "jvm_import_external") def java_import_external(jar_sha256, jar_urls, **kwargs): jvm_import_external( rule_name = "java_import", artifact_sha256 = jar_sha256, artifact_urls = jar_urls, **kwargs )