daml/bazel_tools/proto.bzl
Samir Talwar 9976b4cd50
Bazel: Factor out logic around Protobuf JARs. [KVL-714] (#8084)
* kvutils: Use ScalaPB to generate a Scala JAR for daml_kvutils.proto.

* Bazel: Delete the unused `da_java_binary` rule, and inline `_wrap_rule`.

* Bazel: Factor out Java/Scala protobuf class generation into a helper.

CHANGELOG_BEGIN
CHANGELOG_END

* daml-lf/archive: Use `proto_jars`.

* Bazel: Remove the visibility modifier from `proto_jars`.

It's too confusing. Just make everything public.

* daml-lf/archive: Push protobuf source tarballs into `proto_jars`.

* Bazel: Add comments to the various parts of `proto_jars`.

* daml-assistant: Do unpleasant things with `location` in Bazel.
2020-11-27 08:34:53 +00:00

260 lines
8.8 KiB
Python

# Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load("//bazel_tools:javadoc_library.bzl", "javadoc_library")
load("//bazel_tools:pkg.bzl", "pkg_empty_zip")
load("//bazel_tools:pom_file.bzl", "pom_file")
load("@io_bazel_rules_scala//scala:scala.bzl", "scala_library")
load("@os_info//:os_info.bzl", "is_windows")
load("@rules_pkg//:pkg.bzl", "pkg_tar")
load("@rules_proto//proto:defs.bzl", "proto_library")
# taken from rules_proto:
# https://github.com/stackb/rules_proto/blob/f5d6eea6a4528bef3c1d3a44d486b51a214d61c2/compile.bzl#L369-L393
def get_plugin_runfiles(tool, plugin_runfiles):
"""Gather runfiles for a plugin.
"""
files = []
if not tool:
return files
info = tool[DefaultInfo]
if not info:
return files
if info.files:
files += info.files.to_list()
if info.default_runfiles:
runfiles = info.default_runfiles
if runfiles.files:
files += runfiles.files.to_list()
if info.data_runfiles:
runfiles = info.data_runfiles
if runfiles.files:
files += runfiles.files.to_list()
if plugin_runfiles:
for target in plugin_runfiles:
files += target.files.to_list()
return files
def _proto_gen_impl(ctx):
src_descs = [src[ProtoInfo].direct_descriptor_set for src in ctx.attr.srcs]
dep_descs = [depset for dep in ctx.attr.deps for depset in dep[ProtoInfo].transitive_descriptor_sets.to_list()]
descriptors = src_descs + dep_descs
sources_out = ctx.actions.declare_directory(ctx.attr.name + "-sources")
descriptor_set_delim = "\\;" if _is_windows(ctx) else ":"
args = []
args += [
"--descriptor_set_in=" + descriptor_set_delim.join([d.path for d in descriptors]),
]
args += [
"--{}_out={}:{}".format(ctx.attr.plugin_name, ",".join(ctx.attr.plugin_options), sources_out.path),
]
plugins = []
plugin_runfiles = []
if ctx.attr.plugin_name not in ["java", "python"]:
plugins = [ctx.executable.plugin_exec]
plugin_runfiles = get_plugin_runfiles(ctx.attr.plugin_exec, ctx.attr.plugin_runfiles)
args += [
"--plugin=protoc-gen-{}={}".format(ctx.attr.plugin_name, ctx.executable.plugin_exec.path),
]
inputs = []
for src in ctx.attr.srcs:
src_root = src[ProtoInfo].proto_source_root
for direct_source in src[ProtoInfo].direct_sources:
path = ""
# in some cases the paths of src_root and direct_source are only partially
# overlapping. the following for loop finds the maximum overlap of these two paths
for i in range(len(src_root) + 1):
if direct_source.path.startswith(src_root[-i:]):
path = direct_source.path[i:]
else:
# this noop is needed to make bazel happy
noop = ""
path = direct_source.short_path if not path else path
path = path[1:] if path.startswith("/") else path
inputs += [path]
args += inputs
posix = ctx.toolchains["@rules_sh//sh/posix:toolchain_type"]
ctx.actions.run_shell(
mnemonic = "ProtoGen",
outputs = [sources_out],
inputs = descriptors + [ctx.executable.protoc] + plugin_runfiles,
command = posix.commands["mkdir"] + " -p " + sources_out.path + " && " + ctx.executable.protoc.path + " " + " ".join(args),
tools = plugins,
)
# since we only have the output directory of the protoc compilation,
# we need to find all the files below sources_out and add them to the zipper args file
zipper_args_file = ctx.actions.declare_file(ctx.label.name + ".zipper_args")
ctx.actions.run_shell(
mnemonic = "CreateZipperArgsFile",
outputs = [zipper_args_file],
inputs = [sources_out],
command = "{find} -L {src_path} -type f | {sed} -E 's#^{src_path}/(.*)$#\\1={src_path}/\\1#' | {sort} > {args_file}".format(
find = posix.commands["find"],
sed = posix.commands["sed"],
sort = posix.commands["sort"],
src_path = sources_out.path,
args_file = zipper_args_file.path,
),
progress_message = "zipper_args_file %s" % zipper_args_file.path,
)
# Call zipper to create srcjar
zipper_args = ctx.actions.args()
zipper_args.add("c")
zipper_args.add(ctx.outputs.out.path)
zipper_args.add("@%s" % zipper_args_file.path)
ctx.actions.run(
executable = ctx.executable._zipper,
inputs = [sources_out, zipper_args_file],
outputs = [ctx.outputs.out],
arguments = [zipper_args],
progress_message = "srcjar %s" % ctx.outputs.out.short_path,
)
proto_gen = rule(
implementation = _proto_gen_impl,
attrs = {
"srcs": attr.label_list(allow_files = True),
"deps": attr.label_list(providers = [ProtoInfo]),
"plugin_name": attr.string(),
"plugin_exec": attr.label(
cfg = "host",
executable = True,
),
"plugin_options": attr.string_list(),
"plugin_runfiles": attr.label_list(
default = [],
allow_files = True,
),
"protoc": attr.label(
default = Label("@com_google_protobuf//:protoc"),
cfg = "host",
allow_files = True,
executable = True,
),
"_zipper": attr.label(
default = Label("@bazel_tools//tools/zip:zipper"),
cfg = "host",
executable = True,
allow_files = True,
),
},
outputs = {
"out": "%{name}.srcjar",
},
output_to_genfiles = True,
toolchains = ["@rules_sh//sh/posix:toolchain_type"],
)
def _is_windows(ctx):
return ctx.configuration.host_path_separator == ";"
def maven_tags(group, artifact_prefix, artifact_suffix):
if group and artifact_prefix and artifact_suffix:
return ["maven_coordinates=%s:%s-%s:__VERSION__" % (group, artifact_prefix, artifact_suffix)]
else:
return []
def proto_jars(
name,
srcs,
strip_import_prefix = "",
deps = [],
proto_deps = [],
java_deps = [],
scala_deps = [],
file_root = None,
javadoc_root_packages = [],
maven_group = None,
maven_artifact_prefix = None,
maven_java_artifact_suffix = "java-proto"):
# Tarball containing the *.proto files.
pkg_tar(
name = "%s_src" % name,
srcs = srcs,
extension = "tar.gz",
strip_prefix = strip_import_prefix,
package_dir = file_root,
visibility = ["//visibility:public"],
)
# Compiled protobufs. Used in subsequent targets.
proto_library(
name = name,
srcs = srcs,
strip_import_prefix = strip_import_prefix,
visibility = ["//visibility:public"],
deps = deps + proto_deps,
)
# JAR containing the generated Java bindings.
native.java_proto_library(
name = "%s_java" % name,
tags = maven_tags(maven_group, maven_artifact_prefix, maven_java_artifact_suffix),
visibility = ["//visibility:public"],
deps = [":%s" % name] + java_deps,
)
if maven_group and maven_artifact_prefix:
pom_file(
name = "%s_java_pom" % name,
target = ":%s_java" % name,
visibility = ["//visibility:public"],
)
if javadoc_root_packages:
javadoc_library(
name = "%s_java_javadoc" % name,
srcs = [":%s_java" % name],
root_packages = javadoc_root_packages,
visibility = ["//visibility:public"],
deps = ["@maven//:com_google_protobuf_protobuf_java"],
) if not is_windows else None
else:
# Create an empty Javadoc JAR for uploading proto JARs to Maven Central.
# We don't need to create an empty JAR file for sources, because `java_proto_library`
# creates a source JAR automatically.
pkg_empty_zip(
name = "%s_java_javadoc" % name,
out = "%s_java_javadoc.jar" % name,
)
# JAR containing the generated Scala bindings.
proto_gen(
name = "%s_scala_sources" % name,
srcs = [":%s" % name],
plugin_exec = "//scala-protoc-plugins/scalapb:protoc-gen-scalapb",
plugin_name = "scalapb",
visibility = ["//visibility:public"],
deps = deps + proto_deps,
)
scala_library(
name = "%s_scala" % name,
srcs = [":%s_scala_sources" % name],
unused_dependency_checker_mode = "error",
visibility = ["//visibility:public"],
deps = [
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:com_thesamet_scalapb_lenses_2_12",
"@maven//:com_thesamet_scalapb_scalapb_runtime_2_12",
] + ["%s_scala" % label for label in proto_deps] + scala_deps,
)