# 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 )