diff --git a/zig/private/common/zig_build.bzl b/zig/private/common/zig_build.bzl index 0b618779..c5750ed5 100644 --- a/zig/private/common/zig_build.bzl +++ b/zig/private/common/zig_build.bzl @@ -277,6 +277,16 @@ def zig_build_impl(ctx, *, kind): use_cc_common_link = settings.use_cc_common_link use_test_obj = kind == "zig_test" and use_cc_common_link and semver.gte(zigtoolchaininfo.zig_version, "0.16.0") + cc_toolchain = None + feature_configuration = None + generate_dsym_file = False + if use_cc_common_link and kind != "zig_static_library": + cc_toolchain, feature_configuration = find_cc_toolchain(ctx, mandatory = True) + generate_dsym_file = cc_common.is_enabled( + feature_configuration = feature_configuration, + feature_name = "generate_dsym_file", + ) + providers = [] exported_library_to_link = None direct_data = [] @@ -317,8 +327,11 @@ def zig_build_impl(ctx, *, kind): elif ctx.attr.compiler_runtime == "exclude": args.add("-fno-compiler-rt") - if ctx.attr.strip_debug_symbols and not settings.strip: - args.add("-fstrip") + if ctx.attr.strip_debug_symbols: + if generate_dsym_file: + fail("'strip_debug_symbols' cannot be enabled when generating a dSYM") + if not settings.strip: + args.add("-fstrip") zig_lib_dir( zigtoolchaininfo = zigtoolchaininfo, @@ -361,6 +374,15 @@ def zig_build_impl(ctx, *, kind): bin_output = ctx.actions.declare_file(bin_output_name) solib_parents = [""] + cc_link_kwargs = {} + if generate_dsym_file: + dsym_file = ctx.actions.declare_directory(ctx.label.name + ".dSYM", sibling = bin_output) + cc_link_kwargs = { + "additional_outputs": [dsym_file], + "variables_extension": {"dsym_path": dsym_file.path}, + } + output_groups["dsyms"] = depset([dsym_file]) + if kind == "zig_test" and ctx.attr.test_runner: args.add("--test-runner", ctx.file.test_runner) direct_inputs.append(ctx.file.test_runner) @@ -547,7 +569,6 @@ def zig_build_impl(ctx, *, kind): **zig_build_kwargs ) - cc_toolchain, feature_configuration = find_cc_toolchain(ctx, mandatory = True) library_to_link = cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, @@ -571,6 +592,7 @@ def zig_build_impl(ctx, *, kind): main_output = bin_output, linking_contexts = [linking_context, root_module.cc_info.linking_context], user_link_flags = linkopts, + **cc_link_kwargs ) else: args.add(bin_output, format = "-femit-bin=%s") @@ -638,7 +660,6 @@ def zig_build_impl(ctx, *, kind): **zig_build_kwargs ) - cc_toolchain, feature_configuration = find_cc_toolchain(ctx, mandatory = True) library_to_link = cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, @@ -662,6 +683,7 @@ def zig_build_impl(ctx, *, kind): main_output = bin_output, linking_contexts = [linking_context, root_module.cc_info.linking_context], user_link_flags = linkopts, + **cc_link_kwargs ) else: args.add(bin_output, format = "-femit-bin=%s") @@ -713,7 +735,6 @@ def zig_build_impl(ctx, *, kind): **zig_build_kwargs ) - cc_toolchain, feature_configuration = find_cc_toolchain(ctx, mandatory = True) library_to_link = cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, @@ -738,6 +759,7 @@ def zig_build_impl(ctx, *, kind): main_output = bin_output, linking_contexts = [linking_context, root_module.cc_info.linking_context], user_link_flags = linkopts, + **cc_link_kwargs ) exported_library_to_link = link_outputs.library_to_link diff --git a/zig/tests/BUILD.bazel b/zig/tests/BUILD.bazel index 48078ccc..b0a5b430 100644 --- a/zig/tests/BUILD.bazel +++ b/zig/tests/BUILD.bazel @@ -2,6 +2,7 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load(":bzlmod_zig_test.bzl", "bzlmod_zig_test_suite") load(":cache_test.bzl", "cache_test_suite") load(":config_test.bzl", "config_test_suite") +load(":dsym_test.bzl", "dsym_test_suite") load(":emit_attr_test.bzl", "emit_attr_test_suite") load(":mode_test.bzl", "mode_test_suite") load(":module_info_test.bzl", "module_info_test_suite") @@ -26,6 +27,8 @@ cache_test_suite(name = "cache_test") config_test_suite(name = "config_test") +dsym_test_suite(name = "dsym_test") + emit_attr_test_suite(name = "emit_attr_test") use_cc_common_link_test_suite(name = "use_cc_common_link_test") diff --git a/zig/tests/dsym_test.bzl b/zig/tests/dsym_test.bzl new file mode 100644 index 00000000..ffce021b --- /dev/null +++ b/zig/tests/dsym_test.bzl @@ -0,0 +1,142 @@ +"""Analysis tests for dSYM outputs from cc_common.link.""" + +load("@bazel_features//:features.bzl", "bazel_features") +load("@bazel_skylib//lib:partial.bzl", "partial") +load("@bazel_skylib//lib:sets.bzl", "sets") +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts", "unittest") +load( + ":util.bzl", + "assert_find_action", + "assert_flag_set", + "assert_flag_unset", + "canonical_label", +) + +_APPLE_GENERATE_DSYM = "//command_line_option:apple_generate_dsym" +_EXTRA_TOOLCHAINS = "//command_line_option:extra_toolchains" +_STRIP = "//command_line_option:strip" +_TARGET_PLATFORM = "//command_line_option:platforms" +_SETTINGS_USE_CC_COMMON_LINK = canonical_label("@//zig/settings:use_cc_common_link") + +_PLATFORM_ZIG_ONLY_X86_64_LINUX = canonical_label("@//zig/tests/platforms:zig-only-x86_64-linux") +_TOOLCHAIN_UNCONSTRAINED_DEFAULT_TEST = canonical_label("@//zig/tests/platforms:unconstrained_default_test_toolchain") +_TOOLCHAIN_ZIG_ONLY_X86_64_LINUX = canonical_label("@//zig/tests/platforms:zig-only-x86_64-linux_toolchain") + +def _dsyms_enabled_test_impl(ctx): + env = analysistest.begin(ctx) + target = analysistest.target_under_test(env) + output_groups = target[OutputGroupInfo] + + asserts.true(env, hasattr(output_groups, "dsyms"), "dSYMs should be exposed when the C++ toolchain enables generate_dsym_file.") + dsyms = output_groups.dsyms.to_list() if hasattr(output_groups, "dsyms") else [] + asserts.equals(env, 1, len(dsyms), "Exactly one dSYM should be exposed.") + if dsyms: + dsym = dsyms[0] + asserts.true(env, dsym.is_directory, "The dSYM should be a directory artifact.") + asserts.equals(env, target.label.name + ".dSYM", dsym.basename) + + link = assert_find_action(env, "CppLink") + asserts.true(env, sets.contains(sets.make(link.outputs.to_list()), dsym), "The CppLink action should produce the dSYM.") + + link_variables = link.argv + getattr(link, "env", {}).values() + asserts.true( + env, + any([dsym.path in value for value in link_variables]), + "The CppLink command line or environment should contain the dSYM path.", + ) + + for action in analysistest.target_actions(env): + if action.mnemonic in ["ZigBuildLib", "ZigBuildTest"]: + assert_flag_unset(env, "-fstrip", action.argv) + + return analysistest.end(env) + +_dsyms_enabled_test = analysistest.make( + _dsyms_enabled_test_impl, + config_settings = { + _APPLE_GENERATE_DSYM: True, + _SETTINGS_USE_CC_COMMON_LINK: True, + _STRIP: "never", + }, +) + +def _dsyms_strip_debug_symbols_test_impl(ctx): + env = analysistest.begin(ctx) + asserts.expect_failure(env, "'strip_debug_symbols' cannot be enabled when generating a dSYM") + return analysistest.end(env) + +_dsyms_strip_debug_symbols_test = analysistest.make( + _dsyms_strip_debug_symbols_test_impl, + config_settings = { + _APPLE_GENERATE_DSYM: True, + _SETTINGS_USE_CC_COMMON_LINK: True, + }, + expect_failure = True, +) + +def _dsyms_disabled_test_impl(ctx): + env = analysistest.begin(ctx) + target = analysistest.target_under_test(env) + output_groups = target[OutputGroupInfo] + + asserts.false(env, hasattr(output_groups, "dsyms"), "dSYMs should not be exposed when generate_dsym_file is disabled.") + assert_find_action(env, "CppLink") + assert_flag_set(env, "-fstrip", assert_find_action(env, "ZigBuildLib").argv) + + return analysistest.end(env) + +_dsyms_disabled_test = analysistest.make( + _dsyms_disabled_test_impl, + config_settings = { + _APPLE_GENERATE_DSYM: False, + _SETTINGS_USE_CC_COMMON_LINK: True, + _STRIP: "always", + }, +) + +def _dsyms_without_cc_toolchain_test_impl(ctx): + env = analysistest.begin(ctx) + target = analysistest.target_under_test(env) + output_groups = target[OutputGroupInfo] + + asserts.false(env, hasattr(output_groups, "dsyms"), "A Zig-only target should not expose a dSYM that no action can create.") + assert_find_action(env, "ZigBuildExe") + asserts.false( + env, + any([action.mnemonic == "CppLink" for action in analysistest.target_actions(env)]), + "A Zig-only target should not create a CppLink action.", + ) + + return analysistest.end(env) + +def _zig_only_config(generate_dsym): + return { + _APPLE_GENERATE_DSYM: generate_dsym, + _EXTRA_TOOLCHAINS: ",".join([ + _TOOLCHAIN_ZIG_ONLY_X86_64_LINUX, + ] + ([_TOOLCHAIN_UNCONSTRAINED_DEFAULT_TEST] if bazel_features.toolchains.has_default_test_toolchain_type else [])), + _SETTINGS_USE_CC_COMMON_LINK: False, + _TARGET_PLATFORM: _PLATFORM_ZIG_ONLY_X86_64_LINUX, + } + +_dsyms_without_cc_toolchain_requested_test = analysistest.make( + _dsyms_without_cc_toolchain_test_impl, + config_settings = _zig_only_config(True), +) + +_dsyms_without_cc_toolchain_unrequested_test = analysistest.make( + _dsyms_without_cc_toolchain_test_impl, + config_settings = _zig_only_config(False), +) + +def dsym_test_suite(name): + unittest.suite( + name, + partial.make(_dsyms_enabled_test, name = "dsym_binary_enabled_test", target_under_test = "//zig/tests/simple-binary:binary", size = "small", target_compatible_with = ["@platforms//os:macos"]), + partial.make(_dsyms_enabled_test, name = "dsym_shared_library_enabled_test", target_under_test = "//zig/tests/simple-shared-library:shared", size = "small", target_compatible_with = ["@platforms//os:macos"]), + partial.make(_dsyms_enabled_test, name = "dsym_test_enabled_test", target_under_test = "//zig/tests/simple-test:test", size = "small", target_compatible_with = ["@platforms//os:macos"]), + partial.make(_dsyms_strip_debug_symbols_test, name = "dsym_strip_debug_symbols_test", target_under_test = "//zig/tests/strip_debug_symbols:binary-strip", size = "small", target_compatible_with = ["@platforms//os:macos"]), + partial.make(_dsyms_disabled_test, name = "dsym_binary_disabled_test", target_under_test = "//zig/tests/simple-binary:binary", size = "small"), + partial.make(_dsyms_without_cc_toolchain_requested_test, name = "dsym_zig_only_requested_test", target_under_test = "//zig/tests/simple-binary:binary", size = "small"), + partial.make(_dsyms_without_cc_toolchain_unrequested_test, name = "dsym_zig_only_unrequested_test", target_under_test = "//zig/tests/simple-binary:binary", size = "small"), + )