1"""Unittests for rust rules.""" 2 3load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") 4load("@rules_cc//cc:defs.bzl", "cc_library") 5load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro", "rust_shared_library", "rust_static_library") 6load( 7 "//test/unit:common.bzl", 8 "assert_argv_contains", 9 "assert_argv_contains_not", 10 "assert_argv_contains_prefix", 11 "assert_argv_contains_prefix_not", 12 "assert_argv_contains_prefix_suffix", 13 "assert_list_contains_adjacent_elements", 14) 15 16def _get_toolchain(ctx): 17 return ctx.attr._toolchain[platform_common.ToolchainInfo] 18 19def _get_darwin_component(arg): 20 # path/to/darwin_x86_64-fastbuild-fastbuild/package -> darwin_x86_64-fastbuild 21 darwin_component = [x for x in arg.split("/") if x.startswith("darwin")][0] 22 23 # darwin_x86_64-fastbuild -> darwin 24 return darwin_component.split("-")[0] 25 26def _rlib_has_no_native_libs_test_impl(ctx): 27 env = analysistest.begin(ctx) 28 tut = analysistest.target_under_test(env) 29 action = tut.actions[0] 30 assert_argv_contains(env, action, "--crate-type=rlib") 31 assert_argv_contains_not(env, action, "-lstatic=native_dep") 32 assert_argv_contains_not(env, action, "-ldylib=native_dep") 33 assert_argv_contains_prefix_not(env, action, "--codegen=linker=") 34 return analysistest.end(env) 35 36def _cdylib_has_native_libs_test_impl(ctx): 37 env = analysistest.begin(ctx) 38 tut = analysistest.target_under_test(env) 39 action = tut.actions[0] 40 toolchain = _get_toolchain(ctx) 41 compilation_mode = ctx.var["COMPILATION_MODE"] 42 pic_suffix = _get_pic_suffix(ctx, compilation_mode) 43 assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") 44 assert_argv_contains(env, action, "--crate-type=cdylib") 45 assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix)) 46 if toolchain.target_os == "windows": 47 if toolchain.target_triple.abi == "msvc": 48 native_link_arg = "-Clink-arg=native_dep.lib" 49 else: 50 native_link_arg = "-Clink-arg=-lnative_dep.lib" 51 else: 52 native_link_arg = "-Clink-arg=-lnative_dep{}".format(pic_suffix) 53 assert_argv_contains(env, action, native_link_arg) 54 assert_argv_contains_prefix(env, action, "--codegen=linker=") 55 return analysistest.end(env) 56 57def _staticlib_has_native_libs_test_impl(ctx): 58 env = analysistest.begin(ctx) 59 tut = analysistest.target_under_test(env) 60 action = tut.actions[0] 61 toolchain = _get_toolchain(ctx) 62 assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") 63 assert_argv_contains(env, action, "--crate-type=staticlib") 64 assert_argv_contains(env, action, "-lstatic=native_dep") 65 if toolchain.target_os == "windows": 66 if toolchain.target_triple.abi == "msvc": 67 native_link_arg = "-Clink-arg=native_dep.lib" 68 else: 69 native_link_arg = "-Clink-arg=-lnative_dep.lib" 70 else: 71 native_link_arg = "-Clink-arg=-lnative_dep" 72 assert_argv_contains(env, action, native_link_arg) 73 assert_argv_contains_prefix(env, action, "--codegen=linker=") 74 return analysistest.end(env) 75 76def _proc_macro_has_native_libs_test_impl(ctx): 77 env = analysistest.begin(ctx) 78 tut = analysistest.target_under_test(env) 79 action = tut.actions[0] 80 toolchain = _get_toolchain(ctx) 81 compilation_mode = ctx.var["COMPILATION_MODE"] 82 pic_suffix = _get_pic_suffix(ctx, compilation_mode) 83 assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") 84 assert_argv_contains(env, action, "--crate-type=proc-macro") 85 assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix)) 86 if toolchain.target_os == "windows": 87 if toolchain.target_triple.abi == "msvc": 88 native_link_arg = "-Clink-arg=native_dep.lib" 89 else: 90 native_link_arg = "-Clink-arg=-lnative_dep.lib" 91 else: 92 native_link_arg = "-Clink-arg=-lnative_dep{}".format(pic_suffix) 93 assert_argv_contains(env, action, native_link_arg) 94 assert_argv_contains_prefix(env, action, "--codegen=linker=") 95 return analysistest.end(env) 96 97def _bin_has_native_libs_test_impl(ctx): 98 env = analysistest.begin(ctx) 99 tut = analysistest.target_under_test(env) 100 action = tut.actions[0] 101 toolchain = _get_toolchain(ctx) 102 assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps") 103 assert_argv_contains(env, action, "-lstatic=native_dep") 104 if toolchain.target_os == "windows": 105 if toolchain.target_triple.abi == "msvc": 106 native_link_arg = "-Clink-arg=native_dep.lib" 107 else: 108 native_link_arg = "-Clink-arg=-lnative_dep.lib" 109 else: 110 native_link_arg = "-Clink-arg=-lnative_dep" 111 assert_argv_contains(env, action, native_link_arg) 112 assert_argv_contains_prefix(env, action, "--codegen=linker=") 113 return analysistest.end(env) 114 115def _extract_linker_args(argv): 116 return [ 117 a.removeprefix("--codegen=").removeprefix("-C").removeprefix("link-arg=").removeprefix("link-args=") 118 for a in argv 119 if ( 120 a.startswith("--codegen=link-arg=") or 121 a.startswith("--codegen=link-args=") or 122 a.startswith("-Clink-args=") or 123 a.startswith("-Clink-arg=") or 124 a.startswith("link-args=") or 125 a.startswith("link-arg=") or 126 a.startswith("-l") or 127 a.endswith(".lo") or 128 a.endswith(".o") 129 ) 130 ] 131 132def _bin_has_native_dep_and_alwayslink_test_impl(ctx): 133 env = analysistest.begin(ctx) 134 tut = analysistest.target_under_test(env) 135 action = tut.actions[0] 136 137 toolchain = _get_toolchain(ctx) 138 compilation_mode = ctx.var["COMPILATION_MODE"] 139 workspace_prefix = "" if ctx.workspace_name == "rules_rust" else "external/rules_rust/" 140 link_args = _extract_linker_args(action.argv) 141 if toolchain.target_os == "darwin": 142 darwin_component = _get_darwin_component(link_args[-1]) 143 want = [ 144 "-lstatic=native_dep", 145 "-lnative_dep", 146 "-Wl,-force_load,bazel-out/{}-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(darwin_component, compilation_mode, workspace_prefix), 147 ] 148 assert_list_contains_adjacent_elements(env, link_args, want) 149 elif toolchain.target_os == "windows": 150 if toolchain.target_triple.abi == "msvc": 151 want = [ 152 "-lstatic=native_dep", 153 "native_dep.lib", 154 "/WHOLEARCHIVE:bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), 155 ] 156 else: 157 want = [ 158 "-lstatic=native_dep", 159 "native_dep.lib", 160 "-Wl,--whole-archive", 161 "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), 162 "-Wl,--no-whole-archive", 163 ] 164 else: 165 want = [ 166 "-lstatic=native_dep", 167 "-lnative_dep", 168 "-Wl,--whole-archive", 169 "bazel-out/k8-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(compilation_mode, workspace_prefix), 170 "-Wl,--no-whole-archive", 171 ] 172 assert_list_contains_adjacent_elements(env, link_args, want) 173 return analysistest.end(env) 174 175def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx): 176 toolchain = _get_toolchain(ctx) 177 178 env = analysistest.begin(ctx) 179 tut = analysistest.target_under_test(env) 180 action = tut.actions[0] 181 182 linker_args = _extract_linker_args(action.argv) 183 184 toolchain = _get_toolchain(ctx) 185 compilation_mode = ctx.var["COMPILATION_MODE"] 186 workspace_prefix = "" if ctx.workspace_name == "rules_rust" else "external/rules_rust/" 187 pic_suffix = _get_pic_suffix(ctx, compilation_mode) 188 if toolchain.target_os == "darwin": 189 darwin_component = _get_darwin_component(linker_args[-1]) 190 want = [ 191 "-lstatic=native_dep{}".format(pic_suffix), 192 "-lnative_dep{}".format(pic_suffix), 193 "-Wl,-force_load,bazel-out/{}-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(darwin_component, compilation_mode, workspace_prefix, pic_suffix), 194 ] 195 elif toolchain.target_os == "windows": 196 if toolchain.target_triple.abi == "msvc": 197 want = [ 198 "-lstatic=native_dep", 199 "native_dep.lib", 200 "/WHOLEARCHIVE:bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), 201 ] 202 else: 203 want = [ 204 "-lstatic=native_dep", 205 "native_dep.lib", 206 "-Wl,--whole-archive", 207 "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix), 208 "-Wl,--no-whole-archive", 209 ] 210 else: 211 want = [ 212 "-lstatic=native_dep{}".format(pic_suffix), 213 "-lnative_dep{}".format(pic_suffix), 214 "-Wl,--whole-archive", 215 "bazel-out/k8-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(compilation_mode, workspace_prefix, pic_suffix), 216 "-Wl,--no-whole-archive", 217 ] 218 assert_list_contains_adjacent_elements(env, linker_args, want) 219 return analysistest.end(env) 220 221def _get_pic_suffix(ctx, compilation_mode): 222 toolchain = _get_toolchain(ctx) 223 if toolchain.target_os == "darwin" or toolchain.target_os == "windows": 224 return "" 225 return ".pic" if compilation_mode == "opt" else "" 226 227rlib_has_no_native_libs_test = analysistest.make(_rlib_has_no_native_libs_test_impl) 228staticlib_has_native_libs_test = analysistest.make(_staticlib_has_native_libs_test_impl, attrs = { 229 "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), 230}) 231cdylib_has_native_libs_test = analysistest.make(_cdylib_has_native_libs_test_impl, attrs = { 232 "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), 233}) 234proc_macro_has_native_libs_test = analysistest.make(_proc_macro_has_native_libs_test_impl, attrs = { 235 "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), 236}) 237bin_has_native_libs_test = analysistest.make(_bin_has_native_libs_test_impl, attrs = { 238 "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), 239}) 240bin_has_native_dep_and_alwayslink_test = analysistest.make(_bin_has_native_dep_and_alwayslink_test_impl, attrs = { 241 "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), 242}) 243cdylib_has_native_dep_and_alwayslink_test = analysistest.make(_cdylib_has_native_dep_and_alwayslink_test_impl, attrs = { 244 "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")), 245}) 246 247def _native_dep_test(): 248 rust_library( 249 name = "rlib_has_no_native_dep", 250 srcs = ["lib_using_native_dep.rs"], 251 edition = "2018", 252 deps = [":native_dep"], 253 ) 254 255 rust_static_library( 256 name = "staticlib_has_native_dep", 257 srcs = ["lib_using_native_dep.rs"], 258 edition = "2018", 259 deps = [":native_dep"], 260 ) 261 262 rust_shared_library( 263 name = "cdylib_has_native_dep", 264 srcs = ["lib_using_native_dep.rs"], 265 edition = "2018", 266 deps = [":native_dep"], 267 ) 268 269 rust_proc_macro( 270 name = "proc_macro_has_native_dep", 271 srcs = ["proc_macro_using_native_dep.rs"], 272 edition = "2018", 273 deps = [":native_dep"], 274 ) 275 276 rust_binary( 277 name = "bin_has_native_dep", 278 srcs = ["bin_using_native_dep.rs"], 279 edition = "2018", 280 deps = [":native_dep"], 281 ) 282 283 rust_binary( 284 name = "bin_has_native_dep_and_alwayslink", 285 srcs = ["bin_using_native_dep.rs"], 286 edition = "2018", 287 deps = [":native_dep", ":alwayslink"], 288 ) 289 290 cc_library( 291 name = "native_dep", 292 srcs = ["native_dep.cc"], 293 visibility = ["//test/unit:__subpackages__"], 294 ) 295 296 cc_library( 297 name = "alwayslink", 298 srcs = ["alwayslink.cc"], 299 alwayslink = 1, 300 ) 301 302 rust_shared_library( 303 name = "cdylib_has_native_dep_and_alwayslink", 304 srcs = ["lib_using_native_dep.rs"], 305 edition = "2018", 306 deps = [":native_dep", ":alwayslink"], 307 ) 308 309 rlib_has_no_native_libs_test( 310 name = "rlib_has_no_native_libs_test", 311 target_under_test = ":rlib_has_no_native_dep", 312 ) 313 staticlib_has_native_libs_test( 314 name = "staticlib_has_native_libs_test", 315 target_under_test = ":staticlib_has_native_dep", 316 ) 317 cdylib_has_native_libs_test( 318 name = "cdylib_has_native_libs_test", 319 target_under_test = ":cdylib_has_native_dep", 320 ) 321 proc_macro_has_native_libs_test( 322 name = "proc_macro_has_native_libs_test", 323 target_under_test = ":proc_macro_has_native_dep", 324 ) 325 bin_has_native_libs_test( 326 name = "bin_has_native_libs_test", 327 target_under_test = ":bin_has_native_dep", 328 ) 329 bin_has_native_dep_and_alwayslink_test( 330 name = "bin_has_native_dep_and_alwayslink_test", 331 target_under_test = ":bin_has_native_dep_and_alwayslink", 332 ) 333 cdylib_has_native_dep_and_alwayslink_test( 334 name = "cdylib_has_native_dep_and_alwayslink_test", 335 target_under_test = ":cdylib_has_native_dep_and_alwayslink", 336 ) 337 338def _linkopts_propagate_test_impl(ctx): 339 env = analysistest.begin(ctx) 340 tut = analysistest.target_under_test(env) 341 action = tut.actions[0] 342 343 # Ensure linkopts from direct (-Llinkoptdep1) and transitive 344 # (-Llinkoptdep2) dependencies are propagated. 345 # Consistently with cc rules, dependency linkopts take precedence over 346 # dependent linkopts (i.e. dependency linkopts appear later in the command 347 # line). 348 349 linkopt_args = _extract_linker_args(action.argv) 350 assert_list_contains_adjacent_elements( 351 env, 352 linkopt_args, 353 ["-Llinkoptdep1", "-Llinkoptdep2"], 354 ) 355 return analysistest.end(env) 356 357linkopts_propagate_test = analysistest.make(_linkopts_propagate_test_impl) 358 359def _linkopts_test(): 360 rust_binary( 361 name = "linkopts_rust_bin", 362 srcs = ["bin_using_native_dep.rs"], 363 edition = "2018", 364 deps = [":linkopts_native_dep_a"], 365 ) 366 367 cc_library( 368 name = "linkopts_native_dep_a", 369 srcs = ["native_dep.cc"], 370 linkopts = ["-Llinkoptdep1"], 371 deps = [":linkopts_native_dep_b"], 372 ) 373 374 cc_library( 375 name = "linkopts_native_dep_b", 376 linkopts = ["-Llinkoptdep2"], 377 ) 378 379 linkopts_propagate_test( 380 name = "native_linkopts_propagate_test", 381 target_under_test = ":linkopts_rust_bin", 382 ) 383 384def _check_additional_deps_test_impl(ctx, expect_additional_deps): 385 env = analysistest.begin(ctx) 386 tut = analysistest.target_under_test(env) 387 action = tut.actions[0] 388 additional_inputs = [inp.basename for inp in action.inputs.to_list()] 389 asserts.equals(env, "dynamic.lds" in additional_inputs, expect_additional_deps) 390 return analysistest.end(env) 391 392def _has_additional_deps_test_impl(ctx): 393 return _check_additional_deps_test_impl(ctx, expect_additional_deps = True) 394 395def _has_no_additional_deps_test_impl(ctx): 396 return _check_additional_deps_test_impl(ctx, expect_additional_deps = False) 397 398has_additional_deps_test = analysistest.make(_has_additional_deps_test_impl) 399has_no_additional_deps_test = analysistest.make(_has_no_additional_deps_test_impl) 400 401def _additional_deps_test(): 402 rust_binary( 403 name = "bin_additional_deps", 404 srcs = ["bin_using_native_dep.rs"], 405 edition = "2018", 406 deps = [":additional_deps_cc"], 407 ) 408 409 rust_shared_library( 410 name = "cdylib_additional_deps", 411 srcs = ["lib_using_native_dep.rs"], 412 edition = "2018", 413 deps = [":additional_deps_cc"], 414 ) 415 416 rust_library( 417 name = "lib_additional_deps", 418 srcs = ["lib_using_native_dep.rs"], 419 edition = "2018", 420 deps = ["additional_deps_cc"], 421 ) 422 423 cc_library( 424 name = "additional_deps_cc", 425 srcs = ["native_dep.cc"], 426 linkopts = ["-L$(execpath :dynamic.lds)"], 427 deps = [":dynamic.lds"], 428 ) 429 430 has_additional_deps_test( 431 name = "bin_has_additional_deps_test", 432 target_under_test = ":bin_additional_deps", 433 ) 434 435 has_additional_deps_test( 436 name = "cdylib_has_additional_deps_test", 437 target_under_test = ":cdylib_additional_deps", 438 ) 439 440 has_no_additional_deps_test( 441 name = "lib_has_no_additional_deps_test", 442 target_under_test = ":lib_additional_deps", 443 ) 444 445def native_deps_test_suite(name): 446 """Entry-point macro called from the BUILD file. 447 448 Args: 449 name: Name of the macro. 450 """ 451 _native_dep_test() 452 _linkopts_test() 453 _additional_deps_test() 454 455 native.test_suite( 456 name = name, 457 tests = [ 458 ":bin_has_additional_deps_test", 459 ":bin_has_native_dep_and_alwayslink_test", 460 ":bin_has_native_libs_test", 461 ":cdylib_has_additional_deps_test", 462 ":cdylib_has_native_dep_and_alwayslink_test", 463 ":cdylib_has_native_libs_test", 464 ":lib_has_no_additional_deps_test", 465 ":native_linkopts_propagate_test", 466 ":proc_macro_has_native_libs_test", 467 ":rlib_has_no_native_libs_test", 468 ":staticlib_has_native_libs_test", 469 ], 470 ) 471