| """Tests for debug info packaging (Fission).""" | |
| load("@rules_testing//lib:analysis_test.bzl", "test_suite") | |
| load("@rules_testing//lib:truth.bzl", "subjects") | |
| load("@rules_testing//lib:util.bzl", "util") | |
| load("//cc:cc_binary.bzl", "cc_binary") | |
| load("//cc:cc_library.bzl", "cc_library") | |
| load("//tests/cc/testutil:cc_analysis_test.bzl", "cc_analysis_test") | |
| load("//tests/cc/testutil/toolchains:features.bzl", "FEATURE_NAMES") | |
| def _create_fission_binary(name): | |
| util.empty_file(name + "_fission.cc") | |
| util.helper_target( | |
| cc_binary, | |
| name = name + "_fission", | |
| srcs = [name + "_fission.cc"], | |
| ) | |
| def _test_dwp_output_names(name): | |
| """Verifies the names of expected implicit .dwp outputs for supporting cc_* rules.""" | |
| _create_fission_binary(name) | |
| cc_analysis_test( | |
| name = name, | |
| impl = _test_dwp_output_names_impl, | |
| target = name + "_fission", | |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info], | |
| config_settings = { | |
| "//command_line_option:fission": "yes", | |
| "//command_line_option:force_pic": True, | |
| }, | |
| ) | |
| def _test_dwp_output_names_impl(env, target): | |
| executable = target[DefaultInfo].files_to_run.executable | |
| dwp_path = executable.short_path + ".dwp" | |
| dwp_action = env.expect.that_target(target).action_generating(dwp_path) | |
| dwp_action.mnemonic().equals("CcGenerateDwp") | |
| def _test_dwp_builds_require_fission_mode_disabled(name): | |
| """Tests that .dwp files can be built in both fission and non-fission configurations.""" | |
| _create_fission_binary(name) | |
| cc_analysis_test( | |
| name = name, | |
| impl = _test_dwp_builds_require_fission_mode_disabled_impl, | |
| target = name + "_fission", | |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info], | |
| config_settings = { | |
| "//command_line_option:fission": "no", | |
| }, | |
| ) | |
| def _test_dwp_builds_require_fission_mode_disabled_impl(env, target): | |
| # Fission disabled (output is a non-meaningful empty file): | |
| executable = target[DefaultInfo].files_to_run.executable | |
| dwp_path = executable.short_path + ".dwp" | |
| action = env.expect.that_target(target).action_generating(dwp_path) | |
| action.mnemonic().equals("FileWrite") | |
| action.content().equals("") | |
| def _test_dwp_builds_require_fission_mode_enabled(name): | |
| """Tests that .dwp files can be built in both fission and non-fission configurations.""" | |
| _create_fission_binary(name) | |
| cc_analysis_test( | |
| name = name, | |
| impl = _test_dwp_builds_require_fission_mode_enabled_impl, | |
| target = name + "_fission", | |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info], | |
| config_settings = { | |
| "//command_line_option:fission": "yes", | |
| "//command_line_option:force_pic": True, | |
| }, | |
| ) | |
| def _test_dwp_builds_require_fission_mode_enabled_impl(env, target): | |
| # Fission enabled: | |
| executable = target[DefaultInfo].files_to_run.executable | |
| dwp_path = executable.short_path + ".dwp" | |
| action = env.expect.that_target(target).action_generating(dwp_path) | |
| action.mnemonic().equals("CcGenerateDwp") | |
| def _test_dwp_action_structure(name): | |
| """Tests the structure (i.e. inputs, outputs, command line) of the action that generates a target's .dwp.""" | |
| _create_fission_binary(name) | |
| cc_analysis_test( | |
| name = name, | |
| impl = _test_dwp_action_structure_impl, | |
| target = name + "_fission", | |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info], | |
| config_settings = { | |
| "//command_line_option:fission": "yes", | |
| "//command_line_option:force_pic": True, | |
| }, | |
| ) | |
| def _test_dwp_action_structure_impl(env, target): | |
| executable = target[DefaultInfo].files_to_run.executable | |
| dwp_path = executable.short_path + ".dwp" | |
| action = env.expect.that_target(target).action_generating(dwp_path) | |
| action.mnemonic().equals("CcGenerateDwp") | |
| argv = action.argv() | |
| # The first argument should be the command being executed. | |
| argv.offset(0, subjects.str).equals("/usr/bin/mock-dwp") | |
| # The final two arguments should be "-o dwpOutputFile". | |
| argv.offset(-2, subjects.str).equals("-o") | |
| argv.offset(-1, subjects.str).ends_with("/" + dwp_path) | |
| # The remaining arguments should be the set of .dwo inputs. | |
| dwo_files = _get_dwo_files_subject(env, target, dwp_path) | |
| expected_dwo = "{package}/_objs/{target}/{target}.pic.dwo".format( | |
| package = target.label.package, | |
| target = target.label.name, | |
| ) | |
| dwo_files.contains_exactly([expected_dwo]) | |
| def _create_dwo_input_set_tree(name, linkstatic): | |
| util.empty_file(name + "_lib1.cc") | |
| util.empty_file(name + "_lib2.cc") | |
| util.empty_file(name + "_lib_pregen.o") | |
| util.empty_file(name + "_test_malloc.cc") | |
| util.empty_file(name + "_main.cc") | |
| util.empty_file(name + "_bin_pregen.o") | |
| util.helper_target( | |
| cc_library, | |
| name = name + "_lib", | |
| srcs = [ | |
| name + "_lib1.cc", | |
| name + "_lib2.cc", | |
| name + "_lib_pregen.o", | |
| ], | |
| ) | |
| util.helper_target( | |
| cc_library, | |
| name = name + "_test_malloc", | |
| srcs = [name + "_test_malloc.cc"], | |
| ) | |
| util.helper_target( | |
| cc_library, | |
| name = name + "_empty_lib", | |
| ) | |
| util.helper_target( | |
| cc_binary, | |
| name = name + "_binary", | |
| srcs = [ | |
| name + "_main.cc", | |
| name + "_bin_pregen.o", | |
| ], | |
| deps = [name + "_lib"], | |
| malloc = name + "_test_malloc", | |
| link_extra_lib = name + "_empty_lib", # copybara:strip | |
| linkstatic = linkstatic, | |
| ) | |
| def _get_dwo_files_subject(env, target, dwp_path): | |
| action = env.expect.that_target(target).action_generating(dwp_path) | |
| dwo_files = [] | |
| package_prefix = target.label.package + "/" | |
| for f in action.actual.inputs.to_list(): | |
| if f.basename.endswith(".dwo") and f.short_path.startswith(package_prefix): | |
| dwo_files.append(f.short_path) | |
| elif f.basename.endswith(".dwp") and f.short_path != dwp_path: | |
| sub_action = env.expect.that_target(target).action_generating(f.short_path) | |
| for sf in sub_action.actual.inputs.to_list(): | |
| if sf.basename.endswith(".dwo") and sf.short_path.startswith(package_prefix): | |
| dwo_files.append(sf.short_path) | |
| return env.expect.that_collection(dwo_files) | |
| def _test_dwo_input_set_pic_mode(name): | |
| """Tests that the set of input .dwo files is as expected. | |
| This set should consist of the corresponding .dwo for every generated .o file | |
| that is fed into a binary's link. Note that "generated .o" files are distinguished | |
| from "source .o" files: .o files checked directly into the depot are not compiled | |
| at build time and thus have no corresponding .dwo. | |
| This version of the test applies "pic-mode" linking (.pic.o files are linked in | |
| over their .o counterparts). | |
| Args: | |
| name: test target name | |
| """ | |
| _create_dwo_input_set_tree(name, linkstatic = True) | |
| cc_analysis_test( | |
| name = name, | |
| impl = _test_dwo_input_set_pic_mode_impl, | |
| target = name + "_binary", | |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info], | |
| config_settings = { | |
| "//command_line_option:fission": "yes", | |
| "//command_line_option:force_pic": True, | |
| }, | |
| ) | |
| def _test_dwo_input_set_pic_mode_impl(env, target): | |
| executable = target[DefaultInfo].files_to_run.executable | |
| dwp_path = executable.short_path + ".dwp" | |
| dwo_files = _get_dwo_files_subject(env, target, dwp_path) | |
| pkg = target.label.package | |
| prefix = target.label.name.replace("_binary", "") | |
| lib_target = prefix + "_lib" | |
| malloc_target = prefix + "_test_malloc" | |
| bin_target = prefix + "_binary" | |
| expected = [ | |
| "{pkg}/_objs/{lib}/{prefix}_lib1.pic.dwo".format(pkg = pkg, lib = lib_target, prefix = prefix), | |
| "{pkg}/_objs/{lib}/{prefix}_lib2.pic.dwo".format(pkg = pkg, lib = lib_target, prefix = prefix), | |
| "{pkg}/_objs/{malloc}/{prefix}_test_malloc.pic.dwo".format(pkg = pkg, malloc = malloc_target, prefix = prefix), | |
| "{pkg}/_objs/{bin}/{prefix}_main.pic.dwo".format(pkg = pkg, bin = bin_target, prefix = prefix), | |
| ] | |
| dwo_files.contains_exactly(expected) | |
| def _test_dwo_input_set_dynamic_mode(name): | |
| """Tests the .dwo input set for a dynamically linked binary. | |
| As described above, the .dwo input set for a binary should roughly correspond to | |
| the .o files that are fed into the binary's link. For a dynamically linked | |
| binary, this excludes everything in dependent dynamic libraries. | |
| Args: | |
| name: test target name | |
| """ | |
| _create_dwo_input_set_tree(name, linkstatic = False) | |
| cc_analysis_test( | |
| name = name, | |
| impl = _test_dwo_input_set_dynamic_mode_impl, | |
| target = name + "_binary", | |
| with_features = [FEATURE_NAMES.supports_pic, FEATURE_NAMES.per_object_debug_info], | |
| config_settings = { | |
| "//command_line_option:fission": "yes", | |
| "//command_line_option:force_pic": True, | |
| }, | |
| ) | |
| def _test_dwo_input_set_dynamic_mode_impl(env, target): | |
| executable = target[DefaultInfo].files_to_run.executable | |
| dwp_path = executable.short_path + ".dwp" | |
| dwo_files = _get_dwo_files_subject(env, target, dwp_path) | |
| pkg = target.label.package | |
| prefix = target.label.name.replace("_binary", "") | |
| bin_target = prefix + "_binary" | |
| expected_main = "{pkg}/_objs/{bin}/{prefix}_main.pic.dwo".format(pkg = pkg, bin = bin_target, prefix = prefix) | |
| dwo_files.contains_exactly([expected_main]) | |
| def debug_info_packaging_tests(name): | |
| test_suite( | |
| name = name, | |
| tests = [ | |
| _test_dwp_output_names, | |
| _test_dwp_builds_require_fission_mode_disabled, | |
| _test_dwp_builds_require_fission_mode_enabled, | |
| _test_dwp_action_structure, | |
| _test_dwo_input_set_pic_mode, | |
| _test_dwo_input_set_dynamic_mode, | |
| ], | |
| ) |