|  | # 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. | 
|  |  | 
|  | # WARNING: | 
|  | # https://github.com/bazelbuild/bazel/issues/17713 | 
|  | # .bzl files in this package (tools/build_defs/repo) are evaluated | 
|  | # in a Starlark environment without "@_builtins" injection, and must not refer | 
|  | # to symbols associated with build/workspace .bzl files | 
|  |  | 
|  | """ | 
|  | 'jvm_import_external' offers additional functionality above what maven_jar has to offer. | 
|  | In addition to downloading the jars, it allows to define this jar's dependencies. | 
|  | thus it enables the explicit definition of the entire transitive dependency graph. | 
|  |  | 
|  | The rule achieves this by writing 'import' build rules in BUILD files next to the downloaded jars. | 
|  | The name of the underlying 'import' rule needs to be specified. | 
|  | An optional 'load' statement can also be provided, along with any other relevant custom attribute. | 
|  | These import rules must have the following attributes: | 
|  | - "jars" | 
|  | - "deps" | 
|  | - "runtime_deps" | 
|  | - "exports" | 
|  |  | 
|  | the following macros are defined below that utilize jvm_import_external: | 
|  |  | 
|  | - jvm_maven_import_external - offers a 'maven' like api for identifying jars using 'artifact' format | 
|  | - java_import_external - uses `java_import` as the underlying build rule | 
|  | """ | 
|  |  | 
|  | load( | 
|  | ":cache.bzl", | 
|  | "CANONICAL_ID_DOC", | 
|  | "DEFAULT_CANONICAL_ID_ENV", | 
|  | "get_default_canonical_id", | 
|  | ) | 
|  | load( | 
|  | ":utils.bzl", | 
|  | "get_auth", | 
|  | ) | 
|  |  | 
|  | _HEADER = "# DO NOT EDIT: generated by jvm_import_external()" | 
|  |  | 
|  | _PASS_PROPS = ( | 
|  | "neverlink", | 
|  | "testonly_", | 
|  | "visibility", | 
|  | "exports", | 
|  | "runtime_deps", | 
|  | "deps", | 
|  | "tags", | 
|  | ) | 
|  |  | 
|  | _FETCH_SOURCES_ENV_VAR = "BAZEL_JVM_FETCH_SOURCES" | 
|  |  | 
|  | def _jvm_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.artifact_urls | 
|  | sha = repository_ctx.attr.artifact_sha256 | 
|  | extension = repository_ctx.attr.rule_metadata["extension"] | 
|  | file_extension = "." + extension | 
|  | path = repository_ctx.name + file_extension | 
|  | for url in urls: | 
|  | if url.endswith(file_extension): | 
|  | 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(file_extension): | 
|  | srcpath = url[url.rindex("/") + 1:].replace("-sources.jar", "-src.jar") | 
|  | break | 
|  | lines = [_HEADER, ""] | 
|  | if repository_ctx.attr.rule_load: | 
|  | lines.append(repository_ctx.attr.rule_load) | 
|  | lines.append("") | 
|  | 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(_serialize_given_rule_import( | 
|  | name = name, | 
|  | additional_rule_attrs = repository_ctx.attr.additional_rule_attrs, | 
|  | attrs = repository_ctx.attr, | 
|  | import_attr = repository_ctx.attr.rule_metadata["import_attr"], | 
|  | path = path, | 
|  | props = _PASS_PROPS, | 
|  | rule_name = repository_ctx.attr.rule_name, | 
|  | srcpath = srcpath, | 
|  | )) | 
|  | if (repository_ctx.attr.neverlink and | 
|  | repository_ctx.attr.generated_linkable_rule_name): | 
|  | lines.extend(_serialize_given_rule_import( | 
|  | name = repository_ctx.attr.generated_linkable_rule_name, | 
|  | additional_rule_attrs = repository_ctx.attr.additional_rule_attrs, | 
|  | attrs = repository_ctx.attr, | 
|  | import_attr = repository_ctx.attr.rule_metadata["import_attr"], | 
|  | path = path, | 
|  | props = [p for p in _PASS_PROPS if p != "neverlink"], | 
|  | rule_name = repository_ctx.attr.rule_name, | 
|  | srcpath = srcpath, | 
|  | )) | 
|  | 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, | 
|  | canonical_id = repository_ctx.attr.canonical_id or get_default_canonical_id(repository_ctx, urls), | 
|  | auth = get_auth(repository_ctx, urls), | 
|  | ) | 
|  | if srcurls and _should_fetch_sources_in_current_env(repository_ctx): | 
|  | repository_ctx.download( | 
|  | srcurls, | 
|  | srcpath, | 
|  | srcsha, | 
|  | canonical_id = repository_ctx.attr.canonical_id, | 
|  | auth = get_auth(repository_ctx, srcurls), | 
|  | ) | 
|  | repository_ctx.file("BUILD", "\n".join(lines)) | 
|  | repository_ctx.file("%s/BUILD" % extension, "\n".join([ | 
|  | _HEADER, | 
|  | "", | 
|  | "package(default_visibility = %r)" % ( | 
|  | repository_ctx.attr.visibility or | 
|  | repository_ctx.attr.default_visibility | 
|  | ), | 
|  | "", | 
|  | "alias(", | 
|  | "    name = \"%s\"," % extension, | 
|  | "    actual = \"//:%s\"," % name, | 
|  | ")", | 
|  | "", | 
|  | "filegroup(", | 
|  | "    name = \"file\",", | 
|  | "    srcs = [\"//:%s\"]," % path, | 
|  | ")", | 
|  | ])) | 
|  |  | 
|  | def _should_fetch_sources_in_current_env(repository_ctx): | 
|  | return repository_ctx.os.environ.get(_FETCH_SOURCES_ENV_VAR, "true").lower() == "true" | 
|  |  | 
|  | def _decode_maven_coordinates(artifact, default_packaging): | 
|  | parts = artifact.split(":") | 
|  | group_id = parts[0] | 
|  | artifact_id = parts[1] | 
|  | version = parts[2] | 
|  | classifier = None | 
|  | packaging = default_packaging | 
|  | if len(parts) == 4: | 
|  | packaging = parts[2] | 
|  | version = parts[3] | 
|  | elif len(parts) == 5: | 
|  | packaging = parts[2] | 
|  | classifier = parts[3] | 
|  | version = parts[4] | 
|  |  | 
|  | return struct( | 
|  | group_id = group_id, | 
|  | artifact_id = artifact_id, | 
|  | version = version, | 
|  | classifier = classifier, | 
|  | packaging = packaging, | 
|  | ) | 
|  |  | 
|  | # This method is public for usage in android.bzl macros | 
|  | def convert_artifact_coordinate_to_urls(artifact, server_urls, packaging): | 
|  | """This function converts a Maven artifact coordinate into URLs.""" | 
|  | coordinates = _decode_maven_coordinates(artifact, packaging) | 
|  | return _convert_coordinates_to_urls(coordinates, server_urls) | 
|  |  | 
|  | def _convert_coordinates_to_urls(coordinates, server_urls): | 
|  | group_id = coordinates.group_id.replace(".", "/") | 
|  | classifier = coordinates.classifier | 
|  |  | 
|  | if classifier: | 
|  | classifier = "-" + classifier | 
|  | else: | 
|  | classifier = "" | 
|  |  | 
|  | final_name = coordinates.artifact_id + "-" + coordinates.version + classifier + "." + coordinates.packaging | 
|  | url_suffix = group_id + "/" + coordinates.artifact_id + "/" + coordinates.version + "/" + final_name | 
|  |  | 
|  | urls = [] | 
|  | for server_url in server_urls: | 
|  | urls.append(_concat_with_needed_slash(server_url, url_suffix)) | 
|  | return urls | 
|  |  | 
|  | def _concat_with_needed_slash(server_url, url_suffix): | 
|  | if server_url.endswith("/"): | 
|  | return server_url + url_suffix | 
|  | else: | 
|  | return server_url + "/" + url_suffix | 
|  |  | 
|  | def _serialize_given_rule_import(rule_name, import_attr, name, path, srcpath, attrs, props, additional_rule_attrs): | 
|  | lines = [ | 
|  | "%s(" % rule_name, | 
|  | "    name = %s," % repr(name), | 
|  | "    " + import_attr % 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))) | 
|  | for attr_key in additional_rule_attrs: | 
|  | lines.append("    %s = %s," % (attr_key, additional_rule_attrs[attr_key])) | 
|  | lines.append(")") | 
|  | lines.append("") | 
|  | return lines | 
|  |  | 
|  | jvm_import_external = repository_rule( | 
|  | attrs = { | 
|  | "rule_name": attr.string(mandatory = True), | 
|  | "licenses": attr.string_list(default = ["none"]), | 
|  | "artifact_urls": attr.string_list( | 
|  | mandatory = True, | 
|  | allow_empty = False, | 
|  | ), | 
|  | "artifact_sha256": attr.string(), | 
|  | "rule_metadata": attr.string_dict( | 
|  | default = { | 
|  | "extension": "jar", | 
|  | "import_attr": "jars = [%s]", | 
|  | }, | 
|  | ), | 
|  | "rule_load": attr.string(), | 
|  | "additional_rule_attrs": attr.string_dict(), | 
|  | "srcjar_urls": attr.string_list(), | 
|  | "srcjar_sha256": attr.string(), | 
|  | "canonical_id": attr.string( | 
|  | doc = CANONICAL_ID_DOC, | 
|  | ), | 
|  | "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(), | 
|  | }, | 
|  | environ = [ | 
|  | _FETCH_SOURCES_ENV_VAR, | 
|  | DEFAULT_CANONICAL_ID_ENV, | 
|  | ], | 
|  | implementation = _jvm_import_external, | 
|  | ) | 
|  |  | 
|  | def jvm_maven_import_external( | 
|  | name, | 
|  | artifact, | 
|  | server_urls, | 
|  | fetch_sources = False, | 
|  | **kwargs): | 
|  | if kwargs.get("srcjar_urls") and fetch_sources: | 
|  | fail("Either use srcjar_urls or fetch_sources but not both") | 
|  |  | 
|  | coordinates = _decode_maven_coordinates(artifact, default_packaging = "jar") | 
|  |  | 
|  | jar_urls = _convert_coordinates_to_urls(coordinates, server_urls) | 
|  |  | 
|  | srcjar_urls = kwargs.pop("srcjar_urls", None) | 
|  |  | 
|  | rule_name = kwargs.pop("rule_name", "java_import") | 
|  |  | 
|  | if fetch_sources: | 
|  | src_coordinates = struct( | 
|  | group_id = coordinates.group_id, | 
|  | artifact_id = coordinates.artifact_id, | 
|  | version = coordinates.version, | 
|  | classifier = "sources", | 
|  | packaging = "jar", | 
|  | ) | 
|  |  | 
|  | srcjar_urls = _convert_coordinates_to_urls(src_coordinates, server_urls) | 
|  |  | 
|  | tags = kwargs.pop("tags", []) | 
|  | tags.append("maven_coordinates=" + artifact) | 
|  |  | 
|  | jvm_import_external( | 
|  | name = name, | 
|  | generated_rule_name = kwargs.pop("generated_rule_name", name), | 
|  | artifact_urls = jar_urls, | 
|  | srcjar_urls = srcjar_urls, | 
|  | canonical_id = artifact, | 
|  | rule_name = rule_name, | 
|  | tags = tags, | 
|  | **kwargs | 
|  | ) |