diff --git a/BUILD.bazel b/BUILD.bazel index ca78e67014..913c6d063a 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -8,6 +8,7 @@ load( package(default_visibility = ["//visibility:public"]) JAVA_SRCS = [ + "//src/main/java/com/google/api/generator/debug:debug_files", "//src/main/java/com/google/api/generator:generator_files", "//src/main/java/com/google/api/generator/engine:engine_files", "//src/main/java/com/google/api/generator/gapic:gapic_files", @@ -47,6 +48,57 @@ java_binary( ], ) +# Request dumper binary, which dumps the CodeGeneratorRequest to a file on disk +# which will be identical to the one passed to the protoc-gen-java_gapic during +# normal execution. The dumped file then can be used to run this gapic-generator +# directly (instead of relying on protoc to start the process), which would give +# much greater flexibility in terms of debugging features, like attaching a +# debugger, easier work with stdout and stderr etc. +# +# Usage example, via the rule in a corresponding BUILD.bazel file: +# +# load("@gapic_generator_java//rules_java_gapic:java_gapic.bzl", "java_generator_request_dump") +# java_generator_request_dump( +# name = "compute_small_request_dump", +# srcs = [":compute_small_proto_with_info"], +# transport = "rest", +# ) +# +java_binary( + name = "protoc-gen-code_generator_request_dumper", + main_class = "com.google.api.generator.debug.CodeGeneratorRequestDumper", + runtime_deps = [ + "//src/main/java/com/google/api/generator", + "//src/main/java/com/google/api/generator/debug", + "//src/main/java/com/google/api/generator/gapic", + "@com_google_googleapis//google/api:api_java_proto", + "@com_google_googleapis//google/longrunning:longrunning_java_proto", + "@com_google_guava_guava", + "@com_google_protobuf//:protobuf_java", + ], +) + +# A binary similar to protoc-gen-java_gapic but reads the CodeGeneratorRequest +# directly from a file instead of relying on protoc to pipe it in. +# +# Usage example: +# +# bazel run code_generator_request_file_to_gapic_main desc-dump.bin dump.jar +# +java_binary( + name = "code_generator_request_file_to_gapic_main", + main_class = "com.google.api.generator.debug.CodeGeneratorRequestFileToGapicMain", + runtime_deps = [ + "//src/main/java/com/google/api/generator", + "//src/main/java/com/google/api/generator/debug", + "//src/main/java/com/google/api/generator/gapic", + "@com_google_googleapis//google/api:api_java_proto", + "@com_google_googleapis//google/longrunning:longrunning_java_proto", + "@com_google_guava_guava", + "@com_google_protobuf//:protobuf_java", + ], +) + # google-java-format java_binary( name = "google_java_format_binary", diff --git a/rules_java_gapic/java_gapic.bzl b/rules_java_gapic/java_gapic.bzl index 505fbfdbc3..e8917b8f38 100644 --- a/rules_java_gapic/java_gapic.bzl +++ b/rules_java_gapic/java_gapic.bzl @@ -119,19 +119,18 @@ def _append_dep_without_duplicates(dest_deps, new_deps): dest_deps.append(new_deps[i]) return dest_deps -def java_gapic_library( +def _java_gapic_srcjar( name, srcs, - grpc_service_config = None, - gapic_yaml = None, - service_yaml = None, - deps = [], - test_deps = [], + grpc_service_config, + gapic_yaml, + service_yaml, # possible values are: "grpc", "rest", "grpc+rest" - transport = None, + transport, # 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", + java_generator_name = "java_gapic", + output_suffix = ".srcjar", **kwargs): file_args_dict = {} @@ -159,8 +158,6 @@ def java_gapic_library( else: fail("Service.yaml is no longer supported in the Java microgenerator") - srcjar_name = name + "_srcjar" - raw_srcjar_name = srcjar_name + "_raw" output_suffix = ".srcjar" opt_args = [] @@ -172,18 +169,43 @@ def java_gapic_library( plugin_args = ["metadata"] proto_custom_library( - name = raw_srcjar_name, + name = name, deps = srcs, - plugin = Label("@gapic_generator_java//:protoc-gen-%s" % _java_generator_name), + 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_type = java_generator_name, output_suffix = output_suffix, 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, + **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, + java_generator_name = "java_gapic", + **kwargs + ) + _java_gapic_postprocess_srcjar( name = srcjar_name, gapic_srcjar = "%s.srcjar" % raw_srcjar_name, @@ -282,3 +304,26 @@ def java_gapic_test(name, runtime_deps, test_classes, **kwargs): 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, + **kwargs): + _java_gapic_srcjar( + name = name, + srcs = srcs, + grpc_service_config = grpc_service_config, + gapic_yaml = gapic_yaml, + service_yaml = service_yaml, + transport = transport, + java_generator_name = "code_generator_request_dumper", + **kwargs + ) diff --git a/src/main/java/com/google/api/generator/Main.java b/src/main/java/com/google/api/generator/Main.java index 0e1e985e2f..2ad75c19ba 100644 --- a/src/main/java/com/google/api/generator/Main.java +++ b/src/main/java/com/google/api/generator/Main.java @@ -14,34 +14,18 @@ package com.google.api.generator; -import com.google.api.AnnotationsProto; -import com.google.api.ClientProto; -import com.google.api.FieldBehaviorProto; -import com.google.api.ResourceProto; import com.google.api.generator.gapic.Generator; -import com.google.longrunning.OperationsProto; -import com.google.protobuf.Descriptors.DescriptorValidationException; import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse; import java.io.IOException; public class Main { - public static void main(String[] args) - throws IOException, InterruptedException, DescriptorValidationException { + public static void main(String[] args) throws IOException { ExtensionRegistry registry = ExtensionRegistry.newInstance(); - registerAllExtensions(registry); + ProtoRegistry.registerAllExtensions(registry); CodeGeneratorRequest request = CodeGeneratorRequest.parseFrom(System.in, registry); CodeGeneratorResponse response = Generator.generateGapic(request); response.writeTo(System.out); } - - /** Register all extensions needed to process API protofiles. */ - private static void registerAllExtensions(ExtensionRegistry extensionRegistry) { - OperationsProto.registerAllExtensions(extensionRegistry); - AnnotationsProto.registerAllExtensions(extensionRegistry); - ClientProto.registerAllExtensions(extensionRegistry); - ResourceProto.registerAllExtensions(extensionRegistry); - FieldBehaviorProto.registerAllExtensions(extensionRegistry); - } } diff --git a/src/main/java/com/google/api/generator/ProtoRegistry.java b/src/main/java/com/google/api/generator/ProtoRegistry.java new file mode 100644 index 0000000000..da4806bba0 --- /dev/null +++ b/src/main/java/com/google/api/generator/ProtoRegistry.java @@ -0,0 +1,33 @@ +// Copyright 2021 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 +// +// 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. + +package com.google.api.generator; + +import com.google.api.AnnotationsProto; +import com.google.api.ClientProto; +import com.google.api.FieldBehaviorProto; +import com.google.api.ResourceProto; +import com.google.longrunning.OperationsProto; +import com.google.protobuf.ExtensionRegistry; + +public class ProtoRegistry { + /** Register all extensions needed to process API protofiles. */ + public static void registerAllExtensions(ExtensionRegistry extensionRegistry) { + OperationsProto.registerAllExtensions(extensionRegistry); + AnnotationsProto.registerAllExtensions(extensionRegistry); + ClientProto.registerAllExtensions(extensionRegistry); + ResourceProto.registerAllExtensions(extensionRegistry); + FieldBehaviorProto.registerAllExtensions(extensionRegistry); + } +} diff --git a/src/main/java/com/google/api/generator/debug/BUILD.bazel b/src/main/java/com/google/api/generator/debug/BUILD.bazel new file mode 100644 index 0000000000..fbc34298ad --- /dev/null +++ b/src/main/java/com/google/api/generator/debug/BUILD.bazel @@ -0,0 +1,27 @@ +load("@rules_java//java:defs.bzl", "java_library", "java_plugin") + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "debug_files", + srcs = glob(["*.java"]), +) + +java_library( + name = "debug", + srcs = [ + ":debug_files", + ], + deps = [ + "//src/main/java/com/google/api/generator", + "//src/main/java/com/google/api/generator/engine", + "//src/main/java/com/google/api/generator/engine/ast", + "//src/main/java/com/google/api/generator/gapic", + "//src/main/java/com/google/api/generator/gapic/model", + "//src/main/java/com/google/api/generator/util", + "@com_google_googleapis//google/api:api_java_proto", + "@com_google_googleapis//google/longrunning:longrunning_java_proto", + "@com_google_guava_guava//jar", + "@com_google_protobuf//:protobuf_java", + ], +) diff --git a/src/main/java/com/google/api/generator/debug/CodeGeneratorRequestDumper.java b/src/main/java/com/google/api/generator/debug/CodeGeneratorRequestDumper.java new file mode 100644 index 0000000000..ef75a6a652 --- /dev/null +++ b/src/main/java/com/google/api/generator/debug/CodeGeneratorRequestDumper.java @@ -0,0 +1,42 @@ +// Copyright 2021 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 +// +// 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. + +package com.google.api.generator.debug; + +import com.google.api.generator.ProtoRegistry; +import com.google.protobuf.ExtensionRegistry; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse; +import java.io.IOException; + +// Request dumper class, which dumps the CodeGeneratorRequest to a file on disk which will be +// identical to the one passed to the Main class during normal execution. The dumped file then can +// be used to run this gapic-generator directly (instead of relying on protoc to start the process), +// which would give much greater flexibility in terms of debugging features, like attaching a +// debugger, easier work with stdout and stderr etc. +public class CodeGeneratorRequestDumper { + public static void main(String[] args) throws IOException { + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + ProtoRegistry.registerAllExtensions(registry); + CodeGeneratorRequest request = CodeGeneratorRequest.parseFrom(System.in, registry); + + CodeGeneratorResponse.Builder response = CodeGeneratorResponse.newBuilder(); + response + .setSupportedFeatures(CodeGeneratorResponse.Feature.FEATURE_PROTO3_OPTIONAL_VALUE) + .addFileBuilder() + .setName("desc-dump.bin") + .setContentBytes(request.toByteString()); + response.build().writeTo(System.out); + } +} diff --git a/src/main/java/com/google/api/generator/debug/CodeGeneratorRequestFileToGapicMain.java b/src/main/java/com/google/api/generator/debug/CodeGeneratorRequestFileToGapicMain.java new file mode 100644 index 0000000000..5cbbb05608 --- /dev/null +++ b/src/main/java/com/google/api/generator/debug/CodeGeneratorRequestFileToGapicMain.java @@ -0,0 +1,45 @@ +// Copyright 2021 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 +// +// 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. + +package com.google.api.generator.debug; + +import com.google.api.generator.ProtoRegistry; +import com.google.api.generator.gapic.Generator; +import com.google.protobuf.ExtensionRegistry; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +// A generator entry point class, similar to Main but reads the CodeGeneratorRequest directly from a +// file instead of relying on protoc to pipe it in. +public class CodeGeneratorRequestFileToGapicMain { + public static void main(String[] args) throws IOException { + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + ProtoRegistry.registerAllExtensions(registry); + + String inputFile = args[0]; + String outputFile = args[1]; + + try (InputStream inputStream = new FileInputStream(inputFile); + OutputStream outputStream = new FileOutputStream(outputFile)) { + CodeGeneratorRequest request = CodeGeneratorRequest.parseFrom(inputStream, registry); + CodeGeneratorResponse response = Generator.generateGapic(request); + response.writeTo(outputStream); + } + } +}