1"""Copyright (C) 2022 The Android Open Source Project 2 3Licensed under the Apache License, Version 2.0 (the "License"); 4you may not use this file except in compliance with the License. 5You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9Unless required by applicable law or agreed to in writing, software 10distributed under the License is distributed on an "AS IS" BASIS, 11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12See the License for the specific language governing permissions and 13limitations under the License. 14""" 15 16load("@bazel_skylib//lib:paths.bzl", "paths") 17load("@bazel_skylib//lib:sets.bzl", "sets") 18load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") 19load(":cc_proto.bzl", "PROTO_GEN_NAME_SUFFIX", "cc_proto_library") 20 21PROTO_GEN = "external/protobuf/aprotoc" 22VIRTUAL_IMPORT = "_virtual_imports" 23RUNFILES = "_middlemen/external_Sprotobuf_Saprotoc-runfiles" 24 25GEN_SUFFIX = [ 26 ".pb.h", 27 ".pb.cc", 28] 29 30def _get_search_paths(action): 31 cmd = action.argv 32 search_paths = sets.make() 33 cmd_len = len(cmd) 34 for i in range(cmd_len): 35 if cmd[i].startswith("-I"): 36 sets.insert(search_paths, cmd[i].lstrip("- I")) 37 38 return search_paths 39 40def _proto_code_gen_test_impl(ctx): 41 env = analysistest.begin(ctx) 42 target_under_test = analysistest.target_under_test(env) 43 actions = analysistest.target_actions(env) 44 package_root = ctx.label.package 45 local_file_output_path = paths.join( 46 package_root, 47 target_under_test.label.name, 48 package_root, 49 ) 50 51 input_files = [ 52 ctx.attr.local_file_path, 53 ctx.attr.external_file_path, 54 ctx.attr.deps_file_path, 55 ] 56 57 output_files = [ 58 ctx.attr.local_file_path, 59 ctx.attr.external_file_path, 60 ] 61 62 asserts.true( 63 env, 64 len(actions) == 1, 65 "Proto gen action not found: %s" % actions, 66 ) 67 68 action = actions[0] 69 70 asserts.set_equals( 71 env, 72 expected = sets.make( 73 [paths.join(package_root, file) for file in input_files] + [ 74 PROTO_GEN, 75 RUNFILES, 76 ], 77 ), 78 actual = sets.make([ 79 file.short_path 80 for file in action.inputs.to_list() 81 ]), 82 ) 83 84 asserts.set_equals( 85 env, 86 expected = sets.make( 87 [ 88 paths.join( 89 local_file_output_path, 90 paths.replace_extension(file, ext), 91 ) 92 for ext in GEN_SUFFIX 93 for file in output_files 94 ], 95 ), 96 actual = sets.make([ 97 file.short_path 98 for file in action.outputs.to_list() 99 ]), 100 ) 101 102 search_paths = _get_search_paths(action) 103 104 asserts.equals( 105 env, 106 expected = sets.make( 107 ["."] + 108 [paths.join(package_root, f) + "=" + paths.join(package_root, f) for f in input_files], 109 ), 110 actual = search_paths, 111 ) 112 113 return analysistest.end(env) 114 115proto_code_gen_test = analysistest.make( 116 _proto_code_gen_test_impl, 117 attrs = { 118 "local_file_path": attr.string(), 119 "deps_file_path": attr.string(), 120 "external_file_path": attr.string(), 121 }, 122) 123 124def _test_proto_code_gen(): 125 test_name = "proto_code_gen_test" 126 local_file_path = "local/proto_local.proto" 127 external_file_path = "external/proto_external.proto" 128 deps_file_path = "deps/proto_deps.proto" 129 external_proto_name = test_name + "_external_proto" 130 deps_proto_name = test_name + "_deps_proto" 131 local_proto_name = test_name + "_proto" 132 cc_name = test_name + "_cc_proto" 133 134 native.proto_library( 135 name = external_proto_name, 136 srcs = [external_file_path], 137 tags = ["manual"], 138 ) 139 140 native.proto_library( 141 name = deps_proto_name, 142 srcs = [deps_file_path], 143 tags = ["manual"], 144 ) 145 146 native.proto_library( 147 name = local_proto_name, 148 srcs = [local_file_path], 149 deps = [":" + deps_proto_name], 150 tags = ["manual"], 151 ) 152 153 cc_proto_library( 154 name = cc_name, 155 deps = [ 156 ":" + local_proto_name, 157 ":" + external_proto_name, 158 ], 159 tags = ["manual"], 160 ) 161 162 proto_code_gen_test( 163 name = test_name, 164 target_under_test = cc_name + PROTO_GEN_NAME_SUFFIX, 165 local_file_path = local_file_path, 166 deps_file_path = deps_file_path, 167 external_file_path = external_file_path, 168 ) 169 170 return test_name 171 172def _proto_strip_import_prefix_test_impl(ctx): 173 env = analysistest.begin(ctx) 174 target_under_test = analysistest.target_under_test(env) 175 actions = analysistest.target_actions(env) 176 package_root = ctx.label.package 177 178 # strip the proto file path, src/stripped/stripped.proto -> stripped/stripped.proto 179 stripped_file_name = paths.relativize(ctx.attr.stripped_file_name, ctx.attr.strip_import_prefix) 180 stripped_file_input_path = paths.join( 181 package_root, 182 VIRTUAL_IMPORT, 183 ctx.attr.stripped_proto_name, 184 ) 185 stripped_file_input_full_path = paths.join( 186 stripped_file_input_path, 187 stripped_file_name, 188 ) 189 unstripped_file_output_path = paths.join( 190 package_root, 191 target_under_test.label.name, 192 package_root, 193 ) 194 stripped_file_output_path = paths.join( 195 unstripped_file_output_path, 196 VIRTUAL_IMPORT, 197 ctx.attr.stripped_proto_name, 198 ) 199 200 asserts.true( 201 env, 202 len(actions) == 1, 203 "Proto gen action not found: %s" % actions, 204 ) 205 206 action = actions[0] 207 208 asserts.set_equals( 209 env, 210 expected = sets.make( 211 [ 212 paths.join(package_root, ctx.attr.unstripped_file_name), 213 stripped_file_input_full_path, 214 PROTO_GEN, 215 RUNFILES, 216 ], 217 ), 218 actual = sets.make([ 219 file.short_path 220 for file in action.inputs.to_list() 221 ]), 222 ) 223 224 asserts.set_equals( 225 env, 226 expected = sets.make( 227 [ 228 paths.join( 229 unstripped_file_output_path, 230 paths.replace_extension(ctx.attr.unstripped_file_name, ext), 231 ) 232 for ext in GEN_SUFFIX 233 ] + 234 [ 235 paths.join( 236 stripped_file_output_path, 237 paths.replace_extension(stripped_file_name, ext), 238 ) 239 for ext in GEN_SUFFIX 240 ], 241 ), 242 actual = sets.make([ 243 file.short_path 244 for file in action.outputs.to_list() 245 ]), 246 ) 247 248 search_paths = _get_search_paths(action) 249 250 asserts.equals( 251 env, 252 expected = sets.make([ 253 ".", 254 paths.join(package_root, ctx.attr.unstripped_file_name) + "=" + paths.join(package_root, ctx.attr.unstripped_file_name), 255 stripped_file_input_full_path + "=" + 256 paths.join( 257 ctx.genfiles_dir.path, 258 stripped_file_input_full_path, 259 ), 260 paths.join( 261 ctx.genfiles_dir.path, 262 stripped_file_input_path, 263 ), 264 ]), 265 actual = search_paths, 266 ) 267 268 return analysistest.end(env) 269 270proto_strip_import_prefix_test = analysistest.make( 271 _proto_strip_import_prefix_test_impl, 272 attrs = { 273 "stripped_proto_name": attr.string(), 274 "stripped_file_name": attr.string(), 275 "unstripped_file_name": attr.string(), 276 "strip_import_prefix": attr.string(), 277 }, 278) 279 280def _test_proto_strip_import_prefix(): 281 test_name = "proto_strip_import_prefix_test" 282 unstripped_proto_name = test_name + "_unstripped_proto" 283 stripped_proto_name = test_name + "_stripped_proto" 284 unstripped_file_name = "unstripped/unstripped.proto" 285 stripped_file_name = "src/stripped/stripped.proto" 286 cc_name = test_name + "_cc_proto" 287 strip_import_prefix = "src" 288 289 native.proto_library( 290 name = unstripped_proto_name, 291 srcs = [unstripped_file_name], 292 tags = ["manual"], 293 ) 294 295 native.proto_library( 296 name = stripped_proto_name, 297 srcs = [stripped_file_name], 298 strip_import_prefix = strip_import_prefix, 299 tags = ["manual"], 300 ) 301 302 cc_proto_library( 303 name = cc_name, 304 deps = [ 305 ":" + stripped_proto_name, 306 ":" + unstripped_proto_name, 307 ], 308 tags = ["manual"], 309 ) 310 311 proto_strip_import_prefix_test( 312 name = test_name, 313 target_under_test = cc_name + PROTO_GEN_NAME_SUFFIX, 314 stripped_proto_name = stripped_proto_name, 315 stripped_file_name = stripped_file_name, 316 unstripped_file_name = unstripped_file_name, 317 strip_import_prefix = strip_import_prefix, 318 ) 319 320 return test_name 321 322def _proto_with_external_packages_test_impl(ctx): 323 env = analysistest.begin(ctx) 324 target_under_test = analysistest.target_under_test(env) 325 actions = analysistest.target_actions(env) 326 package_root = ctx.label.package 327 deps_file_path = ctx.attr.deps_file_path 328 external_file_path = ctx.attr.external_file_path 329 local_file_path = ctx.attr.local_file_path 330 331 asserts.true( 332 env, 333 len(actions) == 1, 334 "Proto gen action not found: %s" % actions, 335 ) 336 337 action = actions[0] 338 339 asserts.set_equals( 340 env, 341 expected = sets.make( 342 [ 343 paths.join(package_root, local_file_path), 344 deps_file_path, 345 external_file_path, 346 PROTO_GEN, 347 RUNFILES, 348 ], 349 ), 350 actual = sets.make([ 351 file.short_path 352 for file in action.inputs.to_list() 353 ]), 354 ) 355 356 asserts.set_equals( 357 env, 358 expected = sets.make( 359 [ 360 paths.join( 361 package_root, 362 target_under_test.label.name, 363 package_root, 364 paths.replace_extension(local_file_path, ext), 365 ) 366 for ext in GEN_SUFFIX 367 ] + 368 [ 369 paths.join( 370 package_root, 371 target_under_test.label.name, 372 paths.replace_extension(external_file_path, ext), 373 ) 374 for ext in GEN_SUFFIX 375 ], 376 ), 377 actual = sets.make([ 378 file.short_path 379 for file in action.outputs.to_list() 380 ]), 381 ) 382 383 search_paths = _get_search_paths(action) 384 385 asserts.equals( 386 env, 387 expected = sets.make([ 388 ".", 389 paths.join(package_root, local_file_path) + "=" + paths.join(package_root, local_file_path), 390 deps_file_path + "=" + deps_file_path, 391 external_file_path + "=" + external_file_path, 392 ]), 393 actual = search_paths, 394 ) 395 396 return analysistest.end(env) 397 398proto_with_external_packages_test = analysistest.make( 399 _proto_with_external_packages_test_impl, 400 attrs = { 401 "local_file_path": attr.string(), 402 "deps_file_path": attr.string(), 403 "external_file_path": attr.string(), 404 }, 405) 406 407def _test_proto_with_external_packages(): 408 test_name = "proto_with_external_packages_test" 409 proto_name = test_name + "_proto" 410 cc_name = test_name + "_cc_proto" 411 local_file_path = "local/proto_local.proto" 412 deps_file_path = "build/bazel/examples/cc/proto/deps/src/enums/proto_deps.proto" 413 external_file_path = "build/bazel/examples/cc/proto/external/src/enums/proto_external.proto" 414 415 native.proto_library( 416 name = proto_name, 417 srcs = [local_file_path], 418 deps = ["//build/bazel/examples/cc/proto/deps:deps_proto"], 419 tags = ["manual"], 420 ) 421 422 cc_proto_library( 423 name = cc_name, 424 deps = [ 425 ":" + proto_name, 426 "//build/bazel/examples/cc/proto/external:external_proto", 427 ], 428 tags = ["manual"], 429 ) 430 431 proto_with_external_packages_test( 432 name = test_name, 433 target_under_test = cc_name + PROTO_GEN_NAME_SUFFIX, 434 local_file_path = local_file_path, 435 deps_file_path = deps_file_path, 436 external_file_path = external_file_path, 437 ) 438 439 return test_name 440 441def cc_proto_test_suite(name): 442 native.test_suite( 443 name = name, 444 tests = [ 445 _test_proto_code_gen(), 446 _test_proto_strip_import_prefix(), 447 _test_proto_with_external_packages(), 448 ], 449 ) 450