blob: eb2cdee79a20c8fd0f3c95907e4865a8c9b3079e [file] [log] [blame]
# Copyright 2020 Google LLC
#
# 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
#
# https://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.
load("@rules_gapic//:gapic.bzl", "proto_custom_library")
NO_GRPC_CONFIG_ALLOWLIST = ["library"]
def _java_gapic_postprocess_srcjar_impl(ctx):
gapic_srcjar = ctx.file.gapic_srcjar
output_srcjar_name = ctx.label.name
output_main = ctx.outputs.main
output_test = ctx.outputs.test
output_samples = ctx.outputs.samples
output_resource_name = ctx.outputs.resource_name
formatter = ctx.executable.formatter
output_dir_name = ctx.label.name
output_dir_path = "%s/%s" % (output_main.dirname, output_dir_name)
script = """
unzip -q {gapic_srcjar}
# Sync'd to the output file name in Writer.java.
unzip -q temp-codegen.srcjar -d {output_dir_path}
# This may fail if there are spaces and/or too many files (exceed max length of command length).
{formatter} --replace $(find {output_dir_path} -type f -printf "%p ")
WORKING_DIR=`pwd`
# Main source files.
cd {output_dir_path}/src/main/java
zip -r $WORKING_DIR/{output_srcjar_name}.srcjar ./
# Resource name source files.
PROTO_DIR=$WORKING_DIR/{output_dir_path}/proto/src/main/java
PROTO_SRCJAR=$WORKING_DIR/{output_srcjar_name}-resource-name.srcjar
if [ ! -d $PROTO_DIR ]
then
# Some APIs don't have resource name helpers, like BigQuery v2.
# Create an empty file so we can finish building. Gating the resource name rule definition
# on file existences go against Bazel's design patterns, so we'll simply delete all empty
# files during the final packaging process (see java_gapic_pkg.bzl)
mkdir -p $PROTO_DIR
touch $PROTO_DIR/PlaceholderFile.java
fi
cd $WORKING_DIR/{output_dir_path}/proto/src/main/java
zip -r $PROTO_SRCJAR ./
# Test source files.
cd $WORKING_DIR/{output_dir_path}/src/test/java
zip -r $WORKING_DIR/{output_srcjar_name}-tests.srcjar ./
# Sample source files.
cd $WORKING_DIR/{output_dir_path}/samples/snippets/generated/src/main/java
zip -r $WORKING_DIR/{output_srcjar_name}-samples.srcjar ./
cd $WORKING_DIR
mv {output_srcjar_name}.srcjar {output_main}
mv {output_srcjar_name}-resource-name.srcjar {output_resource_name}
mv {output_srcjar_name}-tests.srcjar {output_test}
mv {output_srcjar_name}-samples.srcjar {output_samples}
""".format(
gapic_srcjar = gapic_srcjar.path,
output_srcjar_name = output_srcjar_name,
formatter = formatter,
output_dir_name = output_dir_name,
output_dir_path = output_dir_path,
output_main = output_main.path,
output_resource_name = output_resource_name.path,
output_test = output_test.path,
output_samples = output_samples.path,
)
ctx.actions.run_shell(
inputs = [gapic_srcjar],
tools = [formatter],
command = script,
outputs = [output_main, output_resource_name, output_test, output_samples],
)
_java_gapic_postprocess_srcjar = rule(
attrs = {
"gapic_srcjar": attr.label(mandatory = True, allow_single_file = True),
"formatter": attr.label(
default = Label("//:google_java_format_binary"),
executable = True,
cfg = "host",
),
},
outputs = {
"main": "%{name}.srcjar",
"resource_name": "%{name}-resource-name.srcjar",
"test": "%{name}-test.srcjar",
"samples": "%{name}-samples.srcjar",
},
implementation = _java_gapic_postprocess_srcjar_impl,
)
def _java_gapic_samples_srcjar_impl(ctx):
gapic_srcjar = ctx.file.gapic_srcjar
output_srcjar_name = ctx.label.name
output_samples = ctx.outputs.samples
formatter = ctx.executable.formatter
output_dir_name = ctx.label.name
output_dir_path = "%s/%s" % (output_samples.dirname, output_dir_name)
script = """
unzip -q {gapic_srcjar}
# Sync'd to the output file name in Writer.java.
unzip -q temp-codegen.srcjar -d {output_dir_path}
# This may fail if there are spaces and/or too many files (exceed max length of command length).
{formatter} --replace $(find {output_dir_path} -type f -printf "%p ")
WORKING_DIR=`pwd`
# Sample source files.
cd $WORKING_DIR/{output_dir_path}/samples/snippets/generated/src/main/java
zip -r $WORKING_DIR/{output_srcjar_name}-samples.srcjar ./
cd $WORKING_DIR
mv {output_srcjar_name}-samples.srcjar {output_samples}
""".format(
gapic_srcjar = gapic_srcjar.path,
output_srcjar_name = output_srcjar_name,
formatter = formatter,
output_dir_name = output_dir_name,
output_dir_path = output_dir_path,
output_samples = output_samples.path,
)
ctx.actions.run_shell(
inputs = [gapic_srcjar],
tools = [formatter],
command = script,
outputs = [output_samples],
)
_java_gapic_samples_srcjar = rule(
attrs = {
"gapic_srcjar": attr.label(mandatory = True, allow_single_file = True),
"formatter": attr.label(
default = Label("//:google_java_format_binary"),
executable = True,
cfg = "host",
),
},
outputs = {
"samples": "%{name}-samples.srcjar",
},
implementation = _java_gapic_samples_srcjar_impl,
)
def _extract_common_proto_dep(dep):
return dep[dep.index("/"):] if "//google" in dep else dep
def _append_dep_without_duplicates(dest_deps, new_deps):
"""
Appends new_deps into dest_deps only if elements in new_deps
are not already present in dest_deps.
A workaround for the lack of sets in skylark.
"""
# Naive dep checking, since the source (i.e. prefixed) repo can vary.
# Examine only "//google"-prefixed targets, since common proto deps
# are more likely to be duplicated.
processed_dest_deps = [_extract_common_proto_dep(dep) for dep in dest_deps]
processed_new_deps = [_extract_common_proto_dep(dep) for dep in new_deps]
for i in range(len(new_deps)):
if processed_new_deps[i] not in processed_dest_deps:
dest_deps.append(new_deps[i])
return dest_deps
def _java_gapic_srcjar(
name,
srcs,
grpc_service_config,
gapic_yaml,
service_yaml,
# possible values are: "grpc", "rest", "grpc+rest"
transport,
rest_numeric_enums,
# Can be used to provide a java_library with a customized generator,
# like the one which dumps descriptor to a file for future debugging.
java_generator_name = "java_gapic",
**kwargs):
file_args_dict = {}
if grpc_service_config:
file_args_dict[grpc_service_config] = "grpc-service-config"
elif not transport or transport == "grpc":
for keyword in NO_GRPC_CONFIG_ALLOWLIST:
if keyword not in name:
fail("Missing a gRPC service config file")
if gapic_yaml:
file_args_dict[gapic_yaml] = "gapic-config"
if service_yaml:
file_args_dict[service_yaml] = "api-service-config"
opt_args = []
if transport:
opt_args.append("transport=%s" % transport)
if rest_numeric_enums:
opt_args.append("rest-numeric-enums")
# Produces the GAPIC metadata file if this flag is set. to any value.
# Protoc invocation: --java_gapic_opt=metadata
plugin_args = ["metadata"]
proto_custom_library(
name = name,
deps = srcs,
plugin = Label("@gapic_generator_java//:protoc-gen-%s" % java_generator_name),
plugin_args = plugin_args,
plugin_file_args = {},
opt_file_args = file_args_dict,
output_type = java_generator_name,
output_suffix = ".srcjar",
opt_args = opt_args,
**kwargs
)
def java_gapic_library(
name,
srcs,
grpc_service_config = None,
gapic_yaml = None,
service_yaml = None,
deps = [],
test_deps = [],
# possible values are: "grpc", "rest", "grpc+rest"
transport = None,
rest_numeric_enums = False,
**kwargs):
srcjar_name = name + "_srcjar"
raw_srcjar_name = srcjar_name + "_raw"
_java_gapic_srcjar(
name = raw_srcjar_name,
srcs = srcs,
grpc_service_config = grpc_service_config,
gapic_yaml = gapic_yaml,
service_yaml = service_yaml,
transport = transport,
rest_numeric_enums = rest_numeric_enums,
java_generator_name = "java_gapic",
**kwargs
)
_java_gapic_postprocess_srcjar(
name = srcjar_name,
gapic_srcjar = "%s.srcjar" % raw_srcjar_name,
**kwargs
)
_java_gapic_samples_srcjar(
name = "%s_samples" % name,
gapic_srcjar = "%s.srcjar" % raw_srcjar_name,
**kwargs
)
resource_name_name = "%s_resource_name" % name
resource_name_deps = [resource_name_name]
native.java_library(
name = resource_name_name,
srcs = ["%s-resource-name.srcjar" % srcjar_name],
deps = [
"@com_google_api_api_common//jar",
"@com_google_guava_guava//jar",
"@javax_annotation_javax_annotation_api//jar",
],
**kwargs
)
# General additional deps.
actual_deps = deps + resource_name_deps + [
"@com_google_googleapis//google/rpc:rpc_java_proto",
"@com_google_googleapis//google/longrunning:longrunning_java_proto",
"@com_google_protobuf//:protobuf_java",
"@com_google_api_api_common//jar",
"@com_google_api_gax_java//gax:gax",
"@com_google_guava_guava//jar",
"@com_google_code_findbugs_jsr305//jar",
"@org_threeten_threetenbp//jar",
"@io_opencensus_opencensus_api//jar",
"@com_google_auth_google_auth_library_credentials//jar",
"@com_google_auth_google_auth_library_oauth2_http//jar",
"@com_google_http_client_google_http_client//jar",
"@javax_annotation_javax_annotation_api//jar",
]
if not transport or transport == "grpc":
actual_deps += [
"@com_google_api_gax_java//gax-grpc:gax_grpc",
"@io_grpc_grpc_java//core:core",
"@io_grpc_grpc_java//protobuf:protobuf",
]
elif transport == "rest":
actual_deps += [
"@com_google_api_gax_java//gax-httpjson:gax_httpjson",
]
elif transport == "grpc+rest":
actual_deps += [
"@com_google_api_gax_java//gax-grpc:gax_grpc",
"@io_grpc_grpc_java//core:core",
"@io_grpc_grpc_java//protobuf:protobuf",
"@com_google_api_gax_java//gax-httpjson:gax_httpjson",
]
else:
fail("Unknown transport: %s" % transport)
native.java_library(
name = name,
srcs = ["%s.srcjar" % srcjar_name],
deps = actual_deps,
**kwargs
)
# Test deps.
actual_test_deps = [
"@com_google_googleapis//google/type:type_java_proto", # Commonly used.
"@com_google_api_gax_java//gax:gax_testlib",
"@com_google_code_gson_gson//jar",
"@junit_junit//jar",
]
if not transport or transport == "grpc":
actual_test_deps += [
"@com_google_api_gax_java//gax-grpc:gax_grpc_testlib",
"@io_grpc_grpc_java//auth:auth",
"@io_grpc_grpc_netty_shaded//jar",
"@io_grpc_grpc_java//stub:stub",
"@io_opencensus_opencensus_contrib_grpc_metrics//jar",
]
elif transport == "rest":
actual_test_deps += [
"@com_google_api_gax_java//gax-httpjson:gax_httpjson_testlib",
]
elif transport == "grpc+rest":
actual_test_deps += [
"@com_google_api_gax_java//gax-grpc:gax_grpc_testlib",
"@io_grpc_grpc_java//auth:auth",
"@io_grpc_grpc_netty_shaded//jar",
"@io_grpc_grpc_java//stub:stub",
"@io_opencensus_opencensus_contrib_grpc_metrics//jar",
"@com_google_api_gax_java//gax-httpjson:gax_httpjson_testlib",
]
else:
fail("Unknown transport: %s" % transport)
_append_dep_without_duplicates(actual_test_deps, test_deps)
_append_dep_without_duplicates(actual_test_deps, actual_deps)
native.java_library(
name = "%s_test" % name,
srcs = ["%s-test.srcjar" % srcjar_name],
deps = [":%s" % name] + actual_test_deps,
**kwargs
)
def java_gapic_test(name, runtime_deps, test_classes, **kwargs):
for test_class in test_classes:
native.java_test(
name = test_class,
test_class = test_class,
runtime_deps = runtime_deps,
**kwargs
)
native.test_suite(
name = name,
tests = test_classes,
**kwargs
)
# A debugging rule, to dump CodeGenereatorRequest from protoc as is to a file,
# which then can be used to run gapic-generator directly instead of relying on
# protoc to launch it. This would simplify attaching the debugger and/or
# working with stdin/stderr.
def java_generator_request_dump(
name,
srcs,
grpc_service_config = None,
gapic_yaml = None,
service_yaml = None,
transport = None,
rest_numeric_enums = False,
**kwargs):
_java_gapic_srcjar(
name = name,
srcs = srcs,
grpc_service_config = grpc_service_config,
gapic_yaml = gapic_yaml,
service_yaml = service_yaml,
transport = transport,
rest_numeric_enums = rest_numeric_enums,
java_generator_name = "code_generator_request_dumper",
**kwargs
)