From bd1bd10c181a524b040db1a9f65ab36928f513a7 Mon Sep 17 00:00:00 2001 From: Matt Epperson Date: Tue, 2 Apr 2024 07:31:17 -0600 Subject: [PATCH] feat: Strip debug info from opt builds (#2513) Attempts to follow the cargo proposal linked below to remove debug info for release builds. Furthermore this should uphold the expected behavior of bazel for cc builds which automatically strips debug symbols for optimized builds. https://github.com/rust-lang/cargo/issues/4122#issuecomment-1868318860 --- docs/flatten.md | 3 +- docs/rust_repositories.md | 3 +- rust/private/rustc.bzl | 2 + rust/toolchain.bzl | 16 ++- test/unit/strip_level/BUILD.bazel | 5 + .../strip_level/strip_level_test_suite.bzl | 100 ++++++++++++++++++ 6 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 test/unit/strip_level/BUILD.bazel create mode 100644 test/unit/strip_level/strip_level_test_suite.bzl diff --git a/docs/flatten.md b/docs/flatten.md index 24c6baa973..71a2b4210b 100644 --- a/docs/flatten.md +++ b/docs/flatten.md @@ -1180,7 +1180,7 @@ rust_toolchain(name, experimental_use_cc_common_link, extra_exec_rustc_flags, extra_rustc_flags, extra_rustc_flags_for_crate_types, global_allocator_library, llvm_cov, llvm_profdata, llvm_tools, opt_level, per_crate_rustc_flags, rust_doc, rust_std, rustc, rustc_lib, - rustfmt, staticlib_ext, stdlib_linkflags, target_json, target_triple) + rustfmt, staticlib_ext, stdlib_linkflags, strip_level, target_json, target_triple) Declares a Rust toolchain for use. @@ -1260,6 +1260,7 @@ See `@rules_rust//rust:repositories.bzl` for examples of defining the `@rust_cpu | rustfmt | **Deprecated**: Instead see [rustfmt_toolchain](#rustfmt_toolchain) | Label | optional | None | | staticlib_ext | The extension for static libraries created from rustc. | String | required | | | stdlib_linkflags | Additional linker flags to use when Rust standard library is linked by a C++ linker (rustc will deal with these automatically). Subject to location expansion with respect to the srcs of the rust_std attribute. | List of strings | required | | +| strip_level | Rustc strip levels. | Dictionary: String -> String | optional | {"dbg": "none", "fastbuild": "none", "opt": "debuginfo"} | | target_json | Override the target_triple with a custom target specification. For more details see: https://doc.rust-lang.org/rustc/targets/custom.html | String | optional | "" | | target_triple | The platform triple for the toolchains target environment. For more details see: https://docs.bazel.build/versions/master/skylark/rules.html#configurations | String | optional | "" | diff --git a/docs/rust_repositories.md b/docs/rust_repositories.md index 443b07a32d..85c676d974 100644 --- a/docs/rust_repositories.md +++ b/docs/rust_repositories.md @@ -41,7 +41,7 @@ rust_toolchain(name, experimental_use_cc_common_link, extra_exec_rustc_flags, extra_rustc_flags, extra_rustc_flags_for_crate_types, global_allocator_library, llvm_cov, llvm_profdata, llvm_tools, opt_level, per_crate_rustc_flags, rust_doc, rust_std, rustc, rustc_lib, - rustfmt, staticlib_ext, stdlib_linkflags, target_json, target_triple) + rustfmt, staticlib_ext, stdlib_linkflags, strip_level, target_json, target_triple) Declares a Rust toolchain for use. @@ -121,6 +121,7 @@ See `@rules_rust//rust:repositories.bzl` for examples of defining the `@rust_cpu | rustfmt | **Deprecated**: Instead see [rustfmt_toolchain](#rustfmt_toolchain) | Label | optional | None | | staticlib_ext | The extension for static libraries created from rustc. | String | required | | | stdlib_linkflags | Additional linker flags to use when Rust standard library is linked by a C++ linker (rustc will deal with these automatically). Subject to location expansion with respect to the srcs of the rust_std attribute. | List of strings | required | | +| strip_level | Rustc strip levels. | Dictionary: String -> String | optional | {"dbg": "none", "fastbuild": "none", "opt": "debuginfo"} | | target_json | Override the target_triple with a custom target specification. For more details see: https://doc.rust-lang.org/rustc/targets/custom.html | String | optional | "" | | target_triple | The platform triple for the toolchains target environment. For more details see: https://docs.bazel.build/versions/master/skylark/rules.html#configurations | String | optional | "" | diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index f6f632dba6..04e26e40e3 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -959,6 +959,8 @@ def construct_arguments( compilation_mode = get_compilation_mode_opts(ctx, toolchain) rustc_flags.add(compilation_mode.opt_level, format = "--codegen=opt-level=%s") rustc_flags.add(compilation_mode.debug_info, format = "--codegen=debuginfo=%s") + if toolchain.target_os != "windows": + rustc_flags.add(compilation_mode.strip_level, format = "--codegen=strip=%s") # For determinism to help with build distribution and such if remap_path_prefix != None: diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index 36c42b7a67..10146e9552 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -490,11 +490,13 @@ def _rust_toolchain_impl(ctx): list: A list containing the target's toolchain Provider info """ compilation_mode_opts = {} - for k, v in ctx.attr.opt_level.items(): + for k, opt_level in ctx.attr.opt_level.items(): if not k in ctx.attr.debug_info: fail("Compilation mode {} is not defined in debug_info but is defined opt_level".format(k)) - compilation_mode_opts[k] = struct(debug_info = ctx.attr.debug_info[k], opt_level = v) - for k, v in ctx.attr.debug_info.items(): + if not k in ctx.attr.strip_level: + fail("Compilation mode {} is not defined in strip_level but is defined opt_level".format(k)) + compilation_mode_opts[k] = struct(debug_info = ctx.attr.debug_info[k], opt_level = opt_level, strip_level = ctx.attr.strip_level[k]) + for k in ctx.attr.debug_info.keys(): if not k in ctx.attr.opt_level: fail("Compilation mode {} is not defined in opt_level but is defined debug_info".format(k)) @@ -817,6 +819,14 @@ rust_toolchain = rule( ), mandatory = True, ), + "strip_level": attr.string_dict( + doc = "Rustc strip levels.", + default = { + "dbg": "none", + "fastbuild": "none", + "opt": "debuginfo", + }, + ), "target_json": attr.string( doc = ("Override the target_triple with a custom target specification. " + "For more details see: https://doc.rust-lang.org/rustc/targets/custom.html"), diff --git a/test/unit/strip_level/BUILD.bazel b/test/unit/strip_level/BUILD.bazel new file mode 100644 index 0000000000..d342c6a6c3 --- /dev/null +++ b/test/unit/strip_level/BUILD.bazel @@ -0,0 +1,5 @@ +load(":strip_level_test_suite.bzl", "strip_level_test_suite") + +strip_level_test_suite( + name = "strip_level_test_suite", +) diff --git a/test/unit/strip_level/strip_level_test_suite.bzl b/test/unit/strip_level/strip_level_test_suite.bzl new file mode 100644 index 0000000000..eabed67253 --- /dev/null +++ b/test/unit/strip_level/strip_level_test_suite.bzl @@ -0,0 +1,100 @@ +"""Starlark tests for `rust_toolchain.strip_level`""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest") +load("@bazel_skylib//rules:write_file.bzl", "write_file") +load("//rust:defs.bzl", "rust_binary") +load( + "//test/unit:common.bzl", + "assert_action_mnemonic", + "assert_argv_contains", +) + +def _strip_level_test_impl(ctx, expected_level): + env = analysistest.begin(ctx) + target = analysistest.target_under_test(env) + + action = target.actions[0] + assert_action_mnemonic(env, action, "Rustc") + + assert_argv_contains(env, action, "--codegen=strip={}".format(expected_level)) + return analysistest.end(env) + +def _strip_level_for_dbg_test_impl(ctx): + return _strip_level_test_impl(ctx, "none") + +_strip_level_for_dbg_test = analysistest.make( + _strip_level_for_dbg_test_impl, + config_settings = { + "//command_line_option:compilation_mode": "dbg", + }, +) + +def _strip_level_for_fastbuild_test_impl(ctx): + return _strip_level_test_impl(ctx, "none") + +_strip_level_for_fastbuild_test = analysistest.make( + _strip_level_for_fastbuild_test_impl, + config_settings = { + "//command_line_option:compilation_mode": "fastbuild", + }, +) + +def _strip_level_for_opt_test_impl(ctx): + return _strip_level_test_impl(ctx, "debuginfo") + +_strip_level_for_opt_test = analysistest.make( + _strip_level_for_opt_test_impl, + config_settings = { + "//command_line_option:compilation_mode": "opt", + }, +) + +def strip_level_test_suite(name): + """Entry-point macro called from the BUILD file. + + Args: + name (str): The name of the test suite. + """ + write_file( + name = "bin_main", + out = "main.rs", + content = [ + "fn main() {}", + "", + ], + ) + + rust_binary( + name = "bin", + srcs = [":main.rs"], + edition = "2021", + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//os:macos", + "@platforms//os:none", + ], + ) + + _strip_level_for_dbg_test( + name = "strip_level_for_dbg_test", + target_under_test = ":bin", + ) + + _strip_level_for_fastbuild_test( + name = "strip_level_for_fastbuild_test", + target_under_test = ":bin", + ) + + _strip_level_for_opt_test( + name = "strip_level_for_opt_test", + target_under_test = ":bin", + ) + + native.test_suite( + name = name, + tests = [ + ":strip_level_for_dbg_test", + ":strip_level_for_fastbuild_test", + ":strip_level_for_opt_test", + ], + )