| """White-box unit test of cc_binary rule.""" |
| |
| load("@bazel_features//:features.bzl", "bazel_features") |
| load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite") |
| load("@rules_testing//lib:truth.bzl", "matching", "subjects") |
| load("@rules_testing//lib:util.bzl", "TestingAspectInfo", "util") |
| load("//cc:action_names.bzl", "ACTION_NAMES") |
| load("//cc:cc_binary.bzl", _actual_cc_binary = "cc_binary") |
| load("//cc:cc_import.bzl", "cc_import") |
| load("//cc:cc_library.bzl", "cc_library") |
| load("//cc:cc_test.bzl", _actual_cc_test = "cc_test") |
| load("//tests/cc/testutil:cc_analysis_test.bzl", "MOCK_TOOLCHAINS", "cc_analysis_test") |
| load("//tests/cc/testutil:cc_binary_target_subject.bzl", "cc_binary_target_subject") |
| load("//tests/cc/testutil:link_action_subject.bzl", "link_action_subject") |
| load( |
| "//tests/cc/testutil:mock_rules.bzl", |
| "cc_runtimes_toolchain", |
| "mock_go_library", |
| "mock_runtime_library", |
| ) |
| load("//tests/cc/testutil/toolchains:features.bzl", "FEATURE_NAMES") |
| |
| # Wrap cc_binary to mock out common dependencies. |
| def cc_binary(name, **kwargs): |
| if "malloc" not in kwargs: |
| # Avoid the real "malloc", which might use arbitrary toolchain actions and features. |
| kwargs["malloc"] = "//tests/cc/testutil/toolchains:mock_malloc" |
| _actual_cc_binary( |
| name = name, |
| **kwargs |
| ) |
| |
| # Wrap cc_test to mock out common dependencies. |
| def cc_test(name, **kwargs): |
| if "malloc" not in kwargs: |
| # Avoid the real "malloc", which might use arbitrary toolchain actions and features. |
| kwargs["malloc"] = "//tests/cc/testutil/toolchains:mock_malloc" |
| _actual_cc_test( |
| name = name, |
| **kwargs |
| ) |
| |
| def _test_files_to_build(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_files_to_build_impl, |
| target = name + "/hello", |
| **kwargs |
| ) |
| |
| def _test_files_to_build_impl(env, target): |
| cc_binary_subject = cc_binary_target_subject.from_target(env, target) |
| cc_binary_subject.default_outputs().contains_exactly(["{package}/{name}{binary_extension}"]) |
| cc_binary_subject.executable().short_path_equals("{package}/{name}{binary_extension}") |
| |
| def _test_headers_not_passed_to_linking_action(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/bye", |
| srcs = ["bye.cc", "bye.h"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_headers_not_passed_to_linking_action_impl, |
| target = name + "/bye", |
| config_settings = { |
| "//command_line_option:features": ["parse_headers"], |
| "//command_line_option:process_headers_in_dependencies": True, |
| }, |
| **kwargs |
| ) |
| |
| def _test_headers_not_passed_to_linking_action_impl(env, target): |
| link_action_subject.from_target(env, target).inputs().not_contains_predicate( |
| matching.file_extension_in(["h", "hpp", "hxx"]), |
| ) |
| |
| def _test_no_duplicate_linkopts(name, **kwargs): |
| # Regression test for b/943558: linkopts duplicated in linker invocation |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| linkopts = ["-fake_option"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_no_duplicate_linkopts_impl, |
| target = name + "/hello", |
| **kwargs |
| ) |
| |
| def _test_no_duplicate_linkopts_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.argv().contains("-fake_option") |
| link_action.argv().transform( |
| filter = matching.equals_wrapper("-fake_option"), |
| ).contains_no_duplicates() |
| |
| def _test_action_graph(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| config_settings = { |
| "//command_line_option:force_pic": False, |
| } |
| |
| cc_analysis_test( |
| name = name, |
| impl = _test_action_graph_impl, |
| target = name + "/hello", |
| config_settings = config_settings, |
| test_features = [ |
| "no_dotd_file", |
| ], |
| **kwargs |
| ) |
| |
| def _test_action_graph_impl(env, target): |
| executable = target[DefaultInfo].files_to_run.executable |
| link_action = link_action_subject.from_target(env, target) |
| |
| object_file = "{package}/_objs/{name}/hello.o" |
| |
| # link.inputs = { hello.o } |
| link_action.inputs().contains(object_file) |
| |
| # link.outputs = { hello } |
| link_action.outputs().contains_exactly([executable.short_path]) |
| |
| compile_action = env.expect.that_target(target).action_generating(object_file) |
| compile_action.mnemonic().equals("CppCompile") |
| |
| # compile.inputs = { hello_cc } |
| compile_action.inputs().contains("{package}/hello.cc") |
| |
| # compile.outputs = { hello.o } (mock toolchain doesn't generate .d) |
| compile_action.outputs().contains_exactly([ |
| object_file, |
| ]) |
| |
| # TODO: Test stripped action |
| |
| def _make_dll(name): |
| # make a fake dll so the dll shows up in the output directory (where the |
| # binary will be) instead of as a source file (the way cc_import on its own would) |
| # Example taken from |
| # https://github.com/bazelbuild/bazel/blob/f720ed385e245e292b0afe19ebd84e4283c30565/examples/windows/dll/windows_dll_library.bzl |
| dll = name + ".dll" |
| mask = name + "_mask" |
| |
| util.helper_target( |
| cc_binary, |
| name = dll, |
| srcs = ["hello.cc"], |
| linkshared = 1, |
| ) |
| |
| # Mask the cc_binary behind a cc_import so we can depend on it as a library |
| util.helper_target( |
| cc_import, |
| name = mask, |
| shared_library = dll, |
| ) |
| |
| # cc_imports are always source files, so make it a generated file again |
| util.helper_target( |
| cc_library, |
| name = name, |
| deps = [mask], |
| ) |
| |
| def _test_runtime_dynamic_libraries_copy_behavior(name, **kwargs): |
| sub_dir_lib = name + "/dst/sub/sub_dir_lib" |
| same_dir_lib = name + "/dst/same_dir_lib" |
| binary_target = name + "/dst/hello" |
| |
| # project a dll into the same directory as the binary, and a second one in |
| # a subdirectory |
| _make_dll(same_dir_lib) |
| _make_dll(sub_dir_lib) |
| |
| util.helper_target( |
| cc_binary, |
| name = binary_target, |
| srcs = ["hello.cc"], |
| deps = [ |
| same_dir_lib, |
| sub_dir_lib, |
| ], |
| linkstatic = False, |
| ) |
| |
| analysis_test( |
| name = name, |
| impl = _test_runtime_dynamic_libraries_copy_behavior_impl, |
| target = binary_target, |
| # TODO: This would be better-done with the mock toolchain, once that is |
| # wired up |
| attrs = { |
| "copy_feature_supported": attr.bool(), |
| }, |
| attr_values = { |
| "copy_feature_supported": select({ |
| # copy_dynamic_libraries_to_binary is only defined in the |
| # windows toolchain currently |
| "@platforms//os:windows": True, |
| "//conditions:default": False, |
| }), |
| "size": "small", |
| }, |
| **kwargs |
| ) |
| |
| def _test_runtime_dynamic_libraries_copy_behavior_impl(env, target): |
| if not env.ctx.attr.copy_feature_supported: |
| return |
| |
| test_name = env.ctx.label.name |
| |
| expected_copied_library = "tests/cc/common/{name}/dst/sub_dir_lib.dll".format( |
| name = test_name, |
| ) |
| expected_same_dir_library = "tests/cc/common/{name}/dst/same_dir_lib.dll".format( |
| name = test_name, |
| ) |
| |
| # Both libraries should be listed as runtime dependencies, but... |
| expected_libraries = [expected_copied_library, expected_same_dir_library] |
| |
| env.expect \ |
| .that_target(target) \ |
| .output_group("runtime_dynamic_libraries") \ |
| .contains_exactly(expected_libraries) |
| |
| actions = {} |
| for action in target[TestingAspectInfo].actions: |
| if action.mnemonic != "Symlink": |
| continue |
| |
| for output in action.outputs.to_list(): |
| if output.short_path in expected_libraries: |
| actions[output.short_path] = action |
| |
| # ... only one of the libraries should have a Symlink action (Copying |
| # Execution Dynamic Library) since the other one should already be in the |
| # same dir |
| expected_copies = [expected_copied_library] |
| env.expect.that_dict(actions).keys().contains_exactly(expected_copies) |
| |
| def _test_pic(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pic_impl, |
| target = name + "/hello", |
| test_features = [FEATURE_NAMES.supports_pic], |
| config_settings = { |
| "//command_line_option:force_pic": True, |
| }, |
| **kwargs |
| ) |
| |
| def _test_pic_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_predicate( |
| matching.file_basename_equals("hello.pic.o"), |
| ) |
| |
| def _generated_def_file_test(name, impl, with_action_configs = None, **kwargs): |
| if with_action_configs == None: |
| with_action_configs = [] |
| |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| linkshared = True, |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = impl, |
| target = name + "/hello", |
| test_features = [ |
| FEATURE_NAMES.copy_dynamic_libraries_to_binary, |
| FEATURE_NAMES.targets_windows, |
| FEATURE_NAMES.windows_export_all_symbols, |
| ], |
| with_action_configs = with_action_configs, |
| **kwargs |
| ) |
| |
| def _generated_def_file_action(env, target): |
| action = cc_binary_target_subject.from_target(env, target).action_generating( |
| "{package}/{name}.gen.def", |
| ) |
| action.mnemonic().equals("DefParser") |
| return action |
| |
| # buildifier: disable=unused-variable |
| def _test_generated_def_file_uses_default_tool(name, **kwargs): |
| _generated_def_file_test( |
| name, |
| _test_generated_def_file_uses_default_tool_impl, |
| **kwargs |
| ) |
| |
| def _test_generated_def_file_uses_default_tool_impl(env, target): |
| _generated_def_file_action(env, target).argv().contains_predicate( |
| matching.contains("tools/def_parser"), |
| ) |
| |
| # buildifier: disable=unused-variable |
| def _test_generated_def_file_uses_toolchain_action(name, **kwargs): |
| _generated_def_file_test( |
| name, |
| _test_generated_def_file_uses_toolchain_action_impl, |
| with_action_configs = [ACTION_NAMES.generate_def_file], |
| **kwargs |
| ) |
| |
| def _test_generated_def_file_uses_toolchain_action_impl(env, target): |
| _generated_def_file_action(env, target).argv().contains_predicate( |
| matching.contains("def_parser_tool"), |
| ) |
| |
| def _test_duplicate_linkopts(name, **kwargs): |
| util.helper_target( |
| cc_library, |
| name = name + "/lib", |
| srcs = ["lib.cc"], |
| linkopts = [ |
| "-z bar", |
| "-z baz", |
| ], |
| ) |
| |
| util.helper_target( |
| cc_binary, |
| name = name + "/app", |
| srcs = ["app.cc"], |
| linkopts = [ |
| "-z foo", |
| "-z bar", |
| ], |
| deps = [name + "/lib"], |
| ) |
| |
| cc_analysis_test( |
| name = name, |
| impl = _test_duplicate_linkopts_impl, |
| target = name + "/app", |
| **kwargs |
| ) |
| |
| def _test_duplicate_linkopts_impl(env, target): |
| executable = target[DefaultInfo].files_to_run.executable |
| link_action = env.expect.that_target(target).action_generating(executable.short_path) |
| argv = link_action.actual.argv |
| env.expect.that_collection(argv).contains_at_least([ |
| "-z", |
| "foo", |
| "-z", |
| "bar", |
| "-z", |
| "bar", |
| "-z", |
| "baz", |
| ]).in_order() |
| |
| def _test_unsupported_src_file(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/bad", |
| srcs = ["bad.unknown"], |
| ) |
| |
| cc_analysis_test( |
| name = name, |
| impl = _test_unsupported_src_file_impl, |
| target = name + "/bad", |
| expect_failure = True, |
| **kwargs |
| ) |
| |
| def _test_unsupported_src_file_impl(env, target): |
| env.expect.that_target(target).failures().contains_predicate( |
| matching.contains("is misplaced here"), |
| ) |
| env.expect.that_target(target).failures().contains_predicate( |
| matching.contains("bad.unknown"), |
| ) |
| |
| def _test_missing_action_config_for_strip_is_a_rule_error(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_missing_action_config_for_strip_is_a_rule_error_impl, |
| target = name + "/hello", |
| test_features = [FEATURE_NAMES.no_legacy_features, FEATURE_NAMES.pic], |
| with_action_configs = [ |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.cpp_link_static_library, |
| ACTION_NAMES.cpp_link_executable, |
| ], |
| expect_failure = True, |
| **kwargs |
| ) |
| |
| def _test_missing_action_config_for_strip_is_a_rule_error_impl(env, target): |
| env.expect.that_target(target).failures().contains_predicate( |
| matching.contains("Expected action_config for 'strip' to be configured."), |
| ) |
| |
| def _test_sanitize_pwd_feature_enabled(name, **kwargs): |
| """sanitize_pwd is on by default, PWD should be set via the feature's env_set.""" |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_sanitize_pwd_feature_enabled_impl, |
| target = name + "/hello", |
| config_settings = { |
| "//command_line_option:platforms": str(Label("//tests/cc/testutil/toolchains:linux_x86_64")), |
| }, |
| **kwargs |
| ) |
| |
| def _test_sanitize_pwd_feature_enabled_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.env().get("PWD", factory = subjects.str).equals("/proc/self/cwd") |
| |
| def _test_sanitize_pwd_feature_disabled(name, **kwargs): |
| """When sanitize_pwd is explicitly disabled, the legacy requires_darwin fallback still applies.""" |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_sanitize_pwd_feature_disabled_impl, |
| target = name + "/hello", |
| config_settings = { |
| "//command_line_option:features": ["-sanitize_pwd"], |
| }, |
| **kwargs |
| ) |
| |
| def _test_sanitize_pwd_feature_disabled_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.env().get("PWD", factory = subjects.str).equals("/proc/self/cwd") |
| |
| def _test_sanitize_pwd_macos_no_pwd(name, **kwargs): |
| """On macOS, sanitize_pwd is enabled but should not set PWD.""" |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_sanitize_pwd_macos_no_pwd_impl, |
| target = name + "/hello", |
| config_settings = { |
| "//command_line_option:platforms": str(Label("//tests/cc/testutil/toolchains:macos_arm64")), |
| }, |
| **kwargs |
| ) |
| |
| def _test_sanitize_pwd_macos_no_pwd_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.env().keys().not_contains("PWD") |
| |
| def _include_paths_from_argv(argv, flag): |
| include_paths = [] |
| for i in range(len(argv) - 1): |
| if argv[i] == flag: |
| include_paths.append(argv[i + 1]) |
| return include_paths |
| |
| def _system_include_paths_from_argv(argv): |
| return _include_paths_from_argv(argv, "-isystem") |
| |
| def _test_system_include_paths_reclassifies_local_includes_without_propagation(name, **kwargs): |
| util.helper_target( |
| cc_library, |
| name = name + "/dep", |
| srcs = ["dep.cc", "dep_local.h"], |
| hdrs = ["dep_public.h"], |
| local_includes = ["dep_private"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc", "hello_local.h"], |
| deps = [name + "/dep"], |
| local_includes = ["hello_private"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_system_include_paths_reclassifies_local_includes_without_propagation_impl, |
| target = name + "/hello", |
| config_settings = { |
| "//command_line_option:features": ["system_include_paths"], |
| }, |
| **kwargs |
| ) |
| |
| def _test_system_include_paths_reclassifies_local_includes_without_propagation_impl(env, target): |
| executable = target[DefaultInfo].files_to_run.executable |
| link_action = env.expect.that_target(target).action_generating(executable.short_path) |
| hello_obj_files = [ |
| f |
| for f in link_action.actual.inputs.to_list() |
| if f.basename.startswith("hello.") and f.extension in ["o", "obj"] |
| ] |
| |
| env.expect.that_collection(hello_obj_files).has_size(1) |
| compile_action = env.expect.that_target(target).action_generating(hello_obj_files[0].short_path) |
| system_include_paths = _system_include_paths_from_argv(compile_action.actual.argv) |
| |
| env.expect.that_collection(system_include_paths).contains_predicate( |
| matching.str_endswith("/hello_private"), |
| ) |
| env.expect.that_collection(system_include_paths).transform( |
| filter = matching.str_endswith("/dep_private"), |
| ).contains_exactly([]) |
| |
| def _test_system_include_paths_reclassifies_local_includes_after_includes(name, **kwargs): |
| util.helper_target( |
| cc_library, |
| name = name + "/dep", |
| srcs = ["dep.cc", "dep_local.h"], |
| hdrs = ["dep_public.h"], |
| includes = ["dep_includes"], |
| local_includes = ["dep_private"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_system_include_paths_reclassifies_local_includes_after_includes_impl, |
| target = name + "/dep", |
| config_settings = { |
| "//command_line_option:features": ["system_include_paths"], |
| }, |
| **kwargs |
| ) |
| |
| def _test_system_include_paths_reclassifies_local_includes_after_includes_impl(env, target): |
| dep_compile_actions = [] |
| for action in target[TestingAspectInfo].actions: |
| if action.mnemonic != "CppCompile": |
| continue |
| for output in action.outputs.to_list(): |
| if output.basename.startswith("dep.") and output.extension in ["o", "obj"]: |
| dep_compile_actions.append(action) |
| break |
| |
| env.expect.that_collection(dep_compile_actions).has_size(1) |
| dep_system_include_paths = _system_include_paths_from_argv(dep_compile_actions[0].argv) |
| |
| dep_system_include_kinds = [] |
| for path in dep_system_include_paths: |
| if path.endswith("/dep_includes"): |
| dep_system_include_kinds.append("dep_includes") |
| elif path.endswith("/dep_private"): |
| dep_system_include_kinds.append("dep_private") |
| |
| env.expect.that_collection(dep_system_include_kinds).contains_at_least([ |
| "dep_includes", |
| "dep_private", |
| ]).in_order() |
| |
| # buildifier: disable=unused-variable |
| def _test_external_include_paths_reclassifies_external_quote_includes(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| deps = ["@googletest//:gtest"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_external_include_paths_reclassifies_external_quote_includes_impl, |
| target = name + "/hello", |
| test_features = [FEATURE_NAMES.external_include_paths], |
| **kwargs |
| ) |
| |
| def _test_external_include_paths_reclassifies_external_quote_includes_impl(env, target): |
| executable = target[DefaultInfo].files_to_run.executable |
| link_action = env.expect.that_target(target).action_generating(executable.short_path) |
| hello_obj_files = [ |
| f |
| for f in link_action.actual.inputs.to_list() |
| if f.basename.startswith("hello.") and f.extension in ["o", "obj"] |
| ] |
| |
| env.expect.that_collection(hello_obj_files).has_size(1) |
| compile_action = env.expect.that_target(target).action_generating(hello_obj_files[0].short_path) |
| quote_include_paths = _include_paths_from_argv(compile_action.actual.argv, "-iquote") |
| system_include_paths = _system_include_paths_from_argv(compile_action.actual.argv) |
| |
| if not quote_include_paths: |
| fail("expected the compile action to still have non-external -iquote paths") |
| env.expect.that_collection(system_include_paths).contains_predicate( |
| matching.contains("googletest"), |
| ) |
| env.expect.that_collection(quote_include_paths).transform( |
| filter = matching.contains("googletest"), |
| ).contains_exactly([]) |
| |
| def _test_linkopts_diamond(name, **kwargs): |
| util.helper_target( |
| cc_library, |
| name = name + "/core", |
| srcs = ["core.cc"], |
| linkopts = ["-z core"], |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/lib1", |
| srcs = ["lib1.cc"], |
| linkopts = ["-z lib1"], |
| deps = [name + "/core"], |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/lib2", |
| srcs = ["lib2.cc"], |
| linkopts = ["-z lib2"], |
| deps = [name + "/core"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/app", |
| srcs = ["app.cc"], |
| linkopts = ["-z app"], |
| deps = [ |
| name + "/lib1", |
| name + "/lib2", |
| ], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_linkopts_diamond_impl, |
| target = name + "/app", |
| **kwargs |
| ) |
| |
| def _test_linkopts_diamond_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.argv().contains_at_least([ |
| "-z", |
| "app", |
| "-z", |
| "lib1", |
| "-z", |
| "lib2", |
| "-z", |
| "core", |
| ]).in_order() |
| |
| def _test_linkopts_fake_diamond(name, **kwargs): |
| util.helper_target( |
| cc_library, |
| name = name + "/core1", |
| srcs = ["core.cc"], |
| linkopts = ["-z core"], |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/core2", |
| srcs = ["core.cc"], |
| linkopts = ["-z core"], |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/lib1", |
| srcs = ["lib1.cc"], |
| linkopts = ["-z lib1"], |
| deps = [name + "/core1"], |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/lib2", |
| srcs = ["lib2.cc"], |
| linkopts = ["-z lib2"], |
| deps = [name + "/core2"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/app", |
| srcs = ["app.cc"], |
| linkopts = ["-z app"], |
| deps = [ |
| name + "/lib1", |
| name + "/lib2", |
| ], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_linkopts_fake_diamond_impl, |
| target = name + "/app", |
| **kwargs |
| ) |
| |
| def _test_linkopts_fake_diamond_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.argv().contains_at_least([ |
| "-z", |
| "app", |
| "-z", |
| "lib1", |
| "-z", |
| "core", |
| "-z", |
| "lib2", |
| "-z", |
| "core", |
| ]).in_order() |
| |
| def _is_shared_library(f): |
| return f.extension in ["so", "dylib", "dll", "ifso"] or ".so" in f.basename or ".dylib" in f.basename |
| |
| def _assert_link_staticness(env, target, expected_static): |
| link_action = link_action_subject.from_target(env, target) |
| shared_library_matcher = matching.custom("is shared library", _is_shared_library) |
| |
| if expected_static: |
| link_action.inputs().not_contains_predicate(shared_library_matcher) |
| else: |
| link_action.inputs().contains_predicate(shared_library_matcher) |
| |
| def _create_dep_tree(name, use_actual_cc_binary = False): |
| binary_rule = _actual_cc_binary if use_actual_cc_binary else cc_binary |
| |
| # Mallocs |
| util.helper_target( |
| cc_library, |
| name = name + "/system_malloc", |
| srcs = ["system_malloc.cc"], |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/mymalloc", |
| srcs = ["mymalloc.cc"], |
| linkopts = ["-Lmalloc_dir -lmalloc_opt"], |
| ) |
| |
| # Infrastructure |
| util.helper_target( |
| cc_library, |
| name = name + "/infrastructure1", |
| srcs = ["infrastructure1.cc"], |
| linkopts = [ |
| "-Linfrastructure1_dir", |
| "-linfrastructure1_opt", |
| ], |
| linkstamp = "linkstamp.cc", |
| linkstatic = 1, |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/infrastructure2", |
| srcs = ["infrastructure2.cc"], |
| linkopts = ["-Linfrastructure2_dir -linfrastructure2_opt"], |
| linkstamp = "linkstamp.cc", |
| ) |
| |
| # Middleware |
| util.helper_target( |
| cc_library, |
| name = name + "/middleware1", |
| srcs = ["middleware1.cc"], |
| linkopts = [ |
| "-Lmiddleware1_dir", |
| "-lmiddleware1_opt", |
| ], |
| deps = [name + "/infrastructure1"], |
| ) |
| util.helper_target( |
| binary_rule, |
| name = name + "/middleware2.so", |
| srcs = ["middleware2.cc"], |
| linkopts = [ |
| "-Lmiddleware2_dir", |
| "-lmiddleware2_opt", |
| ], |
| linkshared = 1, |
| linkstatic = 1, |
| deps = [name + "/infrastructure2"], |
| ) |
| util.helper_target( |
| binary_rule, |
| name = name + "/middleware3.so.1", |
| srcs = ["middleware3.cc"], |
| linkopts = [ |
| "-Lmiddleware3_dir", |
| "-lmiddleware3_opt", |
| ], |
| linkshared = 1, |
| deps = [name + "/infrastructure2"], |
| ) |
| |
| # App |
| util.helper_target( |
| binary_rule, |
| name = name + "/app", |
| srcs = ["app.cc"], |
| # Override potential default link_extra_lib attribute value |
| link_extra_lib = "//tests/cc/testutil/toolchains:link_extra_lib", |
| linkopts = [ |
| "-Lapp_dir1 -Lapp_dir2", |
| "-lapp_opt", |
| ], |
| deps = [ |
| name + "/middleware1", |
| name + "/middleware2.so", |
| name + "/middleware3.so.1", |
| ], |
| ) |
| util.helper_target( |
| binary_rule, |
| name = name + "/app_nonstatic", |
| srcs = ["app.cc"], |
| # Override potential default link_extra_lib attribute value |
| link_extra_lib = "//tests/cc/testutil/toolchains:link_extra_lib", |
| linkopts = [ |
| "-Lapp_dir1 -Lapp_dir2", |
| "-lapp_opt", |
| ], |
| linkstatic = 0, |
| deps = [ |
| name + "/middleware1", |
| name + "/middleware2.so", |
| name + "/middleware3.so.1", |
| ], |
| ) |
| |
| # buildifier: disable=unused-variable |
| def _test_cc_runtimes_added_to_libraries(name, **kwargs): |
| _create_dep_tree(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_cc_runtimes_added_to_libraries_impl, |
| target = name + "/app", |
| test_features = [ |
| FEATURE_NAMES.supports_pic, |
| ], |
| config_settings = { |
| "//command_line_option:extra_toolchains": ",".join( |
| MOCK_TOOLCHAINS + [ |
| "//tests/cc/common:cc_runtimes_mock_toolchain", |
| ], |
| ), |
| }, |
| **kwargs |
| ) |
| |
| def _test_cc_runtimes_added_to_libraries_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_at_least_predicates([ |
| matching.file_basename_equals("app.pic.o"), |
| matching.file_basename_equals("libmiddleware1.a"), |
| matching.file_basename_equals("libinfrastructure1.a"), |
| matching.file_basename_equals("liblink_extra_lib.a"), |
| matching.file_basename_equals("libruntime.a"), |
| matching.file_basename_equals("linkstamp.o"), |
| ]) |
| |
| def _test_ignore_custom_malloc(name, **kwargs): |
| _create_dep_tree(name, use_actual_cc_binary = True) |
| custom_malloc_label = Label("//tests/cc/common:" + name + "/system_malloc") |
| cc_analysis_test( |
| name = name, |
| impl = _test_ignore_custom_malloc_impl, |
| target = name + "/app", |
| config_settings = { |
| "//command_line_option:custom_malloc": custom_malloc_label, |
| }, |
| **kwargs |
| ) |
| |
| def _test_ignore_custom_malloc_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_predicate(matching.file_basename_equals("libsystem_malloc.a")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("libmock_malloc.a")) |
| |
| def _test_custom_malloc(name, **kwargs): |
| _create_dep_tree(name, use_actual_cc_binary = True) |
| custom_malloc_label = Label("//tests/cc/common:" + name + "/mymalloc") |
| cc_analysis_test( |
| name = name, |
| impl = _test_custom_malloc_impl, |
| target = name + "/app", |
| test_features = [FEATURE_NAMES.supports_pic], |
| config_settings = { |
| "//command_line_option:custom_malloc": custom_malloc_label, |
| }, |
| **kwargs |
| ) |
| |
| def _test_custom_malloc_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_predicate(matching.file_basename_equals("libmymalloc.a")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("libmock_malloc.a")) |
| |
| def _test_app_linking_static(name, **kwargs): |
| _create_dep_tree(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_app_linking_static_impl, |
| target = name + "/app", |
| test_features = [ |
| FEATURE_NAMES.supports_pic, |
| FEATURE_NAMES.supports_dynamic_linker, |
| FEATURE_NAMES.supports_interface_shared_libraries, |
| ], |
| **kwargs |
| ) |
| |
| def _test_app_linking_static_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| |
| # Assert inputs |
| link_action.inputs().contains_at_least_predicates([ |
| matching.file_basename_equals("app.pic.o"), |
| matching.file_basename_equals("libmiddleware1.a"), |
| matching.file_basename_equals("libinfrastructure1.a"), |
| matching.file_basename_equals("linkstamp.o"), |
| ]) |
| |
| # Assert NOT inputs |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("libinfrastructure2.a")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("libmiddleware2.so")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("libmiddleware3.so.1")) |
| |
| # Assert linkopts |
| link_action.argv().contains_at_least([ |
| "-Lapp_dir1", |
| "-Lapp_dir2", |
| "-lapp_opt", |
| "-Lmiddleware1_dir", |
| "-lmiddleware1_opt", |
| "-Linfrastructure1_dir", |
| "-linfrastructure1_opt", |
| ]).in_order() |
| |
| def _test_app_linking_dynamic(name, **kwargs): |
| _create_dep_tree(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_app_linking_dynamic_impl, |
| target = name + "/app_nonstatic", |
| test_features = [ |
| FEATURE_NAMES.supports_pic, |
| FEATURE_NAMES.supports_dynamic_linker, |
| FEATURE_NAMES.supports_interface_shared_libraries, |
| ], |
| **kwargs |
| ) |
| |
| def _test_app_linking_dynamic_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| |
| # Assert inputs |
| link_action.inputs().contains_at_least_predicates([ |
| matching.file_basename_equals("app.pic.o"), |
| matching.file_basename_equals("libinfrastructure1.a"), |
| matching.file_basename_equals("linkstamp.o"), |
| ]) |
| |
| # Assert dynamic library symlink (mangled) |
| link_action.inputs().contains_predicate( |
| matching.custom("basename ends with _Slibmiddleware1.ifso", lambda f: f.basename.endswith("_Slibmiddleware1.ifso")), |
| ) |
| |
| # Assert NOT inputs |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("libmiddleware1.a")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("libinfrastructure2.a")) |
| |
| # Assert linkopts |
| link_action.argv().contains_at_least([ |
| "-Lapp_dir1", |
| "-Lapp_dir2", |
| "-lapp_opt", |
| "-Lmiddleware1_dir", |
| "-lmiddleware1_opt", |
| "-Linfrastructure1_dir", |
| "-linfrastructure1_opt", |
| ]).in_order() |
| |
| def _test_so_in_srcs(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/cookies", |
| srcs = [ |
| "cookies.cc", |
| "library.so", |
| "library2.so.1", |
| ], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_so_in_srcs_impl, |
| target = name + "/cookies", |
| **kwargs |
| ) |
| |
| def _test_so_in_srcs_impl(env, target): |
| runfiles = env.expect.that_target(target).runfiles() |
| runfiles.contains_predicate(matching.str_endswith("library.so")) |
| runfiles.not_contains_predicate(matching.str_endswith("library.so.1")) |
| runfiles.contains_predicate(matching.str_endswith("library2.so.1")) |
| runfiles.not_contains_predicate(matching.str_endswith("library2.so")) |
| |
| def _create_three_rules_chain(name): |
| util.helper_target( |
| cc_library, |
| name = name + "/baz", |
| srcs = ["baz.cc"], |
| linkstamp = "linkstamp.cc", |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/bar", |
| srcs = ["bar.cc"], |
| deps = [":" + name + "/baz"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/foo", |
| srcs = ["foo.cc"], |
| deps = [":" + name + "/bar"], |
| ) |
| |
| def _test_transitive_libs_are_collected(name, **kwargs): |
| _create_three_rules_chain(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_transitive_libs_are_collected_impl, |
| target = name + "/foo", |
| config_settings = { |
| "//command_line_option:start_end_lib": False, |
| }, |
| test_features = [FEATURE_NAMES.supports_pic], |
| **kwargs |
| ) |
| |
| def _test_transitive_libs_are_collected_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_at_least_predicates([ |
| matching.file_basename_equals("foo.pic.o"), |
| matching.file_basename_equals("libbar.a"), |
| matching.file_basename_equals("libbaz.a"), |
| matching.file_basename_equals("linkstamp.o"), |
| ]) |
| |
| def _test_transitive_linkstamps_are_collected(name, **kwargs): |
| _create_three_rules_chain(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_transitive_linkstamps_are_collected_impl, |
| target = name + "/foo", |
| **kwargs |
| ) |
| |
| def _test_transitive_linkstamps_are_collected_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_predicate(matching.file_basename_equals("linkstamp.o")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("linkstamp.cc")) |
| |
| def _test_linker_toolchain_feature(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/foo", |
| srcs = ["foo.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_linker_toolchain_feature_impl, |
| target = name + "/foo", |
| test_features = [FEATURE_NAMES.link_env], |
| **kwargs |
| ) |
| |
| def _test_linker_toolchain_feature_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.env().contains_at_least({"foo": "bar"}) |
| |
| def _test_go_code_comes_after_deps(name, **kwargs): |
| util.helper_target( |
| mock_go_library, |
| name = name + "/go_lib", |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/cc_lib", |
| srcs = ["cc_lib.cc"], |
| deps = [":" + name + "/go_lib"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/bin", |
| srcs = ["binary.cc"], |
| deps = [":" + name + "/cc_lib"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_go_code_comes_after_deps_impl, |
| target = name + "/bin", |
| **kwargs |
| ) |
| |
| def _test_go_code_comes_after_deps_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.argv().contains_at_least_predicates([ |
| matching.str_endswith("libcc_lib.a"), |
| matching.str_endswith("libgo_lib.a"), |
| ]).in_order() |
| |
| def _test_additional_linker_inputs(name, **kwargs): |
| util.helper_target( |
| native.genrule, |
| name = name + "/gen_extra", |
| outs = ["main.extra_file"], |
| cmd = "echo '' > $@", |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/bin", |
| srcs = ["main.cc"], |
| additional_linker_inputs = [":" + name + "/gen_extra"], |
| linkopts = ["--option=$(location :" + name + "/gen_extra)"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_additional_linker_inputs_impl, |
| target = name + "/bin", |
| **kwargs |
| ) |
| |
| def _test_additional_linker_inputs_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.argv().contains_predicate( |
| matching.custom( |
| "starts with --option= and ends with main.extra_file", |
| lambda arg: arg.startswith("--option=") and arg.endswith("main.extra_file"), |
| ), |
| ) |
| link_action.inputs().contains_predicate(matching.file_basename_equals("main.extra_file")) |
| |
| # Regression test for b/193125967 |
| def _test_conflicting_linkstamps(name, **kwargs): |
| util.helper_target( |
| cc_library, |
| name = name + "/foo", |
| hdrs = ["foo.h"], |
| linkstamp = "foo.cc", |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/bar", |
| hdrs = ["bar.h"], |
| linkstamp = "foo.cc", |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/bin", |
| deps = [ |
| ":" + name + "/bar", |
| ":" + name + "/foo", |
| ], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_conflicting_linkstamps_impl, |
| target = name + "/bin", |
| **kwargs |
| ) |
| |
| def _test_conflicting_linkstamps_impl(_env, _target): |
| # The test passes if analysis succeeds. |
| pass |
| |
| def _test_compilation_prerequisites_in_output_group(name, **kwargs): |
| out_file = name + "_a.cc" |
| util.helper_target( |
| native.genrule, |
| name = name + "/gen", |
| outs = [out_file], |
| cmd = "echo '' > $@", |
| ) |
| util.helper_target( |
| cc_library, |
| name = name + "/mylib", |
| hdrs = ["b.h"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/prerequisites", |
| srcs = [ |
| name + "/gen", |
| "hello.cc", |
| "hello.h", |
| ], |
| deps = [name + "/mylib"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_compilation_prerequisites_in_output_group_impl, |
| target = name + "/prerequisites", |
| attrs = { |
| "out_file": attr.string(), |
| }, |
| attr_values = { |
| "out_file": out_file, |
| }, |
| **kwargs |
| ) |
| |
| def _test_compilation_prerequisites_in_output_group_impl(env, target): |
| prereqs = target[OutputGroupInfo].compilation_prerequisites_INTERNAL_.to_list() |
| prereq_paths = [f.short_path for f in prereqs] |
| target_subject = env.expect.that_target(target) |
| prereqs_subject = subjects.collection( |
| prereq_paths, |
| meta = target_subject.meta.derive("compilation_prerequisites"), |
| format = True, |
| ) |
| |
| prereqs_subject.contains("{package}/hello.cc") |
| prereqs_subject.contains("{package}/hello.h") |
| prereqs_subject.contains("{package}/b.h") |
| |
| out_file = env.ctx.attr.out_file |
| prereqs_subject.contains("{package}/" + out_file) |
| prereqs_subject.contains_predicate(matching.str_endswith("cppmap")) |
| |
| def _create_prefers_pic_libs_dep_tree(name): |
| util.helper_target( |
| cc_library, |
| name = name + "/mylib", |
| srcs = ["dep.pic.a", "dep.nopic.a"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/mybinary", |
| srcs = ["mybinary.cc"], |
| deps = [name + "/mylib"], |
| ) |
| |
| def _test_pic_mode_prefers_pic_libs_force_pic_disabled(name, **kwargs): |
| _create_prefers_pic_libs_dep_tree(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pic_mode_prefers_pic_libs_force_pic_disabled_impl, |
| target = name + "/mybinary", |
| test_features = [FEATURE_NAMES.supports_pic], |
| config_settings = { |
| "//command_line_option:force_pic": False, |
| }, |
| **kwargs |
| ) |
| |
| def _test_pic_mode_prefers_pic_libs_force_pic_disabled_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_predicate(matching.file_basename_equals("dep.nopic.a")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("dep.pic.a")) |
| |
| def _test_pic_mode_prefers_pic_libs_force_pic_enabled(name, **kwargs): |
| _create_prefers_pic_libs_dep_tree(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pic_mode_prefers_pic_libs_force_pic_enabled_impl, |
| target = name + "/mybinary", |
| test_features = [FEATURE_NAMES.supports_pic], |
| config_settings = { |
| "//command_line_option:force_pic": True, |
| }, |
| **kwargs |
| ) |
| |
| def _test_pic_mode_prefers_pic_libs_force_pic_enabled_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_predicate(matching.file_basename_equals("dep.pic.a")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("dep.nopic.a")) |
| |
| def _test_pic_mode_uses_pic_libs(name, **kwargs): |
| util.helper_target( |
| cc_library, |
| name = name + "/mylib", |
| srcs = ["dep.pic.o"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/mybinary", |
| srcs = ["mybinary.cc"], |
| deps = [name + "/mylib"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pic_mode_uses_pic_libs_impl, |
| target = name + "/mybinary", |
| test_features = [ |
| FEATURE_NAMES.supports_pic, |
| FEATURE_NAMES.supports_start_end_lib, |
| ], |
| config_settings = { |
| "//command_line_option:force_pic": True, |
| }, |
| **kwargs |
| ) |
| |
| def _test_pic_mode_uses_pic_libs_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_predicate(matching.file_basename_equals("dep.pic.o")) |
| |
| def _create_does_not_use_nopic_library_dep_tree(name): |
| util.helper_target( |
| cc_library, |
| name = name + "/mylib", |
| srcs = ["dep.nopic.o"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/mybinary", |
| srcs = ["mybinary.cc"], |
| deps = [name + "/mylib"], |
| ) |
| |
| def _test_pic_mode_does_not_use_nopic_library_force_pic_disabled(name, **kwargs): |
| _create_does_not_use_nopic_library_dep_tree(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pic_mode_does_not_use_nopic_library_force_pic_disabled_impl, |
| target = name + "/mybinary", |
| test_features = [FEATURE_NAMES.supports_pic], |
| config_settings = { |
| "//command_line_option:force_pic": False, |
| }, |
| **kwargs |
| ) |
| |
| def _test_pic_mode_does_not_use_nopic_library_force_pic_disabled_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().contains_predicate(matching.file_basename_equals("mybinary.pic.o")) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("dep.nopic.o")) |
| |
| def _test_pic_mode_does_not_use_nopic_library_force_pic_enabled(name, **kwargs): |
| _create_does_not_use_nopic_library_dep_tree(name) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pic_mode_does_not_use_nopic_library_force_pic_enabled_impl, |
| target = name + "/mybinary", |
| test_features = [FEATURE_NAMES.supports_pic], |
| config_settings = { |
| "//command_line_option:force_pic": True, |
| }, |
| **kwargs |
| ) |
| |
| def _test_pic_mode_does_not_use_nopic_library_force_pic_enabled_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("dep.nopic.o")) |
| |
| def _test_pic_mode_does_not_use_nopic_binary(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/mybinary", |
| srcs = ["xyz.nopic.o"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pic_mode_does_not_use_nopic_binary_impl, |
| target = name + "/mybinary", |
| test_features = [FEATURE_NAMES.supports_pic], |
| **kwargs |
| ) |
| |
| def _test_pic_mode_does_not_use_nopic_binary_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.inputs().not_contains_predicate(matching.file_basename_equals("xyz.nopic.o")) |
| |
| def _setup_cc_runtimes_mock(): |
| util.helper_target( |
| mock_runtime_library, |
| name = "runtime", |
| ) |
| |
| util.helper_target( |
| cc_runtimes_toolchain, |
| name = "runtimes_toolchain", |
| copts = ["-Iruntimes"], |
| runtimes = [":runtime"], |
| ) |
| |
| native.toolchain( |
| name = "cc_runtimes_mock_toolchain", |
| toolchain = ":runtimes_toolchain", |
| toolchain_type = Label("@bazel_tools//tools/cpp:cc_runtimes_toolchain_type"), |
| tags = ["manual", "notap"], |
| ) |
| |
| def _register_link_staticness_test(name, target_under_test, expected_static, config_settings = {}, with_features = [], **kwargs): |
| config_settings = dict( |
| { |
| # Apple builds do not support statically linked binaries so we force the target platform |
| # to Linux. TODO: Do we want to test this separately for Windows targets? |
| # Note: Already the previous Java tests set --cpu=k8 for these tests. |
| "//command_line_option:platforms": [Label("//tests/cc/testutil:linux_x86_64")], |
| }, |
| **config_settings |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _link_staticness_test_impl, |
| target = target_under_test, |
| config_settings = config_settings, |
| with_features = with_features, |
| attrs = { |
| "expected_static": attr.bool(), |
| }, |
| attr_values = { |
| "expected_static": expected_static, |
| }, |
| **kwargs |
| ) |
| |
| def _link_staticness_test_impl(env, target): |
| _assert_link_staticness(env, target, env.ctx.attr.expected_static) |
| |
| def _create_link_staticness_dep_tree(name): |
| util.helper_target( |
| cc_library, |
| name = name + "/dep", |
| srcs = ["dep.cc"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/binary-static", |
| srcs = ["binary.cc"], |
| deps = [name + "/dep"], |
| linkopts = ["-static"], |
| ) |
| util.helper_target( |
| cc_test, |
| name = name + "/test", |
| srcs = ["binary.cc", "test.cc"], |
| deps = [name + "/dep"], |
| ) |
| util.helper_target( |
| cc_test, |
| name = name + "/test-linkstatic", |
| srcs = ["binary.cc", "test.cc"], |
| deps = [name + "/dep"], |
| linkstatic = 1, |
| ) |
| |
| # Default Config Suite |
| def _test_link_staticness_binary_static_dynamic_mode_default(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/binary-static", |
| expected_static = True, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "default", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| def _test_link_staticness_hello_test_dynamic_mode_default(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/test", |
| expected_static = False, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "default", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| def _test_link_staticness_hello_test_linkstatic_dynamic_mode_default(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/test-linkstatic", |
| expected_static = True, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "default", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| # Off Config Suite |
| def _test_link_staticness_binary_static_dynamic_mode_off(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/binary-static", |
| expected_static = True, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "off", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| def _test_link_staticness_hello_test_dynamic_mode_off(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/test", |
| expected_static = True, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "off", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| def _test_link_staticness_hello_test_linkstatic_dynamic_mode_off(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/test-linkstatic", |
| expected_static = True, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "off", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| # Fully Config Suite |
| def _test_link_staticness_binary_static_dynamic_mode_fully(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/binary-static", |
| expected_static = False, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "fully", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| def _test_link_staticness_hello_test_dynamic_mode_fully(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/test", |
| expected_static = False, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "fully", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| def _test_link_staticness_hello_test_linkstatic_dynamic_mode_fully(name, **kwargs): |
| _create_link_staticness_dep_tree(name) |
| _register_link_staticness_test( |
| name = name, |
| target_under_test = name + "/test-linkstatic", |
| expected_static = False, |
| config_settings = { |
| "//command_line_option:dynamic_mode": "fully", |
| }, |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.supports_dynamic_linker], |
| **kwargs |
| ) |
| |
| def _create_linking_mode_features_dep_tree(name): |
| util.helper_target( |
| cc_binary, |
| name = name + "/linkstatic_true", |
| srcs = ["binary.cc"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/linkstatic_false", |
| srcs = ["binary.cc"], |
| linkstatic = 0, |
| ) |
| |
| def _register_linking_mode_features_test(name, target_under_test, expected_mode, config_settings = {}, **kwargs): |
| config_settings = dict( |
| { |
| # Windows does not support linking_mode features so we force the target platform to |
| # Linux. TODO: Do we want to test this separately for Apple targets? |
| "//command_line_option:platforms": [Label("//tests/cc/testutil:linux_x86_64")], |
| }, |
| **config_settings |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _linking_mode_features_test_impl, |
| target = target_under_test, |
| config_settings = config_settings, |
| with_features = [ |
| FEATURE_NAMES.static_linking_mode, |
| FEATURE_NAMES.dynamic_linking_mode, |
| FEATURE_NAMES.supports_dynamic_linker, |
| ], |
| attrs = { |
| "expected_mode": attr.string(), |
| }, |
| attr_values = { |
| "expected_mode": expected_mode, |
| }, |
| **kwargs |
| ) |
| |
| def _linking_mode_features_test_impl(env, target): |
| link_action = link_action_subject.from_target(env, target) |
| link_action.env().get("linking_mode", factory = subjects.str).equals(env.ctx.attr.expected_mode) |
| |
| # Default Config |
| def _test_linking_mode_features_true_default(name, **kwargs): |
| _create_linking_mode_features_dep_tree(name) |
| _register_linking_mode_features_test(name, name + "/linkstatic_true", "static", **kwargs) |
| |
| def _test_linking_mode_features_false_default(name, **kwargs): |
| _create_linking_mode_features_dep_tree(name) |
| _register_linking_mode_features_test(name, name + "/linkstatic_false", "dynamic", **kwargs) |
| |
| # Fully Config |
| def _test_linking_mode_features_true_fully(name, **kwargs): |
| _create_linking_mode_features_dep_tree(name) |
| _register_linking_mode_features_test( |
| name, |
| name + "/linkstatic_true", |
| "dynamic", |
| config_settings = {"//command_line_option:dynamic_mode": "fully"}, |
| **kwargs |
| ) |
| |
| def _test_linking_mode_features_false_fully(name, **kwargs): |
| _create_linking_mode_features_dep_tree(name) |
| _register_linking_mode_features_test( |
| name, |
| name + "/linkstatic_false", |
| "dynamic", |
| config_settings = {"//command_line_option:dynamic_mode": "fully"}, |
| **kwargs |
| ) |
| |
| # Off Config |
| def _test_linking_mode_features_true_off(name, **kwargs): |
| _create_linking_mode_features_dep_tree(name) |
| _register_linking_mode_features_test( |
| name, |
| name + "/linkstatic_true", |
| "static", |
| config_settings = {"//command_line_option:dynamic_mode": "off"}, |
| **kwargs |
| ) |
| |
| def _test_linking_mode_features_false_off(name, **kwargs): |
| _create_linking_mode_features_dep_tree(name) |
| _register_linking_mode_features_test( |
| name, |
| name + "/linkstatic_false", |
| "static", |
| config_settings = {"//command_line_option:dynamic_mode": "off"}, |
| **kwargs |
| ) |
| |
| # Regression test for b/157471662. |
| def _test_link_shared_does_not_have_to_provide_extension(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/foo", |
| srcs = ["foo.cc"], |
| linkshared = 1, |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_link_shared_does_not_have_to_provide_extension_impl, |
| target = name + "/foo", |
| **kwargs |
| ) |
| |
| def _test_link_shared_does_not_have_to_provide_extension_impl(env, target): |
| executable = target[DefaultInfo].files_to_run.executable |
| env.expect.that_str(executable.basename).equals("libfoo.so") |
| |
| def _test_pdb_files(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/bin.dll", |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pdb_files_impl, |
| target = name + "/bin.dll", |
| test_features = [FEATURE_NAMES.generate_pdb_file], |
| **kwargs |
| ) |
| |
| def _test_pdb_files_impl(env, target): |
| env.expect.that_target(target).output_group("pdb_file").contains_predicate( |
| matching.file_basename_equals("bin.pdb"), |
| ) |
| |
| # Even if there are no instrumented C++ files, a C++ binary must provide some form of coverage |
| # support since this is assumed by other tooling. Specifically, lcov_merger. See b/269749859. |
| # TODO(cmita): This doesn't seem reasonable; if nothing C++ is instrumented then we shouldn't |
| # need this. |
| def _test_coverage_support_always_provided(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/main", |
| srcs = ["main.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_coverage_support_always_provided_impl, |
| target = name + "/main", |
| config_settings = { |
| "//command_line_option:collect_code_coverage": "true", |
| "//command_line_option:instrumentation_filter": "/unused", |
| }, |
| **kwargs |
| ) |
| |
| def _test_coverage_support_always_provided_impl(env, target): |
| target_subject = env.expect.that_target(target) |
| target_subject.has_provider(InstrumentedFilesInfo) |
| info_subject = target_subject.provider(InstrumentedFilesInfo) |
| |
| info_subject.metadata_files().contains_exactly([]) |
| info_subject.instrumented_files().contains_exactly([]) |
| |
| info = info_subject.actual |
| |
| # TODO: Remove the hasattr conditions once all supported Bazel versions support these |
| # InstrumentedFilesInfoApi methods. Expected: Bazel 10, Bazel 9.3, maybe Bazel 8.8. |
| if hasattr(info, "get_coverage_environment"): |
| coverage_env = env.expect.that_dict( |
| info.get_coverage_environment(), |
| meta = env.expect.meta.derive("coverage_environment"), |
| ) |
| coverage_env.contains_at_least({ |
| "COVERAGE_GCOV_PATH": "/usr/bin/mock-gcov", |
| "LLVM_COV": "/usr/bin/mock-llvm-cov", |
| "LLVM_PROFDATA": "/usr/bin/mock-llvm-profdata", |
| "GENERATE_LLVM_LCOV": "0", |
| }) |
| if hasattr(info, "get_coverage_support_files"): |
| support_files = env.expect.that_collection( |
| info.get_coverage_support_files().to_list(), |
| expr = "coverage_support_files", |
| ) |
| support_files.contains_predicate( |
| matching.file_basename_equals("coverage-file"), |
| ) |
| |
| def _test_malloc_is_compile_time_dep(name, **kwargs): |
| util.helper_target( |
| cc_library, |
| name = name + "/malloc", |
| defines = ["foobar"], |
| linkstatic = 1, |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/foo", |
| srcs = ["foo.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_malloc_is_compile_time_dep_impl, |
| target = name + "/foo", |
| config_settings = { |
| "//command_line_option:custom_malloc": Label("//tests/cc/common:" + name + "/malloc"), |
| }, |
| **kwargs |
| ) |
| |
| def _test_malloc_is_compile_time_dep_impl(env, target): |
| compilation_outputs = target[OutputGroupInfo].compilation_outputs.to_list() |
| env.expect.that_collection(compilation_outputs).has_size(1) |
| compile_action = env.expect.that_target(target).action_generating(compilation_outputs[0].short_path) |
| compile_action.argv().contains("-Dfoobar") |
| |
| def _test_custom_malloc_filegroup(name, **kwargs): |
| util.helper_target( |
| native.filegroup, |
| name = name + "/custom_malloc", |
| srcs = ["malloc.cc"], |
| ) |
| util.helper_target( |
| cc_binary, |
| name = name + "/bin", |
| srcs = ["binary.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_custom_malloc_filegroup_impl, |
| target = name + "/bin", |
| config_settings = { |
| "//command_line_option:custom_malloc": Label("//tests/cc/common:" + name + "/custom_malloc"), |
| }, |
| **kwargs |
| ) |
| |
| def _test_custom_malloc_filegroup_impl(env, target): # buildifier: disable=unused-variable |
| pass |
| |
| def _test_pic_and_no_pic_gcno_files(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/a", |
| srcs = ["a.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_pic_and_no_pic_gcno_files_impl, |
| target = name + "/a", |
| test_features = [FEATURE_NAMES.supports_pic], |
| config_settings = { |
| "//command_line_option:collect_code_coverage": "true", |
| "//command_line_option:compilation_mode": "opt", |
| }, |
| **kwargs |
| ) |
| |
| def _test_pic_and_no_pic_gcno_files_impl(env, target): |
| target_subject = env.expect.that_target(target) |
| target_subject.has_provider(InstrumentedFilesInfo) |
| info_subject = target_subject.provider(InstrumentedFilesInfo) |
| info_subject.metadata_files().contains_at_least_predicates([ |
| matching.file_basename_equals("a.gcno"), |
| matching.file_basename_equals("a.pic.gcno"), |
| ]) |
| |
| def _test_cctoolchain_coverage_files(name, **kwargs): |
| util.helper_target( |
| cc_test, |
| name = name + "/a", |
| srcs = ["a.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_cctoolchain_coverage_files_impl, |
| target = name + "/a", |
| config_settings = { |
| "//command_line_option:collect_code_coverage": "true", |
| }, |
| **kwargs |
| ) |
| |
| def _test_cctoolchain_coverage_files_impl(env, target): |
| target_subject = env.expect.that_target(target) |
| target_subject.has_provider(InstrumentedFilesInfo) |
| info_subject = target_subject.provider(InstrumentedFilesInfo) |
| |
| # TODO: Remove the hasattr conditions once all supported Bazel versions support these |
| # InstrumentedFilesInfoApi methods. Expected: Bazel 10, Bazel 9.3, maybe Bazel 8.8. |
| if hasattr(info_subject.actual, "get_coverage_support_files"): |
| support_files = env.expect.that_collection( |
| info_subject.actual.get_coverage_support_files().to_list(), |
| expr = "coverage_support_files", |
| ) |
| support_files.contains_exactly_predicates([ |
| matching.file_basename_equals("coverage-file"), |
| ]) |
| |
| def _test_thin_lto(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_thin_lto_impl, |
| target = name + "/hello", |
| test_features = ["thin_lto", "supports_start_end_lib"], |
| **kwargs |
| ) |
| |
| def _test_thin_lto_impl(env, target): |
| actions = target[TestingAspectInfo].actions |
| |
| compile_actions = [a for a in actions if a.mnemonic == "CppCompile"] |
| thin_lto_compile_actions = [a for a in compile_actions if "-flto=thin" in a.argv] |
| env.expect.that_collection(thin_lto_compile_actions).is_not_empty() |
| |
| lto_indexing_actions = [a for a in actions if a.mnemonic == "CppLTOIndexing"] |
| env.expect.that_collection(lto_indexing_actions).has_size(1) |
| |
| lto_backend_actions = [a for a in actions if a.mnemonic == "CcLtoBackendCompile"] |
| env.expect.that_collection(lto_backend_actions).is_not_empty() |
| |
| def _test_thin_lto_disabled(name, **kwargs): |
| util.helper_target( |
| cc_binary, |
| name = name + "/hello", |
| srcs = ["hello.cc"], |
| ) |
| cc_analysis_test( |
| name = name, |
| impl = _test_thin_lto_disabled_impl, |
| target = name + "/hello", |
| with_features = ["thin_lto", "supports_start_end_lib"], |
| test_features = ["-thin_lto", "supports_start_end_lib"], |
| **kwargs |
| ) |
| |
| def _test_thin_lto_disabled_impl(env, target): |
| actions = target[TestingAspectInfo].actions |
| |
| compile_actions = [a for a in actions if a.mnemonic == "CppCompile"] |
| env.expect.that_collection(compile_actions).is_not_empty() |
| thin_lto_compile_actions = [a for a in compile_actions if "-flto=thin" in a.argv] |
| env.expect.that_collection(thin_lto_compile_actions).is_empty() |
| |
| lto_indexing_actions = [a for a in actions if a.mnemonic == "CppLTOIndexing"] |
| env.expect.that_collection(lto_indexing_actions).is_empty() |
| |
| lto_backend_actions = [a for a in actions if a.mnemonic == "CcLtoBackendCompile"] |
| env.expect.that_collection(lto_backend_actions).is_empty() |
| |
| def cc_binary_configured_target_tests(name): |
| """Creates the test suite for cc_binary tests. |
| |
| Args: |
| name: The name of the test suite. |
| """ |
| tests = [ |
| _test_files_to_build, |
| _test_headers_not_passed_to_linking_action, |
| _test_no_duplicate_linkopts, |
| _test_action_graph, |
| _test_runtime_dynamic_libraries_copy_behavior, |
| _test_pic, |
| _test_duplicate_linkopts, |
| _test_unsupported_src_file, |
| _test_missing_action_config_for_strip_is_a_rule_error, |
| _test_linkopts_diamond, |
| _test_linkopts_fake_diamond, |
| _test_app_linking_static, |
| _test_app_linking_dynamic, |
| _test_so_in_srcs, |
| _test_transitive_libs_are_collected, |
| _test_transitive_linkstamps_are_collected, |
| _test_go_code_comes_after_deps, |
| _test_additional_linker_inputs, |
| _test_conflicting_linkstamps, |
| _test_compilation_prerequisites_in_output_group, |
| _test_pic_mode_prefers_pic_libs_force_pic_disabled, |
| _test_pic_mode_prefers_pic_libs_force_pic_enabled, |
| _test_pic_mode_uses_pic_libs, |
| _test_pic_mode_does_not_use_nopic_library_force_pic_disabled, |
| _test_pic_mode_does_not_use_nopic_library_force_pic_enabled, |
| _test_pic_mode_does_not_use_nopic_binary, |
| _test_ignore_custom_malloc, |
| _test_custom_malloc, |
| _test_link_shared_does_not_have_to_provide_extension, |
| _test_pdb_files, |
| _test_coverage_support_always_provided, |
| _test_malloc_is_compile_time_dep, |
| _test_custom_malloc_filegroup, |
| _test_pic_and_no_pic_gcno_files, |
| _test_cctoolchain_coverage_files, |
| _test_thin_lto, |
| _test_thin_lto_disabled, |
| ] |
| |
| # sanitize_pwd is implemented in Starlark in rules_cc, requires Bazel 9+. |
| if bazel_features.cc.cc_common_is_in_rules_cc: |
| _setup_cc_runtimes_mock() |
| tests.extend([ |
| _test_sanitize_pwd_feature_enabled, |
| _test_sanitize_pwd_feature_disabled, |
| _test_sanitize_pwd_macos_no_pwd, |
| _test_external_include_paths_reclassifies_external_quote_includes, # copybara-uncomment-this-please |
| _test_system_include_paths_reclassifies_local_includes_after_includes, |
| _test_system_include_paths_reclassifies_local_includes_without_propagation, |
| _test_generated_def_file_uses_toolchain_action, # copybara-uncomment-this-please |
| _test_generated_def_file_uses_default_tool, # copybara-uncomment-this-please |
| _test_linker_toolchain_feature, |
| _test_link_staticness_binary_static_dynamic_mode_default, |
| _test_link_staticness_hello_test_dynamic_mode_default, |
| _test_link_staticness_hello_test_linkstatic_dynamic_mode_default, |
| _test_link_staticness_binary_static_dynamic_mode_off, |
| _test_link_staticness_hello_test_dynamic_mode_off, |
| _test_link_staticness_hello_test_linkstatic_dynamic_mode_off, |
| _test_link_staticness_binary_static_dynamic_mode_fully, |
| _test_link_staticness_hello_test_dynamic_mode_fully, |
| _test_link_staticness_hello_test_linkstatic_dynamic_mode_fully, |
| _test_linking_mode_features_true_default, |
| _test_linking_mode_features_false_default, |
| _test_linking_mode_features_true_fully, |
| _test_linking_mode_features_false_fully, |
| _test_linking_mode_features_true_off, |
| _test_linking_mode_features_false_off, |
| _test_cc_runtimes_added_to_libraries, |
| ]) |
| |
| test_suite( |
| name = name, |
| tests = tests, |
| ) |