From 45c2dce449eb7d4b0934471c3d8a8bd3bc635f31 Mon Sep 17 00:00:00 2001 From: natans Date: Tue, 29 May 2018 08:23:41 -0700 Subject: Generaliaze java_import_external to jvm_import_external This PR copies "upstream" a change made to java_import_external in`rules_scala` (see [PR](https://github.com/bazelbuild/rules_scala/pull/473)) This change was originally proposed by @dslomov [here](https://github.com/bazelbuild/bazel/issues/3528) (search for 'jvm_import_external') java_import_external was changed to `jvm_import_external` 'template like' rule + `java_import_external` macro in order to allow for other jvm languages (e.g. scala and kotlin) to utilize the 'import_external' functionality without copying the boiler plate again and again. This has already been used in `rules_scala` with the introduction of `scala_import_external` and `scala_maven_import_external` In addition to the `import rule name`, `jvm_import_external` can also be called with custom attributes needed by the underlying import rules, as well as a custom load statement. `java_import_external` is used as a macro in rules_scala to make sure it's still functioning properly after the change. `jvm_maven_import_external` exposes maven artifact terminology. This will also allow to create a `maven_import_external` macro that will delegate to `jvm_maven_import_external` with a `maven_import` rule which will have `MavenCoordinates` Provider as discussed [here](https://github.com/bazelbuild/bazel/issues/4654) Closes #5068. PiperOrigin-RevId: 198398621 --- tools/build_defs/repo/java.bzl | 116 ++------------------------ tools/build_defs/repo/jvm.bzl | 185 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 108 deletions(-) create mode 100644 tools/build_defs/repo/jvm.bzl diff --git a/tools/build_defs/repo/java.bzl b/tools/build_defs/repo/java.bzl index 559cc77a04..9803658204 100644 --- a/tools/build_defs/repo/java.bzl +++ b/tools/build_defs/repo/java.bzl @@ -169,111 +169,11 @@ 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", - "tags", -) - -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(), - }) +load("@bazel_tools//tools/build_defs/repo:jvm.bzl", "jvm_import_external") + +def java_import_external(jar_sha256, **kwargs): + jvm_import_external( + rule_name = "java_import", + jar_sha256 = jar_sha256, + **kwargs + ) diff --git a/tools/build_defs/repo/jvm.bzl b/tools/build_defs/repo/jvm.bzl new file mode 100644 index 0000000000..f0d80ffe3e --- /dev/null +++ b/tools/build_defs/repo/jvm.bzl @@ -0,0 +1,185 @@ +# 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. + +""" +'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 +""" + +_HEADER = "# DO NOT EDIT: generated by jvm_import_external()" +_PASS_PROPS = ( + "neverlink", + "testonly_", + "visibility", + "exports", + "runtime_deps", + "deps", + "tags", +) + +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.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.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( + repository_ctx.attr.rule_name, name, path, srcpath, repository_ctx.attr, _PASS_PROPS, repository_ctx.attr.additional_rule_attrs)) + if (repository_ctx.attr.neverlink and + repository_ctx.attr.generated_linkable_rule_name): + lines.extend(_serialize_given_rule_import( + repository_ctx.attr.rule_name, + repository_ctx.attr.generated_linkable_rule_name, + path, + srcpath, + repository_ctx.attr, + [p for p in _PASS_PROPS if p != "neverlink"], + repository_ctx.attr.additional_rule_attrs)) + 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 _convert_to_url(artifact, server_urls): + parts = artifact.split(":") + group_id_part = parts[0].replace(".","/") + artifact_id = parts[1] + version = parts[2] + packaging = "jar" + classifier_part = "" + if len(parts) == 4: + packaging = parts[2] + version = parts[3] + elif len(parts) == 5: + packaging = parts[2] + classifier_part = "-"+parts[3] + version = parts[4] + + final_name = artifact_id + "-" + version + classifier_part + "." + packaging + url_suffix = group_id_part+"/"+artifact_id + "/" + 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, name, path, srcpath, attrs, props, additional_rule_attrs): + lines = [ + "%s(" % rule_name, + " 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))) + 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( + implementation=_jvm_import_external, + attrs={ + "rule_name": attr.string(mandatory=True), + "licenses": attr.string_list(mandatory=True, allow_empty=False), + "jar_urls": attr.string_list(mandatory=True, allow_empty=False), + "jar_sha256": attr.string(), + "rule_load": attr.string(), + "additional_rule_attrs": attr.string_dict(), + "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(), + }) + +def jvm_maven_import_external(artifact, server_urls, **kwargs): + jvm_import_external( + jar_urls = _convert_to_url(artifact, server_urls), + **kwargs + ) \ No newline at end of file -- cgit v1.2.3