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