xref: /aosp_15_r20/external/sdk-platform-java/rules_java_gapic/java_gapic.bzl (revision 882aa7c72c3cd3b66e72a261bdd69b93f7de7670)
1# Copyright 2020 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load("@rules_gapic//:gapic.bzl", "proto_custom_library")
16
17NO_GRPC_CONFIG_ALLOWLIST = ["library"]
18
19def _java_gapic_postprocess_srcjar_impl(ctx):
20    gapic_srcjar = ctx.file.gapic_srcjar
21    output_srcjar_name = ctx.label.name
22    output_main = ctx.outputs.main
23    output_test = ctx.outputs.test
24    output_samples = ctx.outputs.samples
25    output_resource_name = ctx.outputs.resource_name
26    formatter = ctx.executable.formatter
27
28    output_dir_name = ctx.label.name
29    output_dir_path = "%s/%s" % (output_main.dirname, output_dir_name)
30
31    script = """
32    unzip -q {gapic_srcjar}
33    # Sync'd to the output file name in Writer.java.
34    unzip -q temp-codegen.srcjar -d {output_dir_path}
35    # This may fail if there are spaces and/or too many files (exceed max length of command length).
36    {formatter} --replace $(find {output_dir_path} -type f -printf "%p ")
37    WORKING_DIR=`pwd`
38
39    # Main source files.
40    cd {output_dir_path}/src/main/java
41    zip -r $WORKING_DIR/{output_srcjar_name}.srcjar ./
42
43    # Resource name source files.
44    PROTO_DIR=$WORKING_DIR/{output_dir_path}/proto/src/main/java
45    PROTO_SRCJAR=$WORKING_DIR/{output_srcjar_name}-resource-name.srcjar
46    if [ ! -d $PROTO_DIR ]
47    then
48      # Some APIs don't have resource name helpers, like BigQuery v2.
49      # Create an empty file so we can finish building. Gating the resource name rule definition
50      # on file existences go against Bazel's design patterns, so we'll simply delete all empty
51      # files during the final packaging process (see java_gapic_pkg.bzl)
52      mkdir -p $PROTO_DIR
53      touch $PROTO_DIR/PlaceholderFile.java
54    fi
55    cd $WORKING_DIR/{output_dir_path}/proto/src/main/java
56    zip -r $PROTO_SRCJAR ./
57
58    # Test source files.
59    cd $WORKING_DIR/{output_dir_path}/src/test/java
60    zip -r $WORKING_DIR/{output_srcjar_name}-tests.srcjar ./
61
62    # Sample source files.
63    cd $WORKING_DIR/{output_dir_path}/samples/snippets/generated/src/main/java
64    zip -r $WORKING_DIR/{output_srcjar_name}-samples.srcjar ./
65
66    cd $WORKING_DIR
67
68    mv {output_srcjar_name}.srcjar {output_main}
69    mv {output_srcjar_name}-resource-name.srcjar {output_resource_name}
70    mv {output_srcjar_name}-tests.srcjar {output_test}
71    mv {output_srcjar_name}-samples.srcjar {output_samples}
72    """.format(
73        gapic_srcjar = gapic_srcjar.path,
74        output_srcjar_name = output_srcjar_name,
75        formatter = formatter,
76        output_dir_name = output_dir_name,
77        output_dir_path = output_dir_path,
78        output_main = output_main.path,
79        output_resource_name = output_resource_name.path,
80        output_test = output_test.path,
81        output_samples = output_samples.path,
82    )
83
84    ctx.actions.run_shell(
85        inputs = [gapic_srcjar],
86        tools = [formatter],
87        command = script,
88        outputs = [output_main, output_resource_name, output_test, output_samples],
89    )
90
91_java_gapic_postprocess_srcjar = rule(
92    attrs = {
93        "gapic_srcjar": attr.label(mandatory = True, allow_single_file = True),
94        "formatter": attr.label(
95            default = Label("//:google_java_format_binary"),
96            executable = True,
97            cfg = "host",
98        ),
99    },
100    outputs = {
101        "main": "%{name}.srcjar",
102        "resource_name": "%{name}-resource-name.srcjar",
103        "test": "%{name}-test.srcjar",
104        "samples": "%{name}-samples.srcjar",
105    },
106    implementation = _java_gapic_postprocess_srcjar_impl,
107)
108
109def _java_gapic_samples_srcjar_impl(ctx):
110    gapic_srcjar = ctx.file.gapic_srcjar
111    output_srcjar_name = ctx.label.name
112    output_samples = ctx.outputs.samples
113    formatter = ctx.executable.formatter
114
115    output_dir_name = ctx.label.name
116    output_dir_path = "%s/%s" % (output_samples.dirname, output_dir_name)
117
118    script = """
119    unzip -q {gapic_srcjar}
120    # Sync'd to the output file name in Writer.java.
121    unzip -q temp-codegen.srcjar -d {output_dir_path}
122    # This may fail if there are spaces and/or too many files (exceed max length of command length).
123    {formatter} --replace $(find {output_dir_path} -type f -printf "%p ")
124    WORKING_DIR=`pwd`
125
126    # Sample source files.
127    cd $WORKING_DIR/{output_dir_path}/samples/snippets/generated/src/main/java
128    zip -r $WORKING_DIR/{output_srcjar_name}-samples.srcjar ./
129
130    cd $WORKING_DIR
131
132    mv {output_srcjar_name}-samples.srcjar {output_samples}
133    """.format(
134        gapic_srcjar = gapic_srcjar.path,
135        output_srcjar_name = output_srcjar_name,
136        formatter = formatter,
137        output_dir_name = output_dir_name,
138        output_dir_path = output_dir_path,
139        output_samples = output_samples.path,
140    )
141
142    ctx.actions.run_shell(
143        inputs = [gapic_srcjar],
144        tools = [formatter],
145        command = script,
146        outputs = [output_samples],
147    )
148
149_java_gapic_samples_srcjar = rule(
150    attrs = {
151        "gapic_srcjar": attr.label(mandatory = True, allow_single_file = True),
152        "formatter": attr.label(
153            default = Label("//:google_java_format_binary"),
154            executable = True,
155            cfg = "host",
156        ),
157    },
158    outputs = {
159        "samples": "%{name}-samples.srcjar",
160    },
161    implementation = _java_gapic_samples_srcjar_impl,
162)
163
164def _extract_common_proto_dep(dep):
165    return dep[dep.index("/"):] if "//google" in dep else dep
166
167def _append_dep_without_duplicates(dest_deps, new_deps):
168    """
169    Appends new_deps into dest_deps only if elements in new_deps
170    are not already present in dest_deps.
171    A workaround for the lack of sets in skylark.
172    """
173
174    # Naive dep checking, since the source (i.e. prefixed) repo can vary.
175    # Examine only "//google"-prefixed targets, since common proto deps
176    # are more likely to be duplicated.
177    processed_dest_deps = [_extract_common_proto_dep(dep) for dep in dest_deps]
178    processed_new_deps = [_extract_common_proto_dep(dep) for dep in new_deps]
179    for i in range(len(new_deps)):
180        if processed_new_deps[i] not in processed_dest_deps:
181            dest_deps.append(new_deps[i])
182    return dest_deps
183
184def _java_gapic_srcjar(
185        name,
186        srcs,
187        grpc_service_config,
188        gapic_yaml,
189        service_yaml,
190        # possible values are: "grpc", "rest", "grpc+rest"
191        transport,
192        rest_numeric_enums,
193        # Can be used to provide a java_library with a customized generator,
194        # like the one which dumps descriptor to a file for future debugging.
195        java_generator_name = "java_gapic",
196        **kwargs):
197    file_args_dict = {}
198
199    if grpc_service_config:
200        file_args_dict[grpc_service_config] = "grpc-service-config"
201    elif not transport or transport == "grpc":
202        for keyword in NO_GRPC_CONFIG_ALLOWLIST:
203            if keyword not in name:
204                fail("Missing a gRPC service config file")
205
206    if gapic_yaml:
207        file_args_dict[gapic_yaml] = "gapic-config"
208
209    if service_yaml:
210        file_args_dict[service_yaml] = "api-service-config"
211
212    opt_args = []
213
214    if transport:
215        opt_args.append("transport=%s" % transport)
216
217    if rest_numeric_enums:
218        opt_args.append("rest-numeric-enums")
219
220    # Produces the GAPIC metadata file if this flag is set. to any value.
221    # Protoc invocation: --java_gapic_opt=metadata
222    plugin_args = ["metadata"]
223
224    proto_custom_library(
225        name = name,
226        deps = srcs,
227        plugin = Label("@gapic_generator_java//:protoc-gen-%s" % java_generator_name),
228        plugin_args = plugin_args,
229        plugin_file_args = {},
230        opt_file_args = file_args_dict,
231        output_type = java_generator_name,
232        output_suffix = ".srcjar",
233        opt_args = opt_args,
234        **kwargs
235    )
236
237def java_gapic_library(
238        name,
239        srcs,
240        grpc_service_config = None,
241        gapic_yaml = None,
242        service_yaml = None,
243        deps = [],
244        test_deps = [],
245        # possible values are: "grpc", "rest", "grpc+rest"
246        transport = None,
247        rest_numeric_enums = False,
248        **kwargs):
249    srcjar_name = name + "_srcjar"
250    raw_srcjar_name = srcjar_name + "_raw"
251
252    _java_gapic_srcjar(
253        name = raw_srcjar_name,
254        srcs = srcs,
255        grpc_service_config = grpc_service_config,
256        gapic_yaml = gapic_yaml,
257        service_yaml = service_yaml,
258        transport = transport,
259        rest_numeric_enums = rest_numeric_enums,
260        java_generator_name = "java_gapic",
261        **kwargs
262    )
263
264    _java_gapic_postprocess_srcjar(
265        name = srcjar_name,
266        gapic_srcjar = "%s.srcjar" % raw_srcjar_name,
267        **kwargs
268    )
269
270    _java_gapic_samples_srcjar(
271        name = "%s_samples" % name,
272        gapic_srcjar = "%s.srcjar" % raw_srcjar_name,
273        **kwargs
274    )
275
276    resource_name_name = "%s_resource_name" % name
277    resource_name_deps = [resource_name_name]
278    native.java_library(
279        name = resource_name_name,
280        srcs = ["%s-resource-name.srcjar" % srcjar_name],
281        deps = [
282            "@com_google_api_api_common//jar",
283            "@com_google_guava_guava//jar",
284            "@javax_annotation_javax_annotation_api//jar",
285        ],
286        **kwargs
287    )
288
289    # General additional deps.
290    actual_deps = deps + resource_name_deps + [
291        "@com_google_googleapis//google/rpc:rpc_java_proto",
292        "@com_google_googleapis//google/longrunning:longrunning_java_proto",
293        "@com_google_protobuf//:protobuf_java",
294        "@com_google_api_api_common//jar",
295        "@com_google_api_gax_java//gax:gax",
296        "@com_google_guava_guava//jar",
297        "@com_google_code_findbugs_jsr305//jar",
298        "@org_threeten_threetenbp//jar",
299        "@io_opencensus_opencensus_api//jar",
300        "@com_google_auth_google_auth_library_credentials//jar",
301        "@com_google_auth_google_auth_library_oauth2_http//jar",
302        "@com_google_http_client_google_http_client//jar",
303        "@javax_annotation_javax_annotation_api//jar",
304    ]
305
306    if not transport or transport == "grpc":
307        actual_deps += [
308            "@com_google_api_gax_java//gax-grpc:gax_grpc",
309            "@io_grpc_grpc_java//core:core",
310            "@io_grpc_grpc_java//protobuf:protobuf",
311        ]
312    elif transport == "rest":
313        actual_deps += [
314            "@com_google_api_gax_java//gax-httpjson:gax_httpjson",
315        ]
316    elif transport == "grpc+rest":
317        actual_deps += [
318            "@com_google_api_gax_java//gax-grpc:gax_grpc",
319            "@io_grpc_grpc_java//core:core",
320            "@io_grpc_grpc_java//protobuf:protobuf",
321            "@com_google_api_gax_java//gax-httpjson:gax_httpjson",
322        ]
323    else:
324        fail("Unknown transport: %s" % transport)
325
326    native.java_library(
327        name = name,
328        srcs = ["%s.srcjar" % srcjar_name],
329        deps = actual_deps,
330        **kwargs
331    )
332
333    # Test deps.
334    actual_test_deps = [
335        "@com_google_googleapis//google/type:type_java_proto",  # Commonly used.
336        "@com_google_api_gax_java//gax:gax_testlib",
337        "@com_google_code_gson_gson//jar",
338        "@junit_junit//jar",
339    ]
340
341    if not transport or transport == "grpc":
342        actual_test_deps += [
343            "@com_google_api_gax_java//gax-grpc:gax_grpc_testlib",
344            "@io_grpc_grpc_java//auth:auth",
345            "@io_grpc_grpc_netty_shaded//jar",
346            "@io_grpc_grpc_java//stub:stub",
347            "@io_opencensus_opencensus_contrib_grpc_metrics//jar",
348        ]
349    elif transport == "rest":
350        actual_test_deps += [
351            "@com_google_api_gax_java//gax-httpjson:gax_httpjson_testlib",
352        ]
353    elif transport == "grpc+rest":
354        actual_test_deps += [
355            "@com_google_api_gax_java//gax-grpc:gax_grpc_testlib",
356            "@io_grpc_grpc_java//auth:auth",
357            "@io_grpc_grpc_netty_shaded//jar",
358            "@io_grpc_grpc_java//stub:stub",
359            "@io_opencensus_opencensus_contrib_grpc_metrics//jar",
360            "@com_google_api_gax_java//gax-httpjson:gax_httpjson_testlib",
361        ]
362    else:
363        fail("Unknown transport: %s" % transport)
364
365    _append_dep_without_duplicates(actual_test_deps, test_deps)
366    _append_dep_without_duplicates(actual_test_deps, actual_deps)
367
368    native.java_library(
369        name = "%s_test" % name,
370        srcs = ["%s-test.srcjar" % srcjar_name],
371        deps = [":%s" % name] + actual_test_deps,
372        **kwargs
373    )
374
375def java_gapic_test(name, runtime_deps, test_classes, **kwargs):
376    for test_class in test_classes:
377        native.java_test(
378            name = test_class,
379            test_class = test_class,
380            runtime_deps = runtime_deps,
381            **kwargs
382        )
383    native.test_suite(
384        name = name,
385        tests = test_classes,
386        **kwargs
387    )
388
389# A debugging rule, to dump CodeGenereatorRequest from protoc as is to a file,
390# which then can be used to run gapic-generator directly instead of relying on
391# protoc to launch it. This would simplify attaching the debugger and/or
392# working with stdin/stderr.
393def java_generator_request_dump(
394        name,
395        srcs,
396        grpc_service_config = None,
397        gapic_yaml = None,
398        service_yaml = None,
399        transport = None,
400        rest_numeric_enums = False,
401        **kwargs):
402    _java_gapic_srcjar(
403        name = name,
404        srcs = srcs,
405        grpc_service_config = grpc_service_config,
406        gapic_yaml = gapic_yaml,
407        service_yaml = service_yaml,
408        transport = transport,
409        rest_numeric_enums = rest_numeric_enums,
410        java_generator_name = "code_generator_request_dumper",
411        **kwargs
412    )
413